Browse Source

fix conflict

pull/1943/head
Zach Ramsay 7 years ago
parent
commit
65487586f8
106 changed files with 7170 additions and 0 deletions
  1. +7
    -0
      .gitignore
  2. +3
    -0
      CODEOWNERS
  3. +3
    -0
      README.md
  4. +4
    -0
      build/.gitignore
  5. +204
    -0
      build/LICENSE
  6. +289
    -0
      build/Makefile
  7. +19
    -0
      build/RPM-GPG-KEY-Tendermint
  8. +7
    -0
      build/Release
  9. +5
    -0
      build/Release_amd64
  10. +8
    -0
      build/_gpg
  11. +6
    -0
      build/basecoind/DEBIAN/changelog
  12. +1
    -0
      build/basecoind/DEBIAN/compat
  13. +14
    -0
      build/basecoind/DEBIAN/control
  14. +21
    -0
      build/basecoind/DEBIAN/copyright
  15. +41
    -0
      build/basecoind/DEBIAN/postinst
  16. +41
    -0
      build/basecoind/DEBIAN/postrm
  17. +38
    -0
      build/basecoind/DEBIAN/preinst
  18. +38
    -0
      build/basecoind/DEBIAN/prerm
  19. +2
    -0
      build/basecoind/etc/systemd/system-preset/50-basecoind.preset
  20. +18
    -0
      build/basecoind/etc/systemd/system/basecoind.service
  21. +12
    -0
      build/basecoind/usr/share/basecoind/key.json
  22. +12
    -0
      build/basecoind/usr/share/basecoind/key2.json
  23. +6
    -0
      build/ethermint/DEBIAN/changelog
  24. +1
    -0
      build/ethermint/DEBIAN/compat
  25. +15
    -0
      build/ethermint/DEBIAN/control
  26. +21
    -0
      build/ethermint/DEBIAN/copyright
  27. +46
    -0
      build/ethermint/DEBIAN/postinst
  28. +41
    -0
      build/ethermint/DEBIAN/postrm
  29. +38
    -0
      build/ethermint/DEBIAN/preinst
  30. +38
    -0
      build/ethermint/DEBIAN/prerm
  31. +2
    -0
      build/ethermint/etc/systemd/system-preset/50-ethermint.preset
  32. +17
    -0
      build/ethermint/etc/systemd/system/ethermint.service
  33. +6
    -0
      build/gaia/DEBIAN/changelog
  34. +1
    -0
      build/gaia/DEBIAN/compat
  35. +14
    -0
      build/gaia/DEBIAN/control
  36. +21
    -0
      build/gaia/DEBIAN/copyright
  37. +41
    -0
      build/gaia/DEBIAN/postinst
  38. +41
    -0
      build/gaia/DEBIAN/postrm
  39. +38
    -0
      build/gaia/DEBIAN/preinst
  40. +38
    -0
      build/gaia/DEBIAN/prerm
  41. +2
    -0
      build/gaia/etc/systemd/system-preset/50-gaia.preset
  42. +17
    -0
      build/gaia/etc/systemd/system/gaia.service
  43. +12
    -0
      build/gaia/usr/share/gaia/key.json
  44. +12
    -0
      build/gaia/usr/share/gaia/key2.json
  45. +36
    -0
      build/generate-spec
  46. +26
    -0
      build/sign
  47. +55
    -0
      build/spectemplates/app-template.spec
  48. +5
    -0
      build/spectemplates/basecoind.data
  49. +5
    -0
      build/spectemplates/ethermint.data
  50. +60
    -0
      build/spectemplates/ethermint.spec
  51. +5
    -0
      build/spectemplates/gaia.data
  52. +31
    -0
      build/spectemplates/tendermint.spec
  53. +1
    -0
      build/tendermint.list
  54. +12
    -0
      build/tendermint.repo
  55. +6
    -0
      build/tendermint/DEBIAN/changelog
  56. +1
    -0
      build/tendermint/DEBIAN/compat
  57. +14
    -0
      build/tendermint/DEBIAN/control
  58. +21
    -0
      build/tendermint/DEBIAN/copyright
  59. +192
    -0
      mintnet-kubernetes/LICENSE
  60. +290
    -0
      mintnet-kubernetes/README.rst
  61. +265
    -0
      mintnet-kubernetes/app.template.yaml
  62. BIN
      mintnet-kubernetes/assets/gce1.png
  63. BIN
      mintnet-kubernetes/assets/gce2.png
  64. BIN
      mintnet-kubernetes/assets/statefulset.png
  65. BIN
      mintnet-kubernetes/assets/t_plus_k.png
  66. +10
    -0
      mintnet-kubernetes/examples/basecoin/Makefile
  67. +42
    -0
      mintnet-kubernetes/examples/basecoin/README.md
  68. +334
    -0
      mintnet-kubernetes/examples/basecoin/app.yaml
  69. +100
    -0
      mintnet-kubernetes/examples/basecoin/lightclient.md
  70. +10
    -0
      mintnet-kubernetes/examples/counter/Makefile
  71. +214
    -0
      mintnet-kubernetes/examples/counter/app.yaml
  72. +17
    -0
      mintnet-kubernetes/examples/dummy/Makefile
  73. +196
    -0
      mintnet-kubernetes/examples/dummy/app.yaml
  74. +13
    -0
      mintnet-kubernetes/examples/dummy/tm-monitor-pod.yaml
  75. +19
    -0
      mintnet-kubernetes/examples/dummy/transacter-pod.yaml
  76. +6
    -0
      tm-bench/Dockerfile
  77. +12
    -0
      tm-bench/Dockerfile.dev
  78. +337
    -0
      tm-bench/Gopkg.lock
  79. +54
    -0
      tm-bench/Gopkg.toml
  80. +204
    -0
      tm-bench/LICENSE
  81. +116
    -0
      tm-bench/Makefile
  82. +69
    -0
      tm-bench/README.md
  83. +18
    -0
      tm-bench/bench_test.go
  84. +300
    -0
      tm-bench/main.go
  85. +252
    -0
      tm-bench/transacter.go
  86. +6
    -0
      tm-monitor/Dockerfile
  87. +12
    -0
      tm-monitor/Dockerfile.dev
  88. +291
    -0
      tm-monitor/Gopkg.lock
  89. +58
    -0
      tm-monitor/Gopkg.toml
  90. +204
    -0
      tm-monitor/LICENSE
  91. +116
    -0
      tm-monitor/Makefile
  92. +77
    -0
      tm-monitor/README.md
  93. +298
    -0
      tm-monitor/eventmeter/eventmeter.go
  94. +88
    -0
      tm-monitor/main.go
  95. +69
    -0
      tm-monitor/mock/eventmeter.go
  96. +251
    -0
      tm-monitor/monitor/monitor.go
  97. +72
    -0
      tm-monitor/monitor/monitor_test.go
  98. +199
    -0
      tm-monitor/monitor/network.go
  99. +79
    -0
      tm-monitor/monitor/network_test.go
  100. +260
    -0
      tm-monitor/monitor/node.go

+ 7
- 0
.gitignore View File

@ -27,3 +27,10 @@ scripts/cutWALUntil/cutWALUntil
libs/pubsub/query/fuzz_test/output
shunit2
*/vendor
*/.glide
.terraform
terraform.tfstate
terraform.tfstate.backup
terraform.tfstate.d

+ 3
- 0
CODEOWNERS View File

@ -0,0 +1,3 @@
* @melekes @Greg-Szabo
*.md @zramsay
*.rst @zramsay

+ 3
- 0
README.md View File

@ -0,0 +1,3 @@
# tools
Tools for working with tendermint and associated technologies. Documentation can be found in the `README.md` of each the `tm-bench/` and `tm-monitor/` directories.

+ 4
- 0
build/.gitignore View File

@ -0,0 +1,4 @@
BUILD
RPMS
SPECS
tmp

+ 204
- 0
build/LICENSE View File

@ -0,0 +1,204 @@
Tendermint Core
License: Apache2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 All in Bits, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 289
- 0
build/Makefile View File

@ -0,0 +1,289 @@
##
# Extra checks, because we do not use autoconf.
##
requirements_check = true
gpg_check = false
go_min_version = 1.9.4
gpg_key = 2122CBE9
ifeq ($(requirements_check),true)
ifndef GOPATH
$(error GOPATH not set)
else
go_version := $(shell go version | sed "s/^.* go\([0-9\.]*\) .*$$/\1/" )
$(info Found go version $(go_version))
go_version_check := $(shell echo -e "$(go_min_version)\n$(go_version)" | sort -V | head -1)
ifneq ($(go_min_version),$(go_version_check))
$(error go version go_min_version or above is required)
endif
endif
ifeq ($(gpg_check),true)
gpg_check := $(shell gpg -K | grep '/$(gpg_key) ' | sed 's,^.*/\($(gpg_key)\) .*$$,\1,')
ifneq ($(gpg_check),$(gpg_key))
$(error GPG key $(gpg_key) not found.)
else
$(info GPG key $(gpg_key) found)
endif
ifndef GPG_PASSPHRASE
$(error GPG_PASSPHRASE not set)
endif
endif
endif
###
# Here comes the real deal
###
binaries = tendermint basecoind ethermint gaia
build-binaries = build-tendermint build-basecoind build-ethermint build-gaia
package-rpm = package-rpm-tendermint package-rpm-basecoind package-rpm-ethermint package-rpm-gaia
install-rpm = install-rpm-tendermint install-rpm-basecoind install-rpm-ethermint install-rpm-gaia
package-deb = package-deb-tendermint package-deb-basecoind package-deb-ethermint package-deb-gaia
install-deb = install-deb-tendermint install-deb-basecoind install-deb-ethermint install-deb-gaia
all: $(binaries)
build: $(build-binaries)
package: $(package-rpm) $(package-deb)
install: $(install-rpm) $(install-deb)
$(binaries): %: build-% package-rpm-% package-deb-%
###
# Build the binaries
###
git-branch:
$(eval GIT_BRANCH=$(shell echo $${GIT_BRANCH:-master}))
gopath-setup:
test -d $(GOPATH) || mkdir -p $(GOPATH)
test -d $(GOPATH)/bin || mkdir -p $(GOPATH)/bin
test -d $(GOPATH)/src || mkdir -p $(GOPATH)/src
build-tendermint: git-branch gopath-setup
@echo "*** Building tendermint"
go get -d -u github.com/tendermint/tendermint/cmd/tendermint
cd $(GOPATH)/src/github.com/tendermint/tendermint && git checkout "$(GIT_BRANCH)" && git pull
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/tendermint get_tools get_vendor_deps build
cp $(GOPATH)/src/github.com/tendermint/tendermint/build/tendermint $(GOPATH)/bin
@echo "*** Built tendermint"
build-ethermint: git-branch gopath-setup
@echo "*** Building ethermint"
go get -d -u github.com/tendermint/ethermint/cmd/ethermint
cd $(GOPATH)/src/github.com/tendermint/ethermint && git checkout "$(GIT_BRANCH)" && git pull
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/ethermint get_vendor_deps build
cp $(GOPATH)/src/github.com/tendermint/ethermint/build/ethermint $(GOPATH)/bin
@echo "*** Built ethermint"
build-gaia: git-branch gopath-setup
@echo "*** Building gaia"
go get -d -u go github.com/cosmos/gaia || echo "Workaround for go downloads."
cd $(GOPATH)/src/github.com/cosmos/gaia && git checkout "$(GIT_BRANCH)" && git pull
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/gaia get_vendor_deps install
@echo "*** Built gaia"
build-basecoind: git-branch gopath-setup
@echo "*** Building basecoind from cosmos-sdk"
go get -d -u github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoind
cd $(GOPATH)/src/github.com/cosmos/cosmos-sdk && git checkout "$(GIT_BRANCH)" && git pull
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/cosmos-sdk get_tools get_vendor_deps build
cp $(GOPATH)/src/github.com/cosmos/cosmos-sdk/build/basecoind $(GOPATH)/bin/basecoind
@echo "*** Built basecoind from cosmos-sdk"
###
# Prepare package files
###
# set app_version
version-%:
@echo "Checking if binary exists"
test -f $(GOPATH)/bin/$*
@echo "BUILD_NUMBER is $(BUILD_NUMBER)"
test -n "$(BUILD_NUMBER)"
$(eval $*_version=$(shell $(GOPATH)/bin/$* version | head -1 | cut -d- -f1 | sed 's/^\(ethermint:\s*\|\)\(v\|\)//' | tr -d '\t ' ))
# set build_folder
folder-%: version-%
$(eval build_folder=BUILD/$*-$($*_version)-$(BUILD_NUMBER))
# clean up folder structure for package files
prepare-files = rm -rf $(build_folder) && mkdir -p $(build_folder) && cp -r ./$(1)/* $(build_folder) && mkdir -p $(build_folder)/usr/bin && cp $(GOPATH)/bin/$(1) $(build_folder)/usr/bin
##
## Package customizations for the different applications
##
prepare-tendermint =
prepare-ethermint = mkdir -p $(build_folder)/etc/ethermint && \
cp $(GOPATH)/src/github.com/tendermint/ethermint/setup/genesis.json $(build_folder)/etc/ethermint/genesis.json && \
cp -r $(GOPATH)/src/github.com/tendermint/ethermint/setup/keystore $(build_folder)/etc/ethermint
prepare-gaia =
prepare-basecoind = cp $(GOPATH)/bin/basecoind $(build_folder)/usr/bin
###
# Package the binary for CentOS/RedHat (RPM) and Debian/Ubuntu (DEB)
###
# Depends on rpmbuild, sorry, this can only be built on CentOS/RedHat machines.
package-rpm-%: folder-%
@echo "*** Packaging RPM $* version $($*_version)"
$(call prepare-files,$*)
$(call prepare-$*)
rm -rf $(build_folder)/DEBIAN
mkdir -p $(build_folder)/usr/share/licenses/$*
cp ./LICENSE $(build_folder)/usr/share/licenses/$*/LICENSE
chmod -Rf a+rX,u+w,g-w,o-w $(build_folder)
mkdir -p {SPECS,tmp}
./generate-spec $* spectemplates SPECS
sed -i "s/@VERSION@/$($*_version)/" SPECS/$*.spec
sed -i "s/@BUILD_NUMBER@/$(BUILD_NUMBER)/" SPECS/$*.spec
sed -i "s/@PACKAGE_NAME@/$*/" SPECS/$*.spec
rpmbuild -bb SPECS/$*.spec --define "_topdir `pwd`" --define "_tmppath `pwd`/tmp"
./sign RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm "$(gpg_key)" "`which gpg`"
rpm -Kv RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm || echo "rpm returns non-zero exist for some reason. ($?)"
@echo "*** Packaged RPM $* version $($*_version)"
package-deb-%: folder-%
@echo "*** Packaging DEB $* version $($*_version)-$(BUILD_NUMBER)"
$(call prepare-files,$*)
$(call prepare-$*)
mkdir -p $(build_folder)/usr/share/doc/$*
cp $(build_folder)/DEBIAN/copyright $(build_folder)/usr/share/doc/$*
chmod -Rf a+rX,u+w,g-w,o-w $(build_folder)
sed -i "s/@VERSION@/$($*_version)-$(BUILD_NUMBER)/" $(build_folder)/DEBIAN/changelog
sed -i "s/@STABILITY@/stable/" $(build_folder)/DEBIAN/changelog
sed -i "s/@DATETIMESTAMP@/`date +%a,\ %d\ %b\ %Y\ %T\ %z`/" $(build_folder)/DEBIAN/changelog
sed -i "s/@VERSION@/$($*_version)-$(BUILD_NUMBER)/" $(build_folder)/DEBIAN/control
gzip -c $(build_folder)/DEBIAN/changelog > $(build_folder)/usr/share/doc/$*/changelog.Debian.gz
gzip -c $(build_folder)/DEBIAN/changelog > $(build_folder)/usr/share/doc/$*/changelog.Debian.amd64.gz
sed -i "s/@INSTALLEDSIZE@/`du -ks $(build_folder) | cut -f 1`/" $(build_folder)/DEBIAN/control
cd $(build_folder) && tar --owner=root --group=root -cvJf ../../tmp/data.tar.xz --exclude DEBIAN *
cd $(build_folder)/DEBIAN && tar --owner=root --group=root -cvzf ../../../tmp/control.tar.gz *
echo "2.0" > tmp/debian-binary
cp ./_gpg tmp/
cd tmp && sed -i "s/@DATETIMESTAMP@/`date +%a\ %b\ %d\ %T\ %Y`/" _gpg
cd tmp && sed -i "s/@BINMD5@/`md5sum debian-binary | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@BINSHA1@/`sha1sum debian-binary | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@BINSIZE@/`stat -c %s debian-binary | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@CONMD5@/`md5sum control.tar.gz | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@CONSHA1@/`sha1sum control.tar.gz | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@CONSIZE@/`stat -c %s control.tar.gz | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@DATMD5@/`md5sum data.tar.xz | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@DATSHA1@/`sha1sum data.tar.xz | cut -d\ -f1`/" _gpg
cd tmp && sed -i "s/@DATSIZE@/`stat -c %s data.tar.xz | cut -d\ -f1`/" _gpg
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --clearsign tmp/_gpg
mv tmp/_gpg.asc tmp/_gpgbuilder
ar r tmp/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb tmp/debian-binary tmp/control.tar.gz tmp/data.tar.xz tmp/_gpgbuilder
mv tmp/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb RPMS/
rm tmp/debian-binary tmp/control.tar.gz tmp/data.tar.xz tmp/_gpgbuilder tmp/_gpg
@echo "*** Packaged DEB $* version $($*_version)-$(BUILD_NUMBER)"
install-rpm-%: version-%
#Make sure your host has the IAM role to read/write the S3 bucket OR that you set up ~/.boto
@echo "*** Uploading $*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm to AWS $(DEVOPS_PATH)CentOS repository"
aws s3 sync s3://tendermint-packages/$(DEVOPS_PATH)centos/ tmp/s3/ --delete
mkdir -p tmp/s3/7/os/x86_64/Packages
cp RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm tmp/s3/7/os/x86_64/Packages
cp ./RPM-GPG-KEY-Tendermint tmp/s3/7/os/x86_64/
cp ./tendermint.repo tmp/s3/7/os/x86_64/
rm -f tmp/s3/7/os/x86_64/repodata/*.bz2 tmp/s3/7/os/x86_64/repodata/*.gz tmp/s3/7/os/x86_64/repodata/repomd.xml.asc
createrepo tmp/s3/7/os/x86_64/Packages -u https://tendermint-packages.interblock.io/$(DEVOPS_PATH)centos/7/os/x86_64/Packages -o tmp/s3/7/os/x86_64 --update -S --repo Tendermint --content tendermint --content basecoind --content ethermint
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --detach-sign -a tmp/s3/7/os/x86_64/repodata/repomd.xml
aws s3 sync tmp/s3/ s3://tendermint-packages/$(DEVOPS_PATH)centos/ --delete --acl public-read
@echo "*** Uploaded $* to AWS $(DEVOPS_PATH)CentOS repository"
install-deb-%: version-%
@echo "*** Uploading $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb to AWS $(DEVOPS_PATH)Debian repository"
@echo "Testing if $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb is already uploaded"
test ! -f tmp/debian-s3/pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb
aws s3 sync s3://tendermint-packages/$(DEVOPS_PATH)debian/ tmp/debian-s3/ --delete
@echo "Testing if $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb is already uploaded"
test ! -f tmp/debian-s3/pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb
cp ./tendermint.list tmp/debian-s3/
mkdir -p tmp/debian-s3/pool tmp/debian-s3/dists/stable/main/binary-amd64
cp RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb tmp/debian-s3/pool
cp ./Release_amd64 tmp/debian-s3/dists/stable/main/binary-amd64/Release
#Packages / Packages.gz
echo > tmp/Package
echo "Filename: pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb" >> tmp/Package
echo "MD5sum: `md5sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package
echo "SHA1: `sha1sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package
echo "SHA256: `sha256sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package
echo "Size: `stat -c %s RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package
cat BUILD/$*-$($*_version)-$(BUILD_NUMBER)/DEBIAN/control >> tmp/Package
cat tmp/Package >> tmp/debian-s3/dists/stable/main/binary-amd64/Packages
rm -f tmp/debian-s3/dists/stable/main/binary-amd64/Packages.gz
gzip -c tmp/debian-s3/dists/stable/main/binary-amd64/Packages > tmp/debian-s3/dists/stable/main/binary-amd64/Packages.gz
rm -f tmp/Package
#main / Release / InRelease / Release.gpg
cp ./Release tmp/debian-s3/dists/stable/main/Release
rm -f tmp/debian-s3/dists/stable/main/InRelease
rm -f tmp/debian-s3/dists/stable/main/Release.gpg
echo "MD5Sum:" >> tmp/debian-s3/dists/stable/main/Release
cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; md5sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release
echo "SHA1:" >> tmp/debian-s3/dists/stable/main/Release
cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha1sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release
echo "SHA256:" >> tmp/debian-s3/dists/stable/main/Release
cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha256sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA256 -b -a tmp/debian-s3/dists/stable/main/Release
mv tmp/debian-s3/dists/stable/main/Release.asc tmp/debian-s3/dists/stable/main/Release.gpg
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA512 --clearsign tmp/debian-s3/dists/stable/main/Release
mv tmp/debian-s3/dists/stable/main/Release.asc tmp/debian-s3/dists/stable/main/InRelease
#stable / Release / InRelease / Release.gpg
cp ./Release tmp/debian-s3/dists/stable/Release
rm -f tmp/debian-s3/dists/stable/InRelease
rm -f tmp/debian-s3/dists/stable/Release.gpg
echo "MD5Sum:" >> tmp/debian-s3/dists/stable/Release
cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; md5sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release
echo "SHA1:" >> tmp/debian-s3/dists/stable/Release
cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha1sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release
echo "SHA256:" >> tmp/debian-s3/dists/stable/Release
cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha256sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA256 -b -a tmp/debian-s3/dists/stable/Release
mv tmp/debian-s3/dists/stable/Release.asc tmp/debian-s3/dists/stable/Release.gpg
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA512 --clearsign tmp/debian-s3/dists/stable/Release
mv tmp/debian-s3/dists/stable/Release.asc tmp/debian-s3/dists/stable/InRelease
aws s3 sync tmp/debian-s3/ s3://tendermint-packages/$(DEVOPS_PATH)debian/ --delete --acl public-read
@echo "*** Uploaded $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb to AWS $(DEVOPS_PATH)Debian repository"
mostlyclean:
rm -rf {BUILDROOT,SOURCES,SPECS,SRPMS,tmp}
clean: mostlyclean
rm -rf {BUILD,RPMS}
distclean: clean
rm -rf $(GOPATH)/src/github.com/tendermint/tendermint
rm -rf $(GOPATH)/src/github.com/cosmos/cosmos-sdk
rm -rf $(GOPATH)/src/github.com/tendermint/ethermint
rm -rf $(GOPATH)/bin/tendermint
rm -rf $(GOPATH)/bin/basecoind
rm -rf $(GOPATH)/bin/ethermint
rm -rf $(GOPATH)/bin/gaia
.PHONY : clean

+ 19
- 0
build/RPM-GPG-KEY-Tendermint View File

@ -0,0 +1,19 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.0.22 (GNU/Linux)
mQENBFk97ngBCADaiPQFKJI7zWYdUKqC490DzY9g9LatsWoJErK5LuMXwEnF5i+a
UkygueukA4C5U7L71l5EeOB9rtb6AbkF4IEZsmmp93APec/3Vfbac9xvK4dBdiht
F8SrazPdHeR6AKcZH8ZpG/+mdONvGb/gEgtxVjaeIJFpCbjKLlKEXazh2zamhhth
q+Nn/17QmI3KBiaGqQK5w4kGZ4mZPy6fXMQhW5dDMq9f4anlGIAYi9O53dVxsx2S
5d+NHuGer5Ps0u6WMJi/e+UT2EGwzP6ygOxkIjyhMFuVftabOtSSrRHHetw8UAaI
N/RPn2gSbQtOQ7unzHDXp3/o6/r2nDEErPyJABEBAAG0LkdyZWcgU3phYm8gKFRl
bmRlcm1pbnQpIDxncmVnQHBoaWxvc29iZWFyLmNvbT6JATkEEwECACMFAlk97ngC
GwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRDIkIHIISLL6bX/CACXTKmO
u5XgvJICH0pHNeVS5/4Om1Rsg1xNmEkGFBP8N2fqn576exbOLgWLSyNHTEyrJNoc
iTeUtod2qqbVGwRgWm1zeiP8NBYiQ9SUbqskIqcPavJNGWIxsCB0p/odoZah8xSj
tGrkoyoxrc+7z2JgKYK8SVSkJXQkzuc5/ZlY85ci5gPKQhlo5YDqGo+4U9n/Ieo5
nkF8LBalFC2j7A7sQNroEicpulpGhIq3jyUHtadX01z3pNzuX+wfHX9futoet0YS
tG2007WoPGV0whGnoKxmk0JhwzhscC2XNtJl1GZcwqOOlPU9eGtZuPKj/HBAlRtz
4xTOAcklpg8soqRA
=jNDW
-----END PGP PUBLIC KEY BLOCK-----

+ 7
- 0
build/Release View File

@ -0,0 +1,7 @@
Origin: Tendermint
Label: Tendermint
Suite: stable
Date: Fri, 16 Jun 2017 19:44:00 UTC
Architectures: amd64
Components: main
Description: Tendermint repository

+ 5
- 0
build/Release_amd64 View File

@ -0,0 +1,5 @@
Archive: stable
Component: main
Origin: Tendermint
Label: Tendermint
Architecture: amd64

+ 8
- 0
build/_gpg View File

@ -0,0 +1,8 @@
Version: 4
Signer:
Date: @DATETIMESTAMP@
Role: builder
Files:
@BINMD5@ @BINSHA1@ @BINSIZE@ debian-binary
@CONMD5@ @CONSHA1@ @CONSIZE@ control.tar.gz
@DATMD5@ @DATSHA1@ @DATSIZE@ data.tar.xz

+ 6
- 0
build/basecoind/DEBIAN/changelog View File

@ -0,0 +1,6 @@
basecoind (@VERSION@) @STABILITY@; urgency=medium
* Automatic build. See https://github.com/cosmos/cosmos-sdk for more information.
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@

+ 1
- 0
build/basecoind/DEBIAN/compat View File

@ -0,0 +1 @@
9

+ 14
- 0
build/basecoind/DEBIAN/control View File

@ -0,0 +1,14 @@
Source: basecoind
Section: net
Priority: optional
Maintainer: Greg Szabo <greg@philosobear.com>
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Homepage: https://tendermint.com
Package: basecoind
Architecture: amd64
Version: @VERSION@
Installed-Size: @INSTALLEDSIZE@
Description: basecoind is a Proof-of-Stake cryptocurrency and framework
Basecoind is an ABCI application designed to be used with the Tendermint consensus engine to form a Proof-of-Stake cryptocurrency. It also provides a general purpose framework for extending the feature-set of the cryptocurrency by implementing plugins.

+ 21
- 0
build/basecoind/DEBIAN/copyright View File

@ -0,0 +1,21 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: basecoind
Source: https://github.com/cosmos/cosmos-sdk
Files: *
Copyright: 2017 All In Bits, Inc.
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License 2.0 can be found
in the file `/usr/share/common-licenses/Apache-2.0'.

+ 41
- 0
build/basecoind/DEBIAN/postinst View File

@ -0,0 +1,41 @@
#!/bin/sh
# postinst script for basecoind
#
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
chown basecoind.basecoind /etc/basecoind
sudo -Hu basecoind basecoind node init --home /etc/basecoind 2B24DEE2364762300168DF19B6C18BCE2D399EA2
systemctl daemon-reload
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 41
- 0
build/basecoind/DEBIAN/postrm View File

@ -0,0 +1,41 @@
#!/bin/sh
# postrm script for basecoin
#
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
upgrade|failed-upgrade|abort-upgrade)
systemctl daemon-reload
;;
purge|remove|abort-install|disappear)
systemctl daemon-reload
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 38
- 0
build/basecoind/DEBIAN/preinst View File

@ -0,0 +1,38 @@
#!/bin/sh
# preinst script for basecoind
#
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
if ! grep -q '^basecoind:' /etc/passwd ; then
useradd -k /dev/null -r -m -b /etc basecoind
chmod 755 /etc/basecoind
fi
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 38
- 0
build/basecoind/DEBIAN/prerm View File

@ -0,0 +1,38 @@
#!/bin/sh
# prerm script for basecoin
#
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|upgrade|deconfigure)
systemctl stop basecoind 2> /dev/null || :
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 2
- 0
build/basecoind/etc/systemd/system-preset/50-basecoind.preset View File

@ -0,0 +1,2 @@
disable basecoind.service

+ 18
- 0
build/basecoind/etc/systemd/system/basecoind.service View File

@ -0,0 +1,18 @@
[Unit]
Description=Basecoind
Requires=network-online.target
After=network-online.target
[Service]
Environment="BCHOME=/etc/basecoind"
Restart=on-failure
User=basecoind
Group=basecoind
PermissionsStartOnly=true
ExecStart=/usr/bin/basecoind start
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target

+ 12
- 0
build/basecoind/usr/share/basecoind/key.json View File

@ -0,0 +1,12 @@
{
"address": "1B1BE55F969F54064628A63B9559E7C21C925165",
"priv_key": {
"type": "ed25519",
"data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
},
"pub_key": {
"type": "ed25519",
"data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
}
}

+ 12
- 0
build/basecoind/usr/share/basecoind/key2.json View File

@ -0,0 +1,12 @@
{
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090",
"priv_key": {
"type": "ed25519",
"data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
},
"pub_key": {
"type": "ed25519",
"data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
}
}

+ 6
- 0
build/ethermint/DEBIAN/changelog View File

@ -0,0 +1,6 @@
ethermint (@VERSION@) @STABILITY@; urgency=medium
* Automatic build. See https://github.com/tendermint/tendermint for more information.
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@

+ 1
- 0
build/ethermint/DEBIAN/compat View File

@ -0,0 +1 @@
9

+ 15
- 0
build/ethermint/DEBIAN/control View File

@ -0,0 +1,15 @@
Source: ethermint
Section: net
Priority: optional
Maintainer: Greg Szabo <greg@philosobear.com>
Build-Depends: debhelper (>=9)
Depends: tendermint (>=0.11.0)
Standards-Version: 3.9.6
Homepage: https://tendermint.com
Package: ethermint
Architecture: amd64
Version: @VERSION@
Installed-Size: @INSTALLEDSIZE@
Description: ethermint enables ethereum as an ABCI application on tendermint and the COSMOS hub
Ethermint enables ethereum to run as an ABCI application on tendermint and the COSMOS hub. This application allows you to get all the benefits of ethereum without having to run your own miners.

+ 21
- 0
build/ethermint/DEBIAN/copyright View File

@ -0,0 +1,21 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ethermint
Source: https://github.com/tendermint/ethermint
Files: *
Copyright: 2017 All In Bits, Inc.
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License 2.0 can be found
in the file `/usr/share/common-licenses/Apache-2.0'.

+ 46
- 0
build/ethermint/DEBIAN/postinst View File

@ -0,0 +1,46 @@
#!/bin/sh
# postinst script for ethermint
#
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
chown ethermint.ethermint /etc/ethermint
chown ethermint.ethermint /etc/ethermint/genesis.json
chown ethermint.ethermint /etc/ethermint/keystore
chown ethermint.ethermint /etc/ethermint/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc
sudo -Hu ethermint /usr/bin/ethermint --datadir /etc/ethermint init /etc/ethermint/genesis.json
sudo -Hu ethermint tendermint init --home /etc/ethermint
systemctl daemon-reload
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 41
- 0
build/ethermint/DEBIAN/postrm View File

@ -0,0 +1,41 @@
#!/bin/sh
# postrm script for ethermint
#
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
upgrade|failed-upgrade|abort-upgrade)
systemctl daemon-reload
;;
purge|remove|abort-install|disappear)
systemctl daemon-reload
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 38
- 0
build/ethermint/DEBIAN/preinst View File

@ -0,0 +1,38 @@
#!/bin/sh
# preinst script for ethermint
#
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
if ! grep -q '^ethermint:' /etc/passwd ; then
useradd -k /dev/null -r -m -b /etc ethermint
chmod 755 /etc/ethermint
fi
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 38
- 0
build/ethermint/DEBIAN/prerm View File

@ -0,0 +1,38 @@
#!/bin/sh
# prerm script for ethermint
#
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|upgrade|deconfigure)
systemctl stop ethermint 2> /dev/null || :
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 2
- 0
build/ethermint/etc/systemd/system-preset/50-ethermint.preset View File

@ -0,0 +1,2 @@
disable ethermint.service

+ 17
- 0
build/ethermint/etc/systemd/system/ethermint.service View File

@ -0,0 +1,17 @@
[Unit]
Description=Ethermint
Requires=network-online.target
After=network-online.target
[Service]
Restart=on-failure
User=ethermint
Group=ethermint
PermissionsStartOnly=true
ExecStart=/usr/bin/ethermint --datadir /etc/ethermint
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target

+ 6
- 0
build/gaia/DEBIAN/changelog View File

@ -0,0 +1,6 @@
gaia (@VERSION@) @STABILITY@; urgency=medium
* Automatic build. See https://github.com/tendermint/basecoin for more information.
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@

+ 1
- 0
build/gaia/DEBIAN/compat View File

@ -0,0 +1 @@
9

+ 14
- 0
build/gaia/DEBIAN/control View File

@ -0,0 +1,14 @@
Source: gaia
Section: net
Priority: optional
Maintainer: Greg Szabo <greg@philosobear.com>
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Homepage: https://cosmos.network
Package: gaia
Architecture: amd64
Version: @VERSION@
Installed-Size: @INSTALLEDSIZE@
Description: gaia - Tendermint Cosmos delegation game chain
Gaia description comes later.

+ 21
- 0
build/gaia/DEBIAN/copyright View File

@ -0,0 +1,21 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: gaia
Source: https://github.com/cosmos/gaia
Files: *
Copyright: 2017 All In Bits, Inc.
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License 2.0 can be found
in the file `/usr/share/common-licenses/Apache-2.0'.

+ 41
- 0
build/gaia/DEBIAN/postinst View File

@ -0,0 +1,41 @@
#!/bin/sh
# postinst script for gaia
#
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
chown gaia.gaia /etc/gaia
sudo -Hu gaia gaia node init --home /etc/gaia 2B24DEE2364762300168DF19B6C18BCE2D399EA2
systemctl daemon-reload
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 41
- 0
build/gaia/DEBIAN/postrm View File

@ -0,0 +1,41 @@
#!/bin/sh
# postrm script for gaia
#
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
upgrade|failed-upgrade|abort-upgrade)
systemctl daemon-reload
;;
purge|remove|abort-install|disappear)
systemctl daemon-reload
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 38
- 0
build/gaia/DEBIAN/preinst View File

@ -0,0 +1,38 @@
#!/bin/sh
# preinst script for gaia
#
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
if ! grep -q '^gaia:' /etc/passwd ; then
useradd -k /dev/null -r -m -b /etc gaia
chmod 755 /etc/gaia
fi
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 38
- 0
build/gaia/DEBIAN/prerm View File

@ -0,0 +1,38 @@
#!/bin/sh
# prerm script for gaia
#
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|upgrade|deconfigure)
systemctl stop gaia 2> /dev/null || :
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

+ 2
- 0
build/gaia/etc/systemd/system-preset/50-gaia.preset View File

@ -0,0 +1,2 @@
disable gaia.service

+ 17
- 0
build/gaia/etc/systemd/system/gaia.service View File

@ -0,0 +1,17 @@
[Unit]
Description=Gaia
Requires=network-online.target
After=network-online.target
[Service]
Restart=on-failure
User=gaia
Group=gaia
PermissionsStartOnly=true
ExecStart=/usr/bin/gaia node start --home=/etc/gaia
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target

+ 12
- 0
build/gaia/usr/share/gaia/key.json View File

@ -0,0 +1,12 @@
{
"address": "1B1BE55F969F54064628A63B9559E7C21C925165",
"priv_key": {
"type": "ed25519",
"data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
},
"pub_key": {
"type": "ed25519",
"data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
}
}

+ 12
- 0
build/gaia/usr/share/gaia/key2.json View File

@ -0,0 +1,12 @@
{
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090",
"priv_key": {
"type": "ed25519",
"data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
},
"pub_key": {
"type": "ed25519",
"data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
}
}

+ 36
- 0
build/generate-spec View File

@ -0,0 +1,36 @@
#!/bin/bash
if [ $# -ne 3 ]; then
echo "Usage: $0 <application> <template_source_dir> <SPEC_dir>"
exit 1
fi
app=$1
src=$2
dst=$3
# Find spectemplate
if [ ! -f "$src/$app.spec" ]; then
if [ ! -f "$src/app-template.spec" ]; then
echo "Source template not found."
exit 1
else
srcfile="$src/app-template.spec"
fi
else
srcfile="$src/$app.spec"
fi
# Copy spectemplate to SPECS
cp "$srcfile" "$dst/$app.spec"
# Apply any variables defined in .data
if [ -f "$src/$app.data" ]; then
srcdata="$src/$app.data"
source "$srcdata"
for var in `grep -v -e ^# -e ^\s*$ "$srcdata" | grep = | sed 's/\s*=.*$//'`
do
sed -i "s\\@${var}@\\${!var}\\g" "$dst/$app.spec"
done
fi

+ 26
- 0
build/sign View File

@ -0,0 +1,26 @@
#!/usr/bin/expect -f
set timeout 3
set PACKAGE [lindex $argv 0]
set GPG_NAME [lindex $argv 1]
set GPG_PATH [lindex $argv 2]
set GPG_PASSPHRASE $env(GPG_PASSPHRASE)
if {[llength $argv] == 0} {
send_user "Usage: ./sign <rpm_package> <gpg_key> <gpg_binary>\n"
exit 1
}
send_user "\nSigning $PACKAGE\n"
spawn rpmsign --resign $PACKAGE --define "_signature gpg" --define "_gpg_name $GPG_NAME" --define "_gpgbin $GPG_PATH"
expect {
timeout { send_user "\nTimeout signing $PACKAGE\n"; exit 1 }
"Enter pass phrase:"
}
send "$GPG_PASSPHRASE\r"
expect {
timeout { send_user "\nTimeout signing $PACKAGE\n"; exit 1 }
"Pass phrase is good."
}
interact
sleep 3

+ 55
- 0
build/spectemplates/app-template.spec View File

@ -0,0 +1,55 @@
Version: @VERSION@
Release: @BUILD_NUMBER@
%define __spec_install_post %{nil}
%define debug_package %{nil}
%define __os_install_post %{nil}
Name: @PACKAGE_NAME@
Summary: @PACKAGE_SUMMARY@
License: Apache 2.0
URL: @PACKAGE_URL@
Packager: Greg Szabo
@PACKAGE_ADDITIONAL_HEADER@
%description
@PACKAGE_DESCRIPTION@
%pre
if ! %{__grep} -q '^%{name}:' /etc/passwd ; then
useradd -r -b %{_sysconfdir} %{name}
mkdir -p %{_sysconfdir}/%{name}
chmod 755 %{_sysconfdir}/%{name}
chown %{name}.%{name} %{_sysconfdir}/%{name}
fi
%prep
# Nothing to do here. - It is done in the Makefile.
%build
# Nothing to do here.
%install
cd %{name}-%{version}-%{release}
%{__cp} -a * %{buildroot}
%post
sudo -Hu %{name} %{name} node init --home %{_sysconfdir}/%{name} 2B24DEE2364762300168DF19B6C18BCE2D399EA2
systemctl daemon-reload
%preun
systemctl stop %{name} 2> /dev/null || :
%postun
systemctl daemon-reload
%files
%ghost %attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name}
%{_bindir}/*
%{_sysconfdir}/systemd/system/*
%{_sysconfdir}/systemd/system-preset/*
%dir %{_datadir}/%{name}
%{_datadir}/%{name}/*
%dir %{_defaultlicensedir}/%{name}
%doc %{_defaultlicensedir}/%{name}/LICENSE

+ 5
- 0
build/spectemplates/basecoind.data View File

@ -0,0 +1,5 @@
PACKAGE_SUMMARY="basecoind is a Proof-of-Stake cryptocurrency and framework"
PACKAGE_URL="https://cosmos.network/"
PACKAGE_ADDITIONAL_HEADER="Provides: basecoind"
PACKAGE_DESCRIPTION="Basecoind is an ABCI application designed to be used with the Tendermint consensus engine to form a Proof-of-Stake cryptocurrency. It also provides a general purpose framework for extending the feature-set of the cryptocurrency by implementing plugins."

+ 5
- 0
build/spectemplates/ethermint.data View File

@ -0,0 +1,5 @@
PACKAGE_SUMMARY="ethermint enables ethereum as an ABCI application on tendermint and the COSMOS hub"
PACKAGE_URL="https://tendermint.com/"
PACKAGE_ADDITIONAL_HEADER="Provides: ethermint"
PACKAGE_DESCRIPTION="Ethermint enables ethereum to run as an ABCI application on tendermint and the COSMOS hub. This application allows you to get all the benefits of ethereum without having to run your own miners."

+ 60
- 0
build/spectemplates/ethermint.spec View File

@ -0,0 +1,60 @@
Version: @VERSION@
Release: @BUILD_NUMBER@
%define __spec_install_post %{nil}
%define debug_package %{nil}
%define __os_install_post %{nil}
Name: @PACKAGE_NAME@
Summary: @PACKAGE_SUMMARY@
License: Apache 2.0
URL: @PACKAGE_URL@
Packager: Greg Szabo
Requires: tendermint >= 0.11.0
@PACKAGE_ADDITIONAL_HEADER@
%description
@PACKAGE_DESCRIPTION@
%pre
if ! %{__grep} -q '^%{name}:' /etc/passwd ; then
useradd -r -b %{_sysconfdir} %{name}
mkdir -p %{_sysconfdir}/%{name}
chmod 755 %{_sysconfdir}/%{name}
chown %{name}.%{name} %{_sysconfdir}/%{name}
fi
%prep
# Nothing to do here. - It is done in the Makefile.
%build
# Nothing to do here.
%install
cd %{name}-%{version}-%{release}
%{__cp} -a * %{buildroot}
%post
sudo -Hu %{name} tendermint init --home %{_sysconfdir}/%{name}
sudo -Hu %{name} %{name} --datadir %{_sysconfdir}/%{name} init %{_sysconfdir}/%{name}/genesis.json
systemctl daemon-reload
%preun
systemctl stop %{name} 2> /dev/null || :
systemctl stop %{name}-service 2> /dev/null || :
%postun
systemctl daemon-reload
%files
%attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name}
%config(noreplace) %attr(0644, %{name}, %{name}) %{_sysconfdir}/%{name}/genesis.json
%attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name}/keystore
%attr(0644, %{name}, %{name}) %{_sysconfdir}/%{name}/keystore/*
%{_bindir}/*
%{_sysconfdir}/systemd/system/*
%{_sysconfdir}/systemd/system-preset/*
%dir %{_defaultlicensedir}/%{name}
%doc %{_defaultlicensedir}/%{name}/LICENSE

+ 5
- 0
build/spectemplates/gaia.data View File

@ -0,0 +1,5 @@
PACKAGE_SUMMARY="gaia - Tendermint Cosmos delegation game chain"
PACKAGE_URL="https://cosmos.network/"
PACKAGE_ADDITIONAL_HEADER=""
PACKAGE_DESCRIPTION="Gaia description comes later."

+ 31
- 0
build/spectemplates/tendermint.spec View File

@ -0,0 +1,31 @@
Version: @VERSION@
Release: @BUILD_NUMBER@
%define __spec_install_post %{nil}
%define debug_package %{nil}
%define __os_install_post %{nil}
Name: tendermint
Summary: securely and consistently replicate an application on many machines
License: Apache 2.0
URL: https://tendermint.com/
Packager: Greg Szabo
%description
Tendermint is software for securely and consistently replicating an application on many machines. By securely, we mean that Tendermint works even if up to 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state.
%prep
# Nothing to do here. - It is done in the Makefile.
%build
# Nothing to do here.
%install
cd %{name}-%{version}-%{release}
%{__cp} -a * %{buildroot}
%files
%{_bindir}/tendermint
%dir %{_defaultlicensedir}/%{name}
%doc %{_defaultlicensedir}/%{name}/LICENSE

+ 1
- 0
build/tendermint.list View File

@ -0,0 +1 @@
deb http://tendermint-packages.s3-website-us-west-1.amazonaws.com/debian stable main

+ 12
- 0
build/tendermint.repo View File

@ -0,0 +1,12 @@
#This is the .repo file for the Tendermint CentOS repositories.
#Although it has only been tested under CentOS 7, it should work under Fedora and RedHat 7 too.
#Currently only 64-bit packages are built.
[tendermint]
name=Tendermint stable releases repository
baseurl=https://do9rmxapsag1v.cloudfront.net/centos/7/os/x86_64
gpgcheck=1
gpgkey=https://do9rmxapsag1v.cloudfront.net/centos/7/os/x86_64/RPM-GPG-KEY-Tendermint
enabled=1
#sslverify = 1

+ 6
- 0
build/tendermint/DEBIAN/changelog View File

@ -0,0 +1,6 @@
tendermint (@VERSION@) @STABILITY@; urgency=medium
* Automatic build. See https://github.com/tendermint/tendermint for more information.
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@

+ 1
- 0
build/tendermint/DEBIAN/compat View File

@ -0,0 +1 @@
9

+ 14
- 0
build/tendermint/DEBIAN/control View File

@ -0,0 +1,14 @@
Source: tendermint
Section: net
Priority: optional
Maintainer: Greg Szabo <greg@philosobear.com>
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Homepage: https://tendermint.com
Package: tendermint
Architecture: amd64
Version: @VERSION@
Installed-Size: @INSTALLEDSIZE@
Description: securely and consistently replicate an application on many machines
Tendermint is software for securely and consistently replicating an application on many machines. By securely, we mean that Tendermint works even if up to 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state.

+ 21
- 0
build/tendermint/DEBIAN/copyright View File

@ -0,0 +1,21 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: tendermint
Source: https://github.com/tendermint/tendermint
Files: *
Copyright: 2017 All In Bits, Inc.
License: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
.
On Debian systems, the full text of the Apache License 2.0 can be found
in the file `/usr/share/common-licenses/Apache-2.0'.

+ 192
- 0
mintnet-kubernetes/LICENSE View File

@ -0,0 +1,192 @@
Copyright (C) 2017 Tendermint
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 290
- 0
mintnet-kubernetes/README.rst View File

@ -0,0 +1,290 @@
Using Kubernetes
================
.. figure:: assets/t_plus_k.png
:alt: Tendermint plus Kubernetes
Tendermint plus Kubernetes
This should primarily be used for testing purposes or for
tightly-defined chains operated by a single stakeholder (see `the
security precautions <#security>`__). If your desire is to launch an
application with many stakeholders, consider using our set of Ansible
scripts.
Quick Start
-----------
For either platform, see the `requirements <https://github.com/kubernetes/minikube#requirements>`__
MacOS
^^^^^
::
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/kubectl
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.18.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
minikube start
git clone https://github.com/tendermint/tools.git && cd tools/mintnet-kubernetes/examples/basecoin && make create
Linux
^^^^^
::
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/kubectl
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.18.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
minikube start
git clone https://github.com/tendermint/tools.git && cd tools/mintnet-kubernetes/examples/basecoin && make create
Verify it worked
~~~~~~~~~~~~~~~~
**Using a shell:**
First wait until all the pods are ``Running``:
``kubectl get pods -w -o wide -L tm``
then query the Tendermint app logs from the first pod:
``kubectl logs -c tm -f tm-0``
finally, use our `Rest API <../specification/rpc.html>`__ to fetch the status of the second pod's Tendermint app.
Note we are using ``kubectl exec`` because pods are not exposed (and should not be) to the
outer network:
``kubectl exec -c tm tm-0 -- curl -s http://tm-1.basecoin:26657/status | json_pp``
**Using the dashboard:**
::
minikube dashboard
Clean up
~~~~~~~~
::
make destroy
Usage
-----
Setup a Kubernetes cluster
^^^^^^^^^^^^^^^^^^^^^^^^^^
- locally using `Minikube <https://github.com/kubernetes/minikube>`__
- on GCE with a single click in the web UI
- on AWS using `Kubernetes
Operations <https://github.com/kubernetes/kops/blob/master/docs/aws.md>`__
- on Linux machines (Digital Ocean) using
`kubeadm <https://kubernetes.io/docs/getting-started-guides/kubeadm/>`__
- on AWS, Azure, GCE or bare metal using `Kargo
(Ansible) <https://kubernetes.io/docs/getting-started-guides/kargo/>`__
Please refer to `the official
documentation <https://kubernetes.io/docs/getting-started-guides/>`__
for overview and comparison of different options.
Kubernetes on Digital Ocean
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Available options:
- `kubeadm (alpha) <https://kubernetes.io/docs/getting-started-guides/kubeadm/>`__
- `kargo <https://kubernetes.io/docs/getting-started-guides/kargo/>`__
- `rancher <http://rancher.com/>`__
- `terraform <https://github.com/hermanjunge/kubernetes-digitalocean-terraform>`__
As you can see, there is no single tool for creating a cluster on DO.
Therefore, choose the one you know and comfortable working with. If you know
and used `terraform <https://www.terraform.io/>`__ before, then choose it. If you
know Ansible, then pick kargo. If none of these seem familiar to you, go with
``kubeadm``. Rancher is a beautiful UI for deploying and managing containers in
production.
Kubernetes on Google Cloud Engine
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Review the `Official Documentation <https://kubernetes.io/docs/getting-started-guides/gce/>`__ for Kubernetes on Google Compute
Engine.
**Create a cluster**
The recommended way is to use `Google Container
Engine <https://cloud.google.com/container-engine/>`__. You should be able
to create a fully fledged cluster with just a few clicks.
**Connect to it**
Install ``gcloud`` as a part of `Google Cloud SDK <https://cloud.google.com/sdk/>`__.
Make sure you have credentials for GCloud by running ``gcloud auth login``.
In order to make API calls against GCE, you must also run ``gcloud auth
application-default login``.
Press ``Connect``:
.. figure:: assets/gce1.png
and execute the first command in your shell. Then start a proxy by
executing ``kubectl` proxy``.
.. figure:: assets/gce2.png
Now you should be able to run ``kubectl`` command to create resources, get
resource info, logs, etc.
**Make sure you have Kubernetes >= 1.5, because you will be using
StatefulSets, which is a beta feature in 1.5.**
Create a configuration file
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Download a template:
::
curl -Lo app.yaml https://github.com/tendermint/tools/raw/master/mintnet-kubernetes/app.template.yaml
Open ``app.yaml`` in your favorite editor and configure your app
container (navigate to ``- name: app``). Kubernetes DSL (Domain Specific
Language) is very simple, so it should be easy. You will need to set
Docker image, command and/or run arguments. Replace variables prefixed
with ``YOUR_APP`` with corresponding values. Set genesis time to now and
preferable chain ID in ConfigMap.
Please note if you are changing ``replicas`` number, do not forget to
update ``validators`` set in ConfigMap. You will be able to scale the
cluster up or down later, but new pods (nodes) won't become validators
automatically.
Deploy your application
^^^^^^^^^^^^^^^^^^^^^^^
::
kubectl create -f ./app.yaml
Observe your cluster
^^^^^^^^^^^^^^^^^^^^
`web UI <https://github.com/kubernetes/dashboard>`__
The easiest way to access Dashboard is to use ``kubectl``. Run the following
command in your desktop environment:
::
kubectl proxy
``kubectl`` will handle authentication with apiserver and make Dashboard
available at http://localhost:8001/ui
**shell**
List all the pods:
::
kubectl get pods -o wide -L tm
StatefulSet details:
::
kubectl describe statefulsets tm
First pod details:
::
kubectl describe pod tm-0
Tendermint app logs from the first pod:
::
kubectl logs tm-0 -c tm -f
App logs from the first pod:
::
kubectl logs tm-0 -c app -f
Status of the second pod's Tendermint app:
::
kubectl exec -c tm tm-0 -- curl -s http://tm-1.<YOUR_APP_NAME>:26657/status | json_pp
Security
--------
Due to the nature of Kubernetes, where you typically have a single
master, the master could be a SPOF (Single Point Of Failure). Therefore,
you need to make sure only authorized people can access it. And these
people themselves had taken basic measures in order not to get hacked.
These are the best practices:
- all access to the master is over TLS
- access to the API Server is X.509 certificate or token based
- etcd is not exposed directly to the cluster
- ensure that images are free of vulnerabilities
(`1 <https://github.com/coreos/clair>`__)
- ensure that only authorized images are used in your environment
- disable direct access to Kubernetes nodes (no SSH)
- define resource quota
Resources:
- https://kubernetes.io/docs/admin/accessing-the-api/
- http://blog.kubernetes.io/2016/08/security-best-practices-kubernetes-deployment.html
- https://blog.openshift.com/securing-kubernetes/
Fault tolerance
---------------
Having a single master (API server) is a bad thing also because if
something happens to it, you risk being left without an access to the
application.
To avoid that you can `run Kubernetes in multiple
zones <https://kubernetes.io/docs/admin/multiple-zones/>`__, each zone
running an `API
server <https://kubernetes.io/docs/admin/high-availability/>`__ and load
balance requests between them. Do not forget to make sure only one
instance of scheduler and controller-manager are running at once.
Running in multiple zones is a lightweight version of a broader `Cluster
Federation feature <https://kubernetes.io/docs/admin/federation/>`__.
Federated deployments could span across multiple regions (not zones). We
haven't tried this feature yet, so any feedback is highly appreciated!
Especially, related to additional latency and cost of exchanging data
between the regions.
Resources:
- https://kubernetes.io/docs/admin/high-availability/
Starting process
----------------
.. figure:: assets/statefulset.png
:alt: StatefulSet
StatefulSet
Init containers (``tm-gen-validator``) are run before all other
containers, creating public-private key pair for each pod. Every ``tm``
container then asks other pods for their public keys, which are served
with nginx (``pub-key`` container). When ``tm`` container have all the
keys, it forms a genesis file and starts the Tendermint process.

+ 265
- 0
mintnet-kubernetes/app.template.yaml View File

@ -0,0 +1,265 @@
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
name: YOUR_APP_NAME
labels:
app: YOUR_APP_NAME
spec:
ports:
- port: 26656
name: p2p
- port: 26657
name: rpc
clusterIP: None
selector:
app: tm
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tm-config
data:
seeds: "tm-0,tm-1,tm-2,tm-3"
validators: "tm-0,tm-1,tm-2,tm-3"
validator.power: "10"
genesis.json: |-
{
"genesis_time": "2017-01-02T10:10:10.164Z",
"chain_id": "chain-B5XXm5",
"validators": [],
"app_hash": ""
}
pub_key_nginx.conf: |-
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
location /pub_key.json { root /usr/share/nginx/; }
}
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: tm-budget
spec:
selector:
matchLabels:
app: tm
minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: tm
spec:
serviceName: YOUR_APP_NAME
replicas: 4
template:
metadata:
labels:
app: tm
version: v1
annotations:
pod.beta.kubernetes.io/init-containers: '[{
"name": "tm-gen-validator",
"image": "tendermint/tendermint:0.10.0",
"imagePullPolicy": "IfNotPresent",
"command": ["bash", "-c", "
set -ex\n
if [ ! -f /tendermint/priv_validator.json ]; then\n
tendermint gen_validator > /tendermint/priv_validator.json\n
# pub_key.json will be served by pub-key container\n
cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n
fi\n
"],
"volumeMounts": [
{"name": "tmdir", "mountPath": "/tendermint"}
]
}]'
spec:
containers:
- name: tm
imagePullPolicy: IfNotPresent
image: tendermint/tendermint:0.10.0
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 100m
memory: 256Mi
ports:
- containerPort: 26656
name: p2p
- containerPort: 26657
name: rpc
env:
- name: SEEDS
valueFrom:
configMapKeyRef:
name: tm-config
key: seeds
- name: VALIDATOR_POWER
valueFrom:
configMapKeyRef:
name: tm-config
key: validator.power
- name: VALIDATORS
valueFrom:
configMapKeyRef:
name: tm-config
key: validators
- name: TMHOME
value: /tendermint
command:
- bash
- "-c"
- |
set -ex
# copy template
cp /etc/tendermint/genesis.json /tendermint/genesis.json
# fill genesis file with validators
IFS=',' read -ra VALS_ARR <<< "$VALIDATORS"
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#')
for v in "${VALS_ARR[@]}"; do
# wait until validator generates priv/pub key pair
set +e
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
while [ "$ERR" != 0 ]; do
sleep 5
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
done
set -e
# add validator to genesis file along with its pub_key
curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json
cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json
rm pub_validator.json
done
# construct seeds
IFS=',' read -ra SEEDS_ARR <<< "$SEEDS"
seeds=()
for s in "${SEEDS_ARR[@]}"; do
seeds+=("$s.$fqdn_suffix:26656")
done
seeds=$(IFS=','; echo "${seeds[*]}")
tendermint node --p2p.seeds="$seeds" --moniker="`hostname`" --proxy_app="unix:///socks/app.sock"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/tendermint/genesis.json
name: configdir
subPath: genesis.json
- name: socksdir
mountPath: /socks
- name: app
imagePullPolicy: IfNotPresent
image: YOUR_APP_IMAGE
args: ["--addr=\"unix:///socks/app.sock\""]
volumeMounts:
- name: socksdir
mountPath: /socks
######## OR ########
#
# - name: app
# imagePullPolicy: IfNotPresent
# image: golang:1.7.5
# resources:
# requests:
# cpu: YOUR_APP_CPU_REQ
# memory: YOUR_APP_MEM_REQ
# limits:
# cpu: YOUR_APP_CPU_LIMIT
# memory: YOUR_APP_MEM_LIMIT
# command:
# - bash
# - "-c"
# - |
# set -ex
# go get -d YOUR_APP_PACKAGE
# cd $GOPATH/YOUR_APP_PACKAGE
# make install
#
# rm -f /socks/app.sock # remove old socket
# YOUR_APP_EXEC --addr="unix:///socks/app.sock"
# volumeMounts:
# - name: socksdir
# mountPath: /socks
######## OPTIONALLY ########
#
# - name: data
# imagePullPolicy: IfNotPresent
# image: golang:1.7.5
# command:
# - bash
# - "-c"
# - |
# set -ex
# go get github.com/tendermint/merkleeyes/cmd/merkleeyes
# rm -f /socks/data.sock # remove old socket
# merkleeyes server --address="unix:///socks/data.sock"
# volumeMounts:
# - name: socksdir
# mountPath: /socks
- name: pub-key
imagePullPolicy: IfNotPresent
image: nginx:1.11.9
resources:
requests:
cpu: 10m
memory: 12Mi
limits:
cpu: 20m
memory: 24Mi
ports:
- containerPort: 80
name: pub-key
command:
- bash
- "-c"
- |
set -ex
# fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied))
# => we cannot serve from /tendermint, so we copy the file
mkdir -p /usr/share/nginx
cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json
nginx -g "daemon off;"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/nginx/conf.d/pub_key.conf
name: configdir
subPath: pub_key_nginx.conf
volumes:
- name: configdir
configMap:
name: tm-config
- name: socksdir
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: tmdir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi

BIN
mintnet-kubernetes/assets/gce1.png View File

Before After
Width: 844  |  Height: 94  |  Size: 13 KiB

BIN
mintnet-kubernetes/assets/gce2.png View File

Before After
Width: 501  |  Height: 366  |  Size: 30 KiB

BIN
mintnet-kubernetes/assets/statefulset.png View File

Before After
Width: 880  |  Height: 600  |  Size: 17 KiB

BIN
mintnet-kubernetes/assets/t_plus_k.png View File

Before After
Width: 250  |  Height: 100  |  Size: 18 KiB

+ 10
- 0
mintnet-kubernetes/examples/basecoin/Makefile View File

@ -0,0 +1,10 @@
create:
@echo "==> Creating deployment"
@kubectl create -f app.yaml
destroy:
@echo "==> Destroying deployment"
@kubectl delete -f app.yaml
@kubectl delete pvc -l app=tm
.PHONY: create destroy

+ 42
- 0
mintnet-kubernetes/examples/basecoin/README.md View File

@ -0,0 +1,42 @@
# Basecoin example
This is an example of using [basecoin](https://github.com/tendermint/basecoin).
## Usage
```
make create
```
### Check account balance and send a transaction
1. wait until all the pods are `Running`.
```
kubectl get pods -w -o wide -L tm
```
2. wait until app starts.
```
kubectl logs -c app -f tm-0
```
3. get account's address of the second pod
```
ADDR=`kubectl exec -c app tm-1 -- cat /app/key.json | jq ".address" | tr -d "\""`
```
4. send 5 coins to it from the first pod
```
kubectl exec -c app tm-0 -- basecoin tx send --to "0x$ADDR" --amount 5mycoin --from /app/key.json --chain_id chain-tTH4mi
```
## Clean up
```
make destroy
```

+ 334
- 0
mintnet-kubernetes/examples/basecoin/app.yaml View File

@ -0,0 +1,334 @@
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
name: basecoin
labels:
app: basecoin
spec:
ports:
- port: 26656
name: p2p
- port: 26657
name: rpc
clusterIP: None
selector:
app: tm
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tm-config
data:
seeds: "tm-0,tm-1,tm-2,tm-3"
validators: "tm-0,tm-1,tm-2,tm-3"
validator.power: "10"
genesis.json: |-
{
"genesis_time": "2016-02-05T06:02:31.526Z",
"chain_id": "chain-tTH4mi",
"validators": [],
"app_hash": ""
}
pub_key_nginx.conf: |-
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
location /pub_key.json { root /usr/share/nginx/; }
location /app_pub_key.json { root /usr/share/nginx/; }
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
genesis.json: |-
{
"chain_id": "chain-tTH4mi",
"app_options": {
"accounts": [
{
"pub_key": "tm-0",
"coins": [
{
"denom": "mycoin",
"amount": 1000000000
}
]
},
{
"pub_key": "tm-1",
"coins": [
{
"denom": "mycoin",
"amount": 1000000000
}
]
},
{
"pub_key": "tm-2",
"coins": [
{
"denom": "mycoin",
"amount": 1000000000
}
]
},
{
"pub_key": "tm-3",
"coins": [
{
"denom": "mycoin",
"amount": 1000000000
}
]
}
]
}
}
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: tm-budget
spec:
selector:
matchLabels:
app: tm
minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: tm
spec:
serviceName: basecoin
replicas: 4
template:
metadata:
labels:
app: tm
annotations:
pod.beta.kubernetes.io/init-containers: '[{
"name": "tm-gen-validator",
"image": "tendermint/tendermint:0.10.0",
"imagePullPolicy": "IfNotPresent",
"command": ["bash", "-c", "
set -ex\n
if [ ! -f /tendermint/priv_validator.json ]; then\n
tendermint gen_validator > /tendermint/priv_validator.json\n
# pub_key.json will be served by pub-key container\n
cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n
fi\n
"],
"volumeMounts": [
{"name": "tmdir", "mountPath": "/tendermint"}
]
},
{
"name": "app-gen-key",
"image": "tendermint/basecoin:0.5.1",
"imagePullPolicy": "IfNotPresent",
"command": ["bash", "-c", "
set -ex\n
if [ ! -f /app/key.json ]; then\n
basecoin key new > /app/key.json\n
# pub_key.json will be served by app-pub-key container\n
cat /app/key.json | jq \".pub_key\" > /app/pub_key.json\n
fi\n
"],
"volumeMounts": [
{"name": "appdir", "mountPath": "/app"}
]
}]'
spec:
containers:
- name: tm
imagePullPolicy: IfNotPresent
image: tendermint/tendermint:0.10.0
ports:
- containerPort: 26656
name: p2p
- containerPort: 26657
name: rpc
env:
- name: SEEDS
valueFrom:
configMapKeyRef:
name: tm-config
key: seeds
- name: VALIDATOR_POWER
valueFrom:
configMapKeyRef:
name: tm-config
key: validator.power
- name: VALIDATORS
valueFrom:
configMapKeyRef:
name: tm-config
key: validators
- name: TMHOME
value: /tendermint
command:
- bash
- "-c"
- |
set -ex
# copy template
cp /etc/tendermint/genesis.json /tendermint/genesis.json
# fill genesis file with validators
IFS=',' read -ra VALS_ARR <<< "$VALIDATORS"
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#')
for v in "${VALS_ARR[@]}"; do
# wait until validator generates priv/pub key pair
set +e
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
while [ "$ERR" != 0 ]; do
sleep 5
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
done
set -e
# add validator to genesis file along with its pub_key
curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json
cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json
rm pub_validator.json
done
# construct seeds
IFS=',' read -ra SEEDS_ARR <<< "$SEEDS"
seeds=()
for s in "${SEEDS_ARR[@]}"; do
seeds+=("$s.$fqdn_suffix:26656")
done
seeds=$(IFS=','; echo "${seeds[*]}")
tendermint node --p2p.seeds="$seeds" --moniker="`hostname`" --proxy_app="unix:///socks/app.sock"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/tendermint/genesis.json
name: tmconfigdir
subPath: genesis.json
- name: socksdir
mountPath: /socks
- name: app
imagePullPolicy: IfNotPresent
image: tendermint/basecoin:0.5.1
env:
- name: BCHOME
value: /app
workingDir: /app
command:
- bash
- "-c"
- |
set -ex
# replace "tm-N" with public keys in genesis file
cp /etc/app/genesis.json genesis.json
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#')
# for every "base/account"
i=0
length=$(cat genesis.json | jq ".app_options.accounts | length")
while [[ $i -lt $length ]]; do
# extract pod name ("tm-0")
pod=$(cat genesis.json | jq -r ".app_options.accounts[$i].pub_key")
# wait until pod starts to serve its pub_key
set +e
curl -s --fail "http://$pod.$fqdn_suffix/app_pub_key.json" > /dev/null
ERR=$?
while [ "$ERR" != 0 ]; do
sleep 5
curl -s --fail "http://$pod.$fqdn_suffix/app_pub_key.json" > /dev/null
ERR=$?
done
set -e
# get its pub_key
curl -s "http://$pod.$fqdn_suffix/app_pub_key.json" | jq "." > k.json
# replace pod name with it ("tm-0" => "{"type": ..., "data": ...}")
cat genesis.json | jq ".app_options.accounts[$i].pub_key = $(cat k.json | jq '.')" > tmpgenesis && mv tmpgenesis genesis.json
rm -f k.json
i=$((i+1))
done
rm -f /socks/app.sock # remove old socket
basecoin start --address="unix:///socks/app.sock" --without-tendermint
volumeMounts:
- name: appdir
mountPath: /app
- mountPath: /etc/app/genesis.json
name: appconfigdir
subPath: genesis.json
- name: socksdir
mountPath: /socks
- name: pub-key
imagePullPolicy: IfNotPresent
image: nginx:latest
ports:
- containerPort: 80
command:
- bash
- "-c"
- |
set -ex
# fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied))
# => we cannot serve from /tendermint, so we copy the file
mkdir -p /usr/share/nginx
cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json
cp /app/pub_key.json /usr/share/nginx/app_pub_key.json
nginx -g "daemon off;"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- name: appdir
mountPath: /app
- mountPath: /etc/nginx/conf.d/pub_key.conf
name: tmconfigdir
subPath: pub_key_nginx.conf
volumes:
- name: tmconfigdir
configMap:
name: tm-config
- name: appconfigdir
configMap:
name: app-config
- name: socksdir
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: tmdir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 2Gi
- metadata:
name: appdir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 12Mi

+ 100
- 0
mintnet-kubernetes/examples/basecoin/lightclient.md View File

@ -0,0 +1,100 @@
**OUTDATED**
# Using with lightclient
We have an awesome cluster running, let's try to test this out without
relying on executing commands on the cluster. Rather, we can connect to the
rpc interface with the `light-client` package and execute commands locally,
or even proxy our webapp to the kubernetes backend.
## Setup
In order to get this working, we need to know a few pieces of info,
the chain id of tendermint, the chain id of basecoin, and an account
with a bit of cash....
### Tendermint Chain ID
`kubectl exec -c tm tm-0 -- curl -s http://tm-1.basecoin:26657/status | json_pp | grep network`
set TM_CHAIN with the value there
### Basecoin Chain ID
`kubectl exec -c app tm-1 -- grep -A1 chainID /app/genesis.json`
set BC_CHAIN with the value there
### Expose tendermint rpc
We need to be able to reach the tendermint rpc interface from our shell.
`kubectl port-forward tm-0 26657:26657`
### Start basecoin-proxy
Using this info, let's connect our proxy and get going
`proxy-basecoin -tmchain=$TM_CHAIN -chain=$BC_CHAIN -rpc=localhost:26657`
## Basecoin accounts
Well, we can connect, but we don't have a registered account yet...
Let's look around, then use the cli to send some money from one of
the validators to our client's address so we can play.
**TODO** we can add some of our known accounts (from `/keys`) into
the genesis file, so we can skip all the kubectl money fiddling here.
We will want to start with money on some known non-validators.
### Getting validator info (kubectl)
The basecoin app deployment starts with 1000 "blank" coin in an account of
each validator. Let's get the address of the first validator
`kubectl exec -c app tm-1 -- grep address /app/key.json`
Store this info as VAL1_ADDR
### Querying state (proxy)
The proxy can read any public info via the tendermint rpc, so let's check
out this account.
`curl localhost:8108/query/account/$VAL1_ADDR`
Now, let's make out own account....
`curl -XPOST http://localhost:8108/keys/ -d '{"name": "k8demo", "passphrase": "1234567890"}'`
(or pick your own user and password). Remember the address you get here. You can
always find it out later by calling:
`curl http://localhost:8108/keys/k8demo`
and store it in DEMO_ADDR, which is empty at first
`curl localhost:8108/query/account/$DEMO_ADDR`
### "Stealing" validator cash (kubectl)
Run one command, that will be signed, now we have money
`kubectl exec -c app tm-0 -- basecoin tx send --to <k8demo-address> --amount 500`
### Using our money
Returning to our remote shell, we have a remote account with some money.
Let's see that.
`curl localhost:8108/query/account/$DEMO_ADDR`
Cool. Now we need to send it to a second account.
`curl -XPOST http://localhost:8108/keys/ -d '{"name": "buddy", "passphrase": "1234567890"}'`
and store the resulting address in BUDDY_ADDR
**TODO** finish this

+ 10
- 0
mintnet-kubernetes/examples/counter/Makefile View File

@ -0,0 +1,10 @@
create:
@echo "==> Creating deployment"
@kubectl create -f app.yaml
destroy:
@echo "==> Destroying deployment"
@kubectl delete -f app.yaml
@kubectl delete pvc -l app=tm
.PHONY: create destroy

+ 214
- 0
mintnet-kubernetes/examples/counter/app.yaml View File

@ -0,0 +1,214 @@
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
name: counter
labels:
app: counter
spec:
ports:
- port: 26656
name: p2p
- port: 26657
name: rpc
clusterIP: None
selector:
app: tm
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tm-config
data:
seeds: "tm-0,tm-1,tm-2,tm-3"
validators: "tm-0,tm-1,tm-2,tm-3"
validator.power: "10"
genesis.json: |-
{
"genesis_time": "2016-02-05T23:17:31.164Z",
"chain_id": "chain-B5XXm5",
"validators": [],
"app_hash": ""
}
pub_key_nginx.conf: |-
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
location /pub_key.json { root /usr/share/nginx/; }
}
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: tm-budget
spec:
selector:
matchLabels:
app: tm
minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: tm
spec:
serviceName: counter
replicas: 4
template:
metadata:
labels:
app: tm
annotations:
pod.beta.kubernetes.io/init-containers: '[{
"name": "tm-gen-validator",
"image": "tendermint/tendermint:0.10.0",
"imagePullPolicy": "IfNotPresent",
"command": ["bash", "-c", "
set -ex\n
if [ ! -f /tendermint/priv_validator.json ]; then\n
tendermint gen_validator > /tendermint/priv_validator.json\n
# pub_key.json will be served by pub-key container\n
cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n
fi\n
"],
"volumeMounts": [
{"name": "tmdir", "mountPath": "/tendermint"}
]
}]'
spec:
containers:
- name: tm
imagePullPolicy: IfNotPresent
image: tendermint/tendermint:0.10.0
ports:
- containerPort: 26656
name: p2p
- containerPort: 26657
name: rpc
env:
- name: SEEDS
valueFrom:
configMapKeyRef:
name: tm-config
key: seeds
- name: VALIDATOR_POWER
valueFrom:
configMapKeyRef:
name: tm-config
key: validator.power
- name: VALIDATORS
valueFrom:
configMapKeyRef:
name: tm-config
key: validators
- name: TMHOME
value: /tendermint
command:
- bash
- "-c"
- |
set -ex
# copy template
cp /etc/tendermint/genesis.json /tendermint/genesis.json
# fill genesis file with validators
IFS=',' read -ra VALS_ARR <<< "$VALIDATORS"
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#')
for v in "${VALS_ARR[@]}"; do
# wait until validator generates priv/pub key pair
set +e
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
while [ "$ERR" != 0 ]; do
sleep 5
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
done
set -e
# add validator to genesis file along with its pub_key
curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json
cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json
rm pub_validator.json
done
# construct seeds
IFS=',' read -ra SEEDS_ARR <<< "$SEEDS"
seeds=()
for s in "${SEEDS_ARR[@]}"; do
seeds+=("$s.$fqdn_suffix:26656")
done
seeds=$(IFS=','; echo "${seeds[*]}")
tendermint node --p2p.seeds="$seeds" --moniker="`hostname`" --proxy_app="unix:///socks/app.sock"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/tendermint/genesis.json
name: tmconfigdir
subPath: genesis.json
- name: socksdir
mountPath: /socks
- name: app
imagePullPolicy: IfNotPresent
image: golang:latest
command:
- bash
- "-c"
- |
set -ex
go get github.com/tendermint/abci/cmd/counter
rm -f /socks/app.sock # remove old socket
counter --serial --addr="unix:///socks/app.sock"
volumeMounts:
- name: socksdir
mountPath: /socks
- name: pub-key
imagePullPolicy: IfNotPresent
image: nginx:latest
ports:
- containerPort: 80
name: pub-key
command:
- bash
- "-c"
- |
set -ex
# fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied))
# => we cannot serve from /tendermint, so we copy the file
mkdir -p /usr/share/nginx
cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json
nginx -g "daemon off;"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/nginx/conf.d/pub_key.conf
name: tmconfigdir
subPath: pub_key_nginx.conf
volumes:
- name: tmconfigdir
configMap:
name: tm-config
- name: socksdir
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: tmdir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi

+ 17
- 0
mintnet-kubernetes/examples/dummy/Makefile View File

@ -0,0 +1,17 @@
create:
@echo "==> Creating deployment"
@kubectl create -f app.yaml
@echo "==> Waiting 10s until it is probably ready"
@sleep 10
@echo "==> Creating monitor and transacter pods"
@kubectl create -f tm-monitor-pod.yaml
@kubectl create -f transacter-pod.yaml
destroy:
@echo "==> Destroying deployment"
@kubectl delete -f transacter-pod.yaml
@kubectl delete -f tm-monitor-pod.yaml
@kubectl delete -f app.yaml
@kubectl delete pvc -l app=tm
.PHONY: create destroy

+ 196
- 0
mintnet-kubernetes/examples/dummy/app.yaml View File

@ -0,0 +1,196 @@
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
name: dummy
labels:
app: dummy
spec:
ports:
- port: 26656
name: p2p
- port: 26657
name: rpc
clusterIP: None
selector:
app: tm
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tm-config
data:
seeds: "tm-0,tm-1,tm-2,tm-3"
validators: "tm-0,tm-1,tm-2,tm-3"
validator.power: "10"
genesis.json: |-
{
"genesis_time": "2016-02-05T23:17:31.164Z",
"chain_id": "chain-B5XXm5",
"validators": [],
"app_hash": ""
}
pub_key_nginx.conf: |-
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
location /pub_key.json { root /usr/share/nginx/; }
}
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: tm-budget
spec:
selector:
matchLabels:
app: tm
minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: tm
spec:
serviceName: dummy
replicas: 4
template:
metadata:
labels:
app: tm
annotations:
pod.beta.kubernetes.io/init-containers: '[{
"name": "tm-gen-validator",
"image": "tendermint/tendermint:0.10.0",
"imagePullPolicy": "IfNotPresent",
"command": ["bash", "-c", "
set -ex\n
if [ ! -f /tendermint/priv_validator.json ]; then\n
tendermint gen_validator > /tendermint/priv_validator.json\n
# pub_key.json will be served by pub-key container\n
cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n
fi\n
"],
"volumeMounts": [
{"name": "tmdir", "mountPath": "/tendermint"}
]
}]'
spec:
containers:
- name: tm
imagePullPolicy: IfNotPresent
image: tendermint/tendermint:0.10.0
ports:
- containerPort: 26656
name: p2p
- containerPort: 26657
name: rpc
env:
- name: SEEDS
valueFrom:
configMapKeyRef:
name: tm-config
key: seeds
- name: VALIDATOR_POWER
valueFrom:
configMapKeyRef:
name: tm-config
key: validator.power
- name: VALIDATORS
valueFrom:
configMapKeyRef:
name: tm-config
key: validators
- name: TMHOME
value: /tendermint
command:
- bash
- "-c"
- |
set -ex
# copy template
cp /etc/tendermint/genesis.json /tendermint/genesis.json
# fill genesis file with validators
IFS=',' read -ra VALS_ARR <<< "$VALIDATORS"
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#')
for v in "${VALS_ARR[@]}"; do
# wait until validator generates priv/pub key pair
set +e
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
while [ "$ERR" != 0 ]; do
sleep 5
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null
ERR=$?
done
set -e
# add validator to genesis file along with its pub_key
curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json
cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json
rm pub_validator.json
done
# construct seeds
IFS=',' read -ra SEEDS_ARR <<< "$SEEDS"
seeds=()
for s in "${SEEDS_ARR[@]}"; do
seeds+=("$s.$fqdn_suffix:26656")
done
seeds=$(IFS=','; echo "${seeds[*]}")
tendermint node --p2p.seeds="$seeds" --moniker="`hostname`" --proxy_app="dummy"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/tendermint/genesis.json
name: tmconfigdir
subPath: genesis.json
- name: socksdir
mountPath: /socks
- name: pub-key
imagePullPolicy: IfNotPresent
image: nginx:latest
ports:
- containerPort: 80
name: pub-key
command:
- bash
- "-c"
- |
set -ex
# fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied))
# => we cannot serve from /tendermint, so we copy the file
mkdir -p /usr/share/nginx
cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json
nginx -g "daemon off;"
volumeMounts:
- name: tmdir
mountPath: /tendermint
- mountPath: /etc/nginx/conf.d/pub_key.conf
name: tmconfigdir
subPath: pub_key_nginx.conf
volumes:
- name: tmconfigdir
configMap:
name: tm-config
- name: socksdir
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: tmdir
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi

+ 13
- 0
mintnet-kubernetes/examples/dummy/tm-monitor-pod.yaml View File

@ -0,0 +1,13 @@
---
apiVersion: v1
kind: Pod
metadata:
name: monitor
spec:
containers:
- name: monitor
image: tendermint/monitor
args: ["-listen-addr=tcp://0.0.0.0:26670", "tm-0.dummy:26657,tm-1.dummy:26657,tm-2.dummy:26657,tm-3.dummy:26657"]
ports:
- containerPort: 26670
name: rpc

+ 19
- 0
mintnet-kubernetes/examples/dummy/transacter-pod.yaml View File

@ -0,0 +1,19 @@
---
apiVersion: v1
kind: Pod
metadata:
name: transacter
spec:
containers:
- name: transacter
image: tendermint/transacter
command:
- bash
- "-c"
- |
set -ex
while true
do
./transact 100 "tm-0.dummy:26657"
sleep 1
done

+ 6
- 0
tm-bench/Dockerfile View File

@ -0,0 +1,6 @@
FROM alpine:3.7
WORKDIR /app
COPY tm-bench /app/tm-bench
ENTRYPOINT ["./tm-bench"]

+ 12
- 0
tm-bench/Dockerfile.dev View File

@ -0,0 +1,12 @@
FROM golang:latest
RUN mkdir -p /go/src/github.com/tendermint/tools/tm-bench
WORKDIR /go/src/github.com/tendermint/tools/tm-bench
COPY Makefile /go/src/github.com/tendermint/tools/tm-bench/
RUN make get_tools
COPY . /go/src/github.com/tendermint/tools/tm-bench
RUN make get_vendor_deps

+ 337
- 0
tm-bench/Gopkg.lock View File

@ -0,0 +1,337 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/beorn7/perks"
packages = ["quantile"]
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "675abc5df3c5531bc741b56a765e35623459da6d"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/ebuchman/fail-test"
packages = ["."]
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]]
name = "github.com/go-kit/kit"
packages = [
"log",
"log/level",
"log/term",
"metrics",
"metrics/discard",
"metrics/internal/lv",
"metrics/prometheus"
]
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
name = "github.com/go-logfmt/logfmt"
packages = ["."]
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
name = "github.com/go-stack/stack"
packages = ["."]
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
"jsonpb",
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types"
]
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
version = "v1.0.0"
[[projects]]
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
]
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/jmhodges/levigo"
packages = ["."]
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]]
branch = "master"
name = "github.com/kr/logfmt"
packages = ["."]
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/promhttp"
]
revision = "c5b7fccd204277076155f10851dad72b76a49317"
version = "v0.8.0"
[[projects]]
branch = "master"
name = "github.com/prometheus/client_model"
packages = ["go"]
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
[[projects]]
branch = "master"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model"
]
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
[[projects]]
branch = "master"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs"
]
revision = "7d6f385de8bea29190f15ba9931442a0eaef9af7"
[[projects]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
branch = "master"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
"leveldb/cache",
"leveldb/comparer",
"leveldb/errors",
"leveldb/filter",
"leveldb/iterator",
"leveldb/journal",
"leveldb/memdb",
"leveldb/opt",
"leveldb/storage",
"leveldb/table",
"leveldb/util"
]
revision = "ae970a0732be3a1f5311da86118d37b9f4bd2a5a"
[[projects]]
branch = "master"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519"
]
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1"
[[projects]]
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
"abci/example/code",
"abci/example/kvstore",
"abci/types",
"blockchain",
"config",
"consensus",
"consensus/types",
"crypto",
"crypto/tmhash",
"evidence",
"libs/events",
"libs/pubsub",
"libs/pubsub/query",
"mempool",
"node",
"p2p",
"p2p/conn",
"p2p/pex",
"p2p/upnp",
"privval",
"proxy",
"rpc/client",
"rpc/core",
"rpc/core/types",
"rpc/grpc",
"rpc/lib",
"rpc/lib/client",
"rpc/lib/server",
"rpc/lib/types",
"state",
"state/txindex",
"state/txindex/kv",
"state/txindex/null",
"types",
"version"
]
revision = "aa20c45ae99bb57a8efa2be7b09898e8967d1965"
version = "v0.21.1-rc1"
[[projects]]
branch = "develop"
name = "github.com/tendermint/tmlibs"
packages = [
"autofile",
"clist",
"common",
"db",
"flowrate",
"log",
"merkle",
"merkle/tmhash"
]
revision = "56f44670ebd5a4e99e55f3f7d6f4d360ef5f1973"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"curve25519",
"nacl/box",
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"poly1305",
"ripemd160",
"salsa20/salsa"
]
revision = "1f94bef427e370e425e55b172f3b5e300ef38304"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"lex/httplex",
"netutil",
"trace"
]
revision = "640f4622ab692b87c2f3a94265e6f579fe38263d"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "86e600f69ee4704c6efbf6a2a40a5c10700e76c2"
[[projects]]
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"codes",
"connectivity",
"credentials",
"grpclb/grpc_lb_v1/messages",
"grpclog",
"internal",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"stats",
"status",
"tap",
"transport"
]
revision = "5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e"
version = "v1.7.5"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "65c380641dcebca21a6343c9ea36bab5cbebff696e27c74d0735077f63272248"
solver-name = "gps-cdcl"
solver-version = 1

+ 54
- 0
tm-bench/Gopkg.toml View File

@ -0,0 +1,54 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/go-kit/kit"
version = "^0.6.0"
[[constraint]]
name = "github.com/gorilla/websocket"
version = "^1.2.0"
[[constraint]]
name = "github.com/pkg/errors"
version = "^0.8.0"
[[constraint]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
[[constraint]]
name = "github.com/tendermint/tendermint"
branch = "develop"
[[constraint]]
name = "github.com/tendermint/tmlibs"
branch = "develop"
[prune]
go-tests = true
unused-packages = true

+ 204
- 0
tm-bench/LICENSE View File

@ -0,0 +1,204 @@
Tendermint Bench
Copyright 2017 Tendermint
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 116
- 0
tm-bench/Makefile View File

@ -0,0 +1,116 @@
DIST_DIRS := find * -type d -exec
VERSION := $(shell perl -ne '/^var version.*"([^"]+)".*$$/ && print "v$$1\n"' main.go)
GOTOOLS = \
github.com/mitchellh/gox \
github.com/golang/dep/cmd/dep \
gopkg.in/alecthomas/gometalinter.v2
all: check get_vendor_deps build test install metalinter
check: check_tools
########################################
### 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
########################################
### Build
build:
@go build
install:
@go install
test:
@go test -race
build-all: check_tools
rm -rf ./dist
gox -verbose \
-ldflags "-s -w" \
-arch="amd64 386 arm arm64" \
-os="linux darwin windows freebsd" \
-osarch="!darwin/arm !darwin/arm64" \
-output="dist/{{.OS}}-{{.Arch}}/{{.Dir}}" .
dist: build-all
cd dist && \
$(DIST_DIRS) cp ../LICENSE {} \; && \
$(DIST_DIRS) cp ../README.rst {} \; && \
$(DIST_DIRS) tar -zcf tm-bench-${VERSION}-{}.tar.gz {} \; && \
shasum -a256 ./*.tar.gz > "./tm-bench_${VERSION}_SHA256SUMS" && \
cd ..
########################################
### Docker
build-docker:
rm -f ./tm-bench
docker run -it --rm -v "$(PWD):/go/src/app" -w "/go/src/app" -e "CGO_ENABLED=0" golang:alpine go build -ldflags "-s -w" -o tm-bench
docker build -t "tendermint/bench" .
clean:
rm -f ./tm-bench
rm -rf ./dist
########################################
### 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:
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 check_tools get_tools update_tools get_vendor_deps build install test build-all dist fmt metalinter metalinter_all build-docker clean

+ 69
- 0
tm-bench/README.md View File

@ -0,0 +1,69 @@
# 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:26657
will output:
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)
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
tm-bench localhost:26657
with the last command being in a seperate window.
## Usage
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)
-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

+ 18
- 0
tm-bench/bench_test.go View File

@ -0,0 +1,18 @@
package main
import (
"testing"
"time"
)
func BenchmarkTimingPerTx(b *testing.B) {
startTime := time.Now()
endTime := startTime.Add(time.Second)
for i := 0; i < b.N; i++ {
if i%20 == 0 {
if time.Now().After(endTime) {
continue
}
}
}
}

+ 300
- 0
tm-bench/main.go View File

@ -0,0 +1,300 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"math"
"os"
"strings"
"text/tabwriter"
"time"
"github.com/go-kit/kit/log/term"
metrics "github.com/rcrowley/go-metrics"
tmrpc "github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tmlibs/log"
)
var version = "0.3.0"
var logger = log.NewNopLogger()
type statistics struct {
TxsThroughput metrics.Histogram `json:"txs_per_sec"`
BlocksThroughput metrics.Histogram `json:"blocks_per_sec"`
}
func main() {
var duration, txsRate, connections, txSize int
var verbose bool
var outputFormat, broadcastTxMethod string
flag.IntVar(&connections, "c", 1, "Connections to keep open per endpoint")
flag.IntVar(&duration, "T", 10, "Exit after the specified amount of time in seconds")
flag.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection")
flag.IntVar(&txSize, "s", 250, "The size of a transaction in bytes.")
flag.StringVar(&outputFormat, "output-format", "plain", "Output format: plain or json")
flag.StringVar(&broadcastTxMethod, "broadcast-tx-method", "async", "Broadcast method: async (no guarantees; fastest), sync (ensures tx is checked) or commit (ensures tx is checked and committed; slowest)")
flag.BoolVar(&verbose, "v", false, "Verbose output")
flag.Usage = func() {
fmt.Println(`Tendermint blockchain benchmarking tool.
Usage:
tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] [-output-format <plain|json> [-broadcast-tx-method <async|sync|commit>]]
Examples:
tm-bench localhost:26657`)
fmt.Println("Flags:")
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(1)
}
if verbose {
if outputFormat == "json" {
fmt.Fprintln(os.Stderr, "Verbose mode not supported with json output.")
os.Exit(1)
}
// Color errors red
colorFn := func(keyvals ...interface{}) term.FgBgColor {
for i := 1; i < len(keyvals); i += 2 {
if _, ok := keyvals[i].(error); ok {
return term.FgBgColor{Fg: term.White, Bg: term.Red}
}
}
return term.FgBgColor{}
}
logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
fmt.Printf("Running %ds test @ %s\n", duration, flag.Arg(0))
}
if broadcastTxMethod != "async" &&
broadcastTxMethod != "sync" &&
broadcastTxMethod != "commit" {
fmt.Fprintln(
os.Stderr,
"broadcast-tx-method should be either 'sync', 'async' or 'commit'.",
)
os.Exit(1)
}
var (
endpoints = strings.Split(flag.Arg(0), ",")
client = tmrpc.NewHTTP(endpoints[0], "/websocket")
initialHeight = latestBlockHeight(client)
)
logger.Info("Latest block height", "h", initialHeight)
// record time start
timeStart := time.Now()
logger.Info("Time started", "t", timeStart)
transacters := startTransacters(
endpoints,
connections,
txsRate,
txSize,
"broadcast_tx_"+broadcastTxMethod,
)
endTime := time.Duration(duration) * time.Second
select {
case <-time.After(endTime):
for i, t := range transacters {
t.Stop()
numCrashes := countCrashes(t.connsBroken)
if numCrashes != 0 {
fmt.Printf("%d connections crashed on transacter #%d\n", numCrashes, i)
}
}
timeStop := time.Now()
logger.Info("Time stopped", "t", timeStop)
stats, err := calculateStatistics(
client,
initialHeight,
timeStart,
timeStop,
duration,
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
printStatistics(stats, outputFormat)
return
}
}
func latestBlockHeight(client tmrpc.Client) int64 {
status, err := client.Status()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return status.SyncInfo.LatestBlockHeight
}
func countCrashes(crashes []bool) int {
count := 0
for i := 0; i < len(crashes); i++ {
if crashes[i] {
count++
}
}
return count
}
// calculateStatistics calculates the tx / second, and blocks / second based
// off of the number the transactions and number of blocks that occurred from
// the start block, and the end time.
func calculateStatistics(
client tmrpc.Client,
minHeight int64,
timeStart, timeStop time.Time,
duration int,
) (*statistics, error) {
stats := &statistics{
BlocksThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
TxsThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
}
// get blocks between minHeight and last height
// This returns max(minHeight,(last_height - 20)) to last_height
info, err := client.BlockchainInfo(minHeight, 0)
if err != nil {
return nil, err
}
var (
blockMetas = info.BlockMetas
lastHeight = info.LastHeight
diff = lastHeight - minHeight
offset = len(blockMetas)
)
for offset < int(diff) {
// get blocks between minHeight and last height
info, err := client.BlockchainInfo(minHeight, lastHeight-int64(offset))
if err != nil {
return nil, err
}
blockMetas = append(blockMetas, info.BlockMetas...)
offset = len(blockMetas)
}
var (
numBlocksPerSec = make(map[int64]int64)
numTxsPerSec = make(map[int64]int64)
)
// because during some seconds blocks won't be created...
for i := int64(0); i < int64(duration); i++ {
numBlocksPerSec[i] = 0
numTxsPerSec[i] = 0
}
// iterates from max height to min height
for _, blockMeta := range blockMetas {
// check if block was created after timeStart
if blockMeta.Header.Time.Before(timeStart) {
break
}
// check if block was created before timeStop
if blockMeta.Header.Time.After(timeStop) {
continue
}
sec := secondsSinceTimeStart(timeStart, blockMeta.Header.Time)
// increase number of blocks for that second
numBlocksPerSec[sec]++
// increase number of txs for that second
numTxsPerSec[sec] += blockMeta.Header.NumTxs
}
for _, n := range numBlocksPerSec {
stats.BlocksThroughput.Update(n)
}
for _, n := range numTxsPerSec {
stats.TxsThroughput.Update(n)
}
return stats, nil
}
func secondsSinceTimeStart(timeStart, timePassed time.Time) int64 {
return int64(math.Round(timePassed.Sub(timeStart).Seconds()))
}
func startTransacters(
endpoints []string,
connections,
txsRate int,
txSize int,
broadcastTxMethod string,
) []*transacter {
transacters := make([]*transacter, len(endpoints))
for i, e := range endpoints {
t := newTransacter(e, connections, txsRate, txSize, broadcastTxMethod)
t.SetLogger(logger)
if err := t.Start(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
transacters[i] = t
}
return transacters
}
func printStatistics(stats *statistics, outputFormat string) {
if outputFormat == "json" {
result, err := json.Marshal(struct {
TxsThroughput float64 `json:"txs_per_sec_avg"`
BlocksThroughput float64 `json:"blocks_per_sec_avg"`
}{stats.TxsThroughput.Mean(), stats.BlocksThroughput.Mean()})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(string(result))
} else {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
fmt.Fprintln(w, "Stats\tAvg\tStdDev\tMax\tTotal\t")
fmt.Fprintln(
w,
fmt.Sprintf(
"Txs/sec\t%.0f\t%.0f\t%d\t%d\t",
stats.TxsThroughput.Mean(),
stats.TxsThroughput.StdDev(),
stats.TxsThroughput.Max(),
stats.TxsThroughput.Sum(),
),
)
fmt.Fprintln(
w,
fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t%d\t",
stats.BlocksThroughput.Mean(),
stats.BlocksThroughput.StdDev(),
stats.BlocksThroughput.Max(),
stats.BlocksThroughput.Sum(),
),
)
w.Flush()
}
}

+ 252
- 0
tm-bench/transacter.go View File

@ -0,0 +1,252 @@
package main
import (
"crypto/md5"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
"github.com/tendermint/tmlibs/log"
)
const (
sendTimeout = 10 * time.Second
// see https://github.com/tendermint/go-rpc/blob/develop/server/handlers.go#L313
pingPeriod = (30 * 9 / 10) * time.Second
)
type transacter struct {
Target string
Rate int
Size int
Connections int
BroadcastTxMethod string
conns []*websocket.Conn
connsBroken []bool
wg sync.WaitGroup
stopped bool
logger log.Logger
}
func newTransacter(target string, connections, rate int, size int, broadcastTxMethod string) *transacter {
return &transacter{
Target: target,
Rate: rate,
Size: size,
Connections: connections,
BroadcastTxMethod: broadcastTxMethod,
conns: make([]*websocket.Conn, connections),
connsBroken: make([]bool, connections),
logger: log.NewNopLogger(),
}
}
// SetLogger lets you set your own logger
func (t *transacter) SetLogger(l log.Logger) {
t.logger = l
}
// Start opens N = `t.Connections` connections to the target and creates read
// and write goroutines for each connection.
func (t *transacter) Start() error {
t.stopped = false
rand.Seed(time.Now().Unix())
for i := 0; i < t.Connections; i++ {
c, _, err := connect(t.Target)
if err != nil {
return err
}
t.conns[i] = c
}
t.wg.Add(2 * t.Connections)
for i := 0; i < t.Connections; i++ {
go t.sendLoop(i)
go t.receiveLoop(i)
}
return nil
}
// Stop closes the connections.
func (t *transacter) Stop() {
t.stopped = true
t.wg.Wait()
for _, c := range t.conns {
c.Close()
}
}
// receiveLoop reads messages from the connection (empty in case of
// `broadcast_tx_async`).
func (t *transacter) receiveLoop(connIndex int) {
c := t.conns[connIndex]
defer t.wg.Done()
for {
_, _, err := c.ReadMessage()
if err != nil {
if !websocket.IsCloseError(err, websocket.CloseNormalClosure) {
t.logger.Error(
fmt.Sprintf("failed to read response on conn %d", connIndex),
"err",
err,
)
}
return
}
if t.stopped || t.connsBroken[connIndex] {
return
}
}
}
// sendLoop generates transactions at a given rate.
func (t *transacter) sendLoop(connIndex int) {
c := t.conns[connIndex]
c.SetPingHandler(func(message string) error {
err := c.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(sendTimeout))
if err == websocket.ErrCloseSent {
return nil
} else if e, ok := err.(net.Error); ok && e.Temporary() {
return nil
}
return err
})
logger := t.logger.With("addr", c.RemoteAddr())
var txNumber = 0
pingsTicker := time.NewTicker(pingPeriod)
txsTicker := time.NewTicker(1 * time.Second)
defer func() {
pingsTicker.Stop()
txsTicker.Stop()
t.wg.Done()
}()
// hash of the host name is a part of each tx
var hostnameHash [md5.Size]byte
hostname, err := os.Hostname()
if err != nil {
hostname = "127.0.0.1"
}
hostnameHash = md5.Sum([]byte(hostname))
for {
select {
case <-txsTicker.C:
startTime := time.Now()
endTime := startTime.Add(time.Second)
numTxSent := t.Rate
for i := 0; i < t.Rate; i++ {
// each transaction embeds connection index, tx number and hash of the hostname
tx := generateTx(connIndex, txNumber, t.Size, hostnameHash)
paramsJSON, err := json.Marshal(map[string]interface{}{"tx": hex.EncodeToString(tx)})
if err != nil {
fmt.Printf("failed to encode params: %v\n", err)
os.Exit(1)
}
rawParamsJSON := json.RawMessage(paramsJSON)
c.SetWriteDeadline(time.Now().Add(sendTimeout))
err = c.WriteJSON(rpctypes.RPCRequest{
JSONRPC: "2.0",
ID: "tm-bench",
Method: t.BroadcastTxMethod,
Params: rawParamsJSON,
})
if err != nil {
err = errors.Wrap(err,
fmt.Sprintf("txs send failed on connection #%d", connIndex))
t.connsBroken[connIndex] = true
logger.Error(err.Error())
return
}
// Time added here is 7.13 ns/op, not significant enough to worry about
if i%20 == 0 {
if time.Now().After(endTime) {
// Plus one accounts for sending this tx
numTxSent = i + 1
break
}
}
txNumber++
}
timeToSend := time.Now().Sub(startTime)
logger.Info(fmt.Sprintf("sent %d transactions", numTxSent), "took", timeToSend)
if timeToSend < 1*time.Second {
sleepTime := time.Second - timeToSend
logger.Debug(fmt.Sprintf("connection #%d is sleeping for %f seconds", connIndex, sleepTime.Seconds()))
time.Sleep(sleepTime)
}
case <-pingsTicker.C:
// go-rpc server closes the connection in the absence of pings
c.SetWriteDeadline(time.Now().Add(sendTimeout))
if err := c.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
err = errors.Wrap(err,
fmt.Sprintf("failed to write ping message on conn #%d", connIndex))
logger.Error(err.Error())
t.connsBroken[connIndex] = true
}
}
if t.stopped {
// To cleanly close a connection, a client should send a close
// frame and wait for the server to close the connection.
c.SetWriteDeadline(time.Now().Add(sendTimeout))
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
err = errors.Wrap(err,
fmt.Sprintf("failed to write close message on conn #%d", connIndex))
logger.Error(err.Error())
t.connsBroken[connIndex] = true
}
return
}
}
}
func connect(host string) (*websocket.Conn, *http.Response, error) {
u := url.URL{Scheme: "ws", Host: host, Path: "/websocket"}
return websocket.DefaultDialer.Dial(u.String(), nil)
}
func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [md5.Size]byte) []byte {
tx := make([]byte, txSize)
binary.PutUvarint(tx[:8], uint64(connIndex))
binary.PutUvarint(tx[8:16], uint64(txNumber))
copy(tx[16:32], hostnameHash[:16])
binary.PutUvarint(tx[32:40], uint64(time.Now().Unix()))
// 40-* random data
if _, err := rand.Read(tx[40:]); err != nil {
panic(errors.Wrap(err, "failed to read random bytes"))
}
return tx
}

+ 6
- 0
tm-monitor/Dockerfile View File

@ -0,0 +1,6 @@
FROM alpine:3.6
WORKDIR /app
COPY tm-monitor /app/tm-monitor
ENTRYPOINT ["./tm-monitor"]

+ 12
- 0
tm-monitor/Dockerfile.dev View File

@ -0,0 +1,12 @@
FROM golang:latest
RUN mkdir -p /go/src/github.com/tendermint/tools/tm-monitor
WORKDIR /go/src/github.com/tendermint/tools/tm-monitor
COPY Makefile /go/src/github.com/tendermint/tools/tm-monitor/
RUN make get_tools
COPY . /go/src/github.com/tendermint/tools/tm-monitor
RUN make get_vendor_deps

+ 291
- 0
tm-monitor/Gopkg.lock View File

@ -0,0 +1,291 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/ebuchman/fail-test"
packages = ["."]
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]]
name = "github.com/go-kit/kit"
packages = [
"log",
"log/level",
"log/term"
]
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
name = "github.com/go-logfmt/logfmt"
packages = ["."]
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
name = "github.com/go-stack/stack"
packages = ["."]
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
"jsonpb",
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types"
]
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
version = "v1.0.0"
[[projects]]
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
]
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/jmhodges/levigo"
packages = ["."]
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]]
branch = "master"
name = "github.com/kr/logfmt"
packages = ["."]
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
[[projects]]
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
"leveldb/cache",
"leveldb/comparer",
"leveldb/errors",
"leveldb/filter",
"leveldb/iterator",
"leveldb/journal",
"leveldb/memdb",
"leveldb/opt",
"leveldb/storage",
"leveldb/table",
"leveldb/util"
]
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
[[projects]]
name = "github.com/tendermint/abci"
packages = [
"client",
"example/code",
"example/kvstore",
"types"
]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
[[projects]]
branch = "master"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519"
]
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "42246108ff925a457fb709475070a03dfd3e2b5c"
version = "0.9.6"
[[projects]]
name = "github.com/tendermint/go-crypto"
packages = ["."]
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/tendermint"
packages = [
"config",
"p2p",
"p2p/conn",
"p2p/upnp",
"proxy",
"rpc/core/types",
"rpc/lib/client",
"rpc/lib/server",
"rpc/lib/types",
"state",
"types"
]
revision = "d0beaba7e8a5652506a34b5fab299cc2dc274c02"
version = "v0.19.0"
[[projects]]
name = "github.com/tendermint/tmlibs"
packages = [
"common",
"db",
"events",
"flowrate",
"log",
"merkle",
"pubsub",
"pubsub/query"
]
revision = "737154202faf75c70437f59ba5303f2eb09f5636"
version = "0.8.2-rc1"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"curve25519",
"nacl/box",
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"poly1305",
"ripemd160",
"salsa20/salsa"
]
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"lex/httplex",
"trace"
]
revision = "8d16fa6dc9a85c1cd3ed24ad08ff21cf94f10888"
[[projects]]
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[[projects]]
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"codes",
"connectivity",
"credentials",
"grpclb/grpc_lb_v1/messages",
"grpclog",
"internal",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"stats",
"status",
"tap",
"transport"
]
revision = "5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e"
version = "v1.7.5"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c445a659655eae3fbc1355e7d3431aa7f4b78f2f052ecba723cf8dcefd6350c4"
solver-name = "gps-cdcl"
solver-version = 1

+ 58
- 0
tm-monitor/Gopkg.toml View File

@ -0,0 +1,58 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"
[[constraint]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.1"
[[constraint]]
name = "github.com/tendermint/go-crypto"
version = "~0.6.2"
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "~0.9.6"
[[constraint]]
name = "github.com/tendermint/tendermint"
version = "0.19.0"
[[override]]
name = "github.com/tendermint/tmlibs"
version = "~0.8.2-rc1"
[prune]
go-tests = true
unused-packages = true

+ 204
- 0
tm-monitor/LICENSE View File

@ -0,0 +1,204 @@
Tendermint Monitor
Copyright 2017 Tendermint
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 116
- 0
tm-monitor/Makefile View File

@ -0,0 +1,116 @@
DIST_DIRS := find * -type d -exec
VERSION := $(shell perl -ne '/^var version.*"([^"]+)".*$$/ && print "v$$1\n"' main.go)
GOTOOLS = \
github.com/mitchellh/gox \
github.com/golang/dep/cmd/dep \
gopkg.in/alecthomas/gometalinter.v2
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
all: check get_vendor_deps build test install metalinter
check: check_tools
########################################
### 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
########################################
### Build
build:
@go build
install:
@go install
test:
@go test -race $(PACKAGES)
build-all: check_tools
rm -rf ./dist
gox -verbose \
-ldflags "-s -w" \
-arch="amd64 386 arm arm64" \
-os="linux darwin windows freebsd" \
-osarch="!darwin/arm !darwin/arm64" \
-output="dist/{{.OS}}-{{.Arch}}/{{.Dir}}" .
dist: build-all
cd dist && \
$(DIST_DIRS) cp ../LICENSE {} \; && \
$(DIST_DIRS) tar -zcf tm-monitor-${VERSION}-{}.tar.gz {} \; && \
shasum -a256 ./*.tar.gz > "./tm-monitor_${VERSION}_SHA256SUMS" && \
cd ..
########################################
### Docker
build-docker:
rm -f ./tm-monitor
docker run -it --rm -v "$(PWD):/go/src/github.com/tendermint/tools/tm-monitor" -w "/go/src/github.com/tendermint/tools/tm-monitor" -e "CGO_ENABLED=0" golang:alpine go build -ldflags "-s -w" -o tm-monitor
docker build -t "tendermint/monitor" .
clean:
rm -f ./tm-monitor
rm -rf ./dist
########################################
### 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:
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 check_tools get_tools update_tools get_vendor_deps build install test build-all dist fmt metalinter metalinter_all build-docker clean

+ 77
- 0
tm-monitor/README.md View File

@ -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 "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
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
### Using Binaries
[Install Tendermint](https://github.com/tendermint/tendermint#install)
then run:
tendermint init
tendermint node --proxy_app=kvstore
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]
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
### 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=_
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

+ 298
- 0
tm-monitor/eventmeter/eventmeter.go View File

@ -0,0 +1,298 @@
// eventmeter - generic system to subscribe to events and record their frequency.
package eventmeter
import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
metrics "github.com/rcrowley/go-metrics"
client "github.com/tendermint/tendermint/rpc/lib/client"
"github.com/tendermint/tmlibs/events"
"github.com/tendermint/tmlibs/log"
)
const (
// Get ping/pong latency and call LatencyCallbackFunc with this period.
latencyPeriod = 1 * time.Second
// Check if the WS client is connected every
connectionCheckPeriod = 100 * time.Millisecond
)
// EventMetric exposes metrics for an event.
type EventMetric struct {
ID string `json:"id"`
Started time.Time `json:"start_time"`
LastHeard time.Time `json:"last_heard"`
MinDuration int64 `json:"min_duration"`
MaxDuration int64 `json:"max_duration"`
// tracks event count and rate
meter metrics.Meter
// filled in from the Meter
Count int64 `json:"count"`
Rate1 float64 `json:"rate_1" amino:"unsafe"`
Rate5 float64 `json:"rate_5" amino:"unsafe"`
Rate15 float64 `json:"rate_15" amino:"unsafe"`
RateMean float64 `json:"rate_mean" amino:"unsafe"`
// so the event can have effects in the eventmeter's consumer. runs in a go
// routine.
callback EventCallbackFunc
}
func (metric *EventMetric) Copy() *EventMetric {
metricCopy := *metric
metricCopy.meter = metric.meter.Snapshot()
return &metricCopy
}
// called on GetMetric
func (metric *EventMetric) fillMetric() *EventMetric {
metric.Count = metric.meter.Count()
metric.Rate1 = metric.meter.Rate1()
metric.Rate5 = metric.meter.Rate5()
metric.Rate15 = metric.meter.Rate15()
metric.RateMean = metric.meter.RateMean()
return metric
}
// EventCallbackFunc is a closure to enable side effects from receiving an
// event.
type EventCallbackFunc func(em *EventMetric, data interface{})
// EventUnmarshalFunc is a closure to get the query and data out of the raw
// JSON received over the RPC WebSocket.
type EventUnmarshalFunc func(b json.RawMessage) (string, events.EventData, error)
// LatencyCallbackFunc is a closure to enable side effects from receiving a latency.
type LatencyCallbackFunc func(meanLatencyNanoSeconds float64)
// DisconnectCallbackFunc is a closure to notify a consumer that the connection
// has died.
type DisconnectCallbackFunc func()
// EventMeter tracks events, reports latency and disconnects.
type EventMeter struct {
wsc *client.WSClient
mtx sync.Mutex
queryToMetricMap map[string]*EventMetric
unmarshalEvent EventUnmarshalFunc
latencyCallback LatencyCallbackFunc
disconnectCallback DisconnectCallbackFunc
subscribed bool
quit chan struct{}
logger log.Logger
}
func NewEventMeter(addr string, unmarshalEvent EventUnmarshalFunc) *EventMeter {
return &EventMeter{
wsc: client.NewWSClient(addr, "/websocket", client.PingPeriod(1*time.Second)),
queryToMetricMap: make(map[string]*EventMetric),
unmarshalEvent: unmarshalEvent,
logger: log.NewNopLogger(),
}
}
// SetLogger lets you set your own logger.
func (em *EventMeter) SetLogger(l log.Logger) {
em.logger = l
em.wsc.SetLogger(l.With("module", "rpcclient"))
}
// String returns a string representation of event meter.
func (em *EventMeter) String() string {
return em.wsc.Address
}
// Start boots up event meter.
func (em *EventMeter) Start() error {
if err := em.wsc.Start(); err != nil {
return err
}
em.quit = make(chan struct{})
go em.receiveRoutine()
go em.disconnectRoutine()
err := em.subscribe()
if err != nil {
return err
}
em.subscribed = true
return nil
}
// Stop stops event meter.
func (em *EventMeter) Stop() {
close(em.quit)
if em.wsc.IsRunning() {
em.wsc.Stop()
}
}
// Subscribe for the given query. Callback function will be called upon
// receiving an event.
func (em *EventMeter) Subscribe(query string, cb EventCallbackFunc) error {
em.mtx.Lock()
defer em.mtx.Unlock()
if err := em.wsc.Subscribe(context.TODO(), query); err != nil {
return err
}
metric := &EventMetric{
meter: metrics.NewMeter(),
callback: cb,
}
em.queryToMetricMap[query] = metric
return nil
}
// Unsubscribe from the given query.
func (em *EventMeter) Unsubscribe(query string) error {
em.mtx.Lock()
defer em.mtx.Unlock()
if err := em.wsc.Unsubscribe(context.TODO(), query); err != nil {
return err
}
return nil
}
// GetMetric fills in the latest data for an query and return a copy.
func (em *EventMeter) GetMetric(query string) (*EventMetric, error) {
em.mtx.Lock()
defer em.mtx.Unlock()
metric, ok := em.queryToMetricMap[query]
if !ok {
return nil, fmt.Errorf("unknown query: %s", query)
}
return metric.fillMetric().Copy(), nil
}
// RegisterLatencyCallback allows you to set latency callback.
func (em *EventMeter) RegisterLatencyCallback(f LatencyCallbackFunc) {
em.mtx.Lock()
defer em.mtx.Unlock()
em.latencyCallback = f
}
// RegisterDisconnectCallback allows you to set disconnect callback.
func (em *EventMeter) RegisterDisconnectCallback(f DisconnectCallbackFunc) {
em.mtx.Lock()
defer em.mtx.Unlock()
em.disconnectCallback = f
}
///////////////////////////////////////////////////////////////////////////////
// Private
func (em *EventMeter) subscribe() error {
for query, _ := range em.queryToMetricMap {
if err := em.wsc.Subscribe(context.TODO(), query); err != nil {
return err
}
}
return nil
}
func (em *EventMeter) receiveRoutine() {
latencyTicker := time.NewTicker(latencyPeriod)
for {
select {
case resp := <-em.wsc.ResponsesCh:
if resp.Error != nil {
em.logger.Error("expected some event, got error", "err", resp.Error.Error())
continue
}
query, data, err := em.unmarshalEvent(resp.Result)
if err != nil {
em.logger.Error("failed to unmarshal event", "err", err)
continue
}
if query != "" { // FIXME how can it be an empty string?
em.updateMetric(query, data)
}
case <-latencyTicker.C:
if em.wsc.IsActive() {
em.callLatencyCallback(em.wsc.PingPongLatencyTimer.Mean())
}
case <-em.wsc.Quit():
return
case <-em.quit:
return
}
}
}
func (em *EventMeter) disconnectRoutine() {
ticker := time.NewTicker(connectionCheckPeriod)
for {
select {
case <-ticker.C:
if em.wsc.IsReconnecting() && em.subscribed { // notify user about disconnect only once
em.callDisconnectCallback()
em.subscribed = false
} else if !em.wsc.IsReconnecting() && !em.subscribed { // resubscribe
em.subscribe()
em.subscribed = true
}
case <-em.wsc.Quit():
return
case <-em.quit:
return
}
}
}
func (em *EventMeter) updateMetric(query string, data events.EventData) {
em.mtx.Lock()
defer em.mtx.Unlock()
metric, ok := em.queryToMetricMap[query]
if !ok {
// we already unsubscribed, or got an unexpected query
return
}
last := metric.LastHeard
metric.LastHeard = time.Now()
metric.meter.Mark(1)
dur := int64(metric.LastHeard.Sub(last))
if dur < metric.MinDuration {
metric.MinDuration = dur
}
if !last.IsZero() && dur > metric.MaxDuration {
metric.MaxDuration = dur
}
if metric.callback != nil {
go metric.callback(metric.Copy(), data)
}
}
func (em *EventMeter) callDisconnectCallback() {
em.mtx.Lock()
if em.disconnectCallback != nil {
go em.disconnectCallback()
}
em.mtx.Unlock()
}
func (em *EventMeter) callLatencyCallback(meanLatencyNanoSeconds float64) {
em.mtx.Lock()
if em.latencyCallback != nil {
go em.latencyCallback(meanLatencyNanoSeconds)
}
em.mtx.Unlock()
}

+ 88
- 0
tm-monitor/main.go View File

@ -0,0 +1,88 @@
package main
import (
"flag"
"fmt"
"os"
"strings"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
monitor "github.com/tendermint/tools/tm-monitor/monitor"
)
var version = "0.4.0"
var logger = log.NewNopLogger()
func main() {
var listenAddr string
var noton bool
flag.StringVar(&listenAddr, "listen-addr", "tcp://0.0.0.0:26670", "HTTP and Websocket server listen address")
flag.BoolVar(&noton, "no-ton", false, "Do not show ton (table of nodes)")
flag.Usage = func() {
fmt.Println(`Tendermint monitor watches over one or more Tendermint core
applications, collecting and providing various statistics to the user.
Usage:
tm-monitor [-no-ton] [-listen-addr="tcp://0.0.0.0:26670"] [endpoints]
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`)
fmt.Println("Flags:")
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(1)
}
if noton {
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
}
m := startMonitor(flag.Arg(0))
startRPC(listenAddr, m, logger)
var ton *Ton
if !noton {
ton = NewTon(m)
ton.Start()
}
cmn.TrapSignal(func() {
if !noton {
ton.Stop()
}
m.Stop()
})
}
func startMonitor(endpoints string) *monitor.Monitor {
m := monitor.NewMonitor()
m.SetLogger(logger.With("component", "monitor"))
for _, e := range strings.Split(endpoints, ",") {
n := monitor.NewNode(e)
n.SetLogger(logger.With("node", e))
if err := m.Monitor(n); err != nil {
panic(err)
}
}
if err := m.Start(); err != nil {
panic(err)
}
return m
}

+ 69
- 0
tm-monitor/mock/eventmeter.go View File

@ -0,0 +1,69 @@
package mock
import (
stdlog "log"
"reflect"
"github.com/tendermint/tmlibs/log"
em "github.com/tendermint/tools/tm-monitor/eventmeter"
"github.com/tendermint/go-amino"
)
type EventMeter struct {
latencyCallback em.LatencyCallbackFunc
disconnectCallback em.DisconnectCallbackFunc
eventCallback em.EventCallbackFunc
}
func (e *EventMeter) Start() error { return nil }
func (e *EventMeter) Stop() {}
func (e *EventMeter) SetLogger(l log.Logger) {}
func (e *EventMeter) RegisterLatencyCallback(cb em.LatencyCallbackFunc) { e.latencyCallback = cb }
func (e *EventMeter) RegisterDisconnectCallback(cb em.DisconnectCallbackFunc) {
e.disconnectCallback = cb
}
func (e *EventMeter) Subscribe(query string, cb em.EventCallbackFunc) error {
e.eventCallback = cb
return nil
}
func (e *EventMeter) Unsubscribe(query string) error {
e.eventCallback = nil
return nil
}
func (e *EventMeter) Call(callback string, args ...interface{}) {
switch callback {
case "latencyCallback":
e.latencyCallback(args[0].(float64))
case "disconnectCallback":
e.disconnectCallback()
case "eventCallback":
e.eventCallback(args[0].(*em.EventMetric), args[1])
}
}
type RpcClient struct {
Stubs map[string]interface{}
cdc *amino.Codec
}
func (c *RpcClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
s, ok := c.Stubs[method]
if !ok {
stdlog.Fatalf("Call to %s, but no stub is defined for it", method)
}
rv, rt := reflect.ValueOf(result), reflect.TypeOf(result)
rv, rt = rv.Elem(), rt.Elem()
rv.Set(reflect.ValueOf(s))
return s, nil
}
func (c *RpcClient) Codec() *amino.Codec {
return c.cdc
}
func (c *RpcClient) SetCodec(cdc *amino.Codec) {
c.cdc = cdc
}

+ 251
- 0
tm-monitor/monitor/monitor.go View File

@ -0,0 +1,251 @@
package monitor
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/pkg/errors"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
)
// waiting more than this many seconds for a block means we're unhealthy
const nodeLivenessTimeout = 5 * time.Second
// Monitor keeps track of the nodes and updates common statistics upon
// receiving new events from nodes.
//
// Common statistics is stored in Network struct.
type Monitor struct {
mtx sync.Mutex
Nodes []*Node
Network *Network
monitorQuit chan struct{} // monitor exitting
nodeQuit map[string]chan struct{} // node is being stopped and removed from under the monitor
recalculateNetworkUptimeEvery time.Duration
numValidatorsUpdateInterval time.Duration
logger log.Logger
}
// NewMonitor creates new instance of a Monitor. You can provide options to
// change some default values.
//
// Example:
// NewMonitor(monitor.SetNumValidatorsUpdateInterval(1 * time.Second))
func NewMonitor(options ...func(*Monitor)) *Monitor {
m := &Monitor{
Nodes: make([]*Node, 0),
Network: NewNetwork(),
monitorQuit: make(chan struct{}),
nodeQuit: make(map[string]chan struct{}),
recalculateNetworkUptimeEvery: 10 * time.Second,
numValidatorsUpdateInterval: 5 * time.Second,
logger: log.NewNopLogger(),
}
for _, option := range options {
option(m)
}
return m
}
// RecalculateNetworkUptimeEvery lets you change network uptime update interval.
func RecalculateNetworkUptimeEvery(d time.Duration) func(m *Monitor) {
return func(m *Monitor) {
m.recalculateNetworkUptimeEvery = d
}
}
// SetNumValidatorsUpdateInterval lets you change num validators update interval.
func SetNumValidatorsUpdateInterval(d time.Duration) func(m *Monitor) {
return func(m *Monitor) {
m.numValidatorsUpdateInterval = d
}
}
// SetLogger lets you set your own logger
func (m *Monitor) SetLogger(l log.Logger) {
m.logger = l
}
// Monitor begins to monitor the node `n`. The node will be started and added
// to the monitor.
func (m *Monitor) Monitor(n *Node) error {
m.mtx.Lock()
m.Nodes = append(m.Nodes, n)
m.mtx.Unlock()
blockCh := make(chan tmtypes.Header, 10)
n.SendBlocksTo(blockCh)
blockLatencyCh := make(chan float64, 10)
n.SendBlockLatenciesTo(blockLatencyCh)
disconnectCh := make(chan bool, 10)
n.NotifyAboutDisconnects(disconnectCh)
if err := n.Start(); err != nil {
return err
}
m.Network.NewNode(n.Name)
m.nodeQuit[n.Name] = make(chan struct{})
go m.listen(n.Name, blockCh, blockLatencyCh, disconnectCh, m.nodeQuit[n.Name])
return nil
}
// Unmonitor stops monitoring node `n`. The node will be stopped and removed
// from the monitor.
func (m *Monitor) Unmonitor(n *Node) {
m.Network.NodeDeleted(n.Name)
n.Stop()
close(m.nodeQuit[n.Name])
delete(m.nodeQuit, n.Name)
i, _ := m.NodeByName(n.Name)
m.mtx.Lock()
m.Nodes[i] = m.Nodes[len(m.Nodes)-1]
m.Nodes = m.Nodes[:len(m.Nodes)-1]
m.mtx.Unlock()
}
// NodeByName returns the node and its index if such node exists within the
// monitor. Otherwise, -1 and nil are returned.
func (m *Monitor) NodeByName(name string) (index int, node *Node) {
m.mtx.Lock()
defer m.mtx.Unlock()
for i, n := range m.Nodes {
if name == n.Name {
return i, n
}
}
return -1, nil
}
// NodeIsOnline is called when connection to the node is restored.
// Must be safe to call multiple times.
func (m *Monitor) NodeIsOnline(name string) {
_, node := m.NodeByName(name)
if nil != node {
if online, ok := m.Network.nodeStatusMap[name]; ok && online {
m.mtx.Lock()
node.Online = online
m.mtx.Unlock()
}
}
}
// Start starts the monitor's routines: recalculating network uptime and
// updating number of validators.
func (m *Monitor) Start() error {
go m.recalculateNetworkUptimeLoop()
go m.updateNumValidatorLoop()
return nil
}
// Stop stops the monitor's routines.
func (m *Monitor) Stop() {
close(m.monitorQuit)
for _, n := range m.Nodes {
m.Unmonitor(n)
}
}
// main loop where we listen for events from the node
func (m *Monitor) listen(nodeName string, blockCh <-chan tmtypes.Header, blockLatencyCh <-chan float64, disconnectCh <-chan bool, quit <-chan struct{}) {
logger := m.logger.With("node", nodeName)
for {
select {
case <-quit:
return
case b := <-blockCh:
m.Network.NewBlock(b)
m.Network.NodeIsOnline(nodeName)
m.NodeIsOnline(nodeName)
case l := <-blockLatencyCh:
m.Network.NewBlockLatency(l)
m.Network.NodeIsOnline(nodeName)
m.NodeIsOnline(nodeName)
case disconnected := <-disconnectCh:
if disconnected {
m.Network.NodeIsDown(nodeName)
} else {
m.Network.NodeIsOnline(nodeName)
m.NodeIsOnline(nodeName)
}
case <-time.After(nodeLivenessTimeout):
logger.Info("event", fmt.Sprintf("node was not responding for %v", nodeLivenessTimeout))
m.Network.NodeIsDown(nodeName)
}
}
}
// recalculateNetworkUptimeLoop every N seconds.
func (m *Monitor) recalculateNetworkUptimeLoop() {
for {
select {
case <-m.monitorQuit:
return
case <-time.After(m.recalculateNetworkUptimeEvery):
m.Network.RecalculateUptime()
}
}
}
// updateNumValidatorLoop sends a request to a random node once every N seconds,
// which in turn makes an RPC call to get the latest validators.
func (m *Monitor) updateNumValidatorLoop() {
rand.Seed(time.Now().Unix())
var height int64
var num int
var err error
for {
m.mtx.Lock()
nodesCount := len(m.Nodes)
m.mtx.Unlock()
if 0 == nodesCount {
time.Sleep(m.numValidatorsUpdateInterval)
continue
}
randomNodeIndex := rand.Intn(nodesCount)
select {
case <-m.monitorQuit:
return
case <-time.After(m.numValidatorsUpdateInterval):
i := 0
m.mtx.Lock()
for _, n := range m.Nodes {
if i == randomNodeIndex {
height, num, err = n.NumValidators()
if err != nil {
m.logger.Info("err", errors.Wrap(err, "update num validators failed"))
}
break
}
i++
}
m.mtx.Unlock()
m.Network.UpdateNumValidatorsForHeight(num, height)
}
}
}

+ 72
- 0
tm-monitor/monitor/monitor_test.go View File

@ -0,0 +1,72 @@
package monitor_test
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
tmtypes "github.com/tendermint/tendermint/types"
mock "github.com/tendermint/tools/tm-monitor/mock"
monitor "github.com/tendermint/tools/tm-monitor/monitor"
"github.com/tendermint/go-amino"
)
func TestMonitorUpdatesNumberOfValidators(t *testing.T) {
m := startMonitor(t)
defer m.Stop()
n, _ := createValidatorNode(t)
m.Monitor(n)
assert.Equal(t, 1, m.Network.NumNodesMonitored)
assert.Equal(t, 1, m.Network.NumNodesMonitoredOnline)
time.Sleep(1 * time.Second)
// DATA RACE
// assert.Equal(t, 1, m.Network.NumValidators())
}
func TestMonitorRecalculatesNetworkUptime(t *testing.T) {
m := startMonitor(t)
defer m.Stop()
assert.Equal(t, 100.0, m.Network.Uptime())
n, _ := createValidatorNode(t)
m.Monitor(n)
m.Network.NodeIsDown(n.Name) // simulate node failure
time.Sleep(200 * time.Millisecond)
m.Network.NodeIsOnline(n.Name)
time.Sleep(1 * time.Second)
assert.True(t, m.Network.Uptime() < 100.0, "Uptime should be less than 100%")
}
func startMonitor(t *testing.T) *monitor.Monitor {
m := monitor.NewMonitor(
monitor.SetNumValidatorsUpdateInterval(200*time.Millisecond),
monitor.RecalculateNetworkUptimeEvery(200*time.Millisecond),
)
err := m.Start()
require.Nil(t, err)
return m
}
func createValidatorNode(t *testing.T) (n *monitor.Node, emMock *mock.EventMeter) {
emMock = &mock.EventMeter{}
stubs := make(map[string]interface{})
pubKey := crypto.GenPrivKeyEd25519().PubKey()
stubs["validators"] = ctypes.ResultValidators{BlockHeight: blockHeight, Validators: []*tmtypes.Validator{tmtypes.NewValidator(pubKey, 0)}}
stubs["status"] = ctypes.ResultStatus{ValidatorInfo: ctypes.ValidatorInfo{PubKey: pubKey}}
cdc := amino.NewCodec()
rpcClientMock := &mock.RpcClient{Stubs: stubs}
rpcClientMock.SetCodec(cdc)
n = monitor.NewNodeWithEventMeterAndRpcClient("tcp://127.0.0.1:26657", emMock, rpcClientMock)
return
}

+ 199
- 0
tm-monitor/monitor/network.go View File

@ -0,0 +1,199 @@
package monitor
import (
"sync"
"time"
metrics "github.com/rcrowley/go-metrics"
tmtypes "github.com/tendermint/tendermint/types"
)
// UptimeData stores data for how long network has been running.
type UptimeData struct {
StartTime time.Time `json:"start_time"`
Uptime float64 `json:"uptime" amino:"unsafe"` // percentage of time we've been healthy, ever
totalDownTime time.Duration // total downtime (only updated when we come back online)
wentDown time.Time
}
// Health describes the health of the network. Note that this applies only to
// the observed nodes, and not to the entire cluster, which may consist of
// thousands of machines. It may change in the future.
type Health int
const (
// FullHealth means all nodes online, synced, validators making blocks
FullHealth = Health(0)
// ModerateHealth means we're making blocks
ModerateHealth = Health(1)
// Dead means we're not making blocks due to all validators freezing or crashing
Dead = Health(2)
)
// Common statistics for network of nodes
type Network struct {
Height int64 `json:"height"`
AvgBlockTime float64 `json:"avg_block_time" amino:"unsafe"` // ms (avg over last minute)
blockTimeMeter metrics.Meter
AvgTxThroughput float64 `json:"avg_tx_throughput" amino:"unsafe"` // tx/s (avg over last minute)
txThroughputMeter metrics.Meter
AvgBlockLatency float64 `json:"avg_block_latency" amino:"unsafe"` // ms (avg over last minute)
blockLatencyMeter metrics.Meter
NumValidators int `json:"num_validators"`
NumNodesMonitored int `json:"num_nodes_monitored"`
NumNodesMonitoredOnline int `json:"num_nodes_monitored_online"`
Health Health `json:"health"`
UptimeData *UptimeData `json:"uptime_data"`
nodeStatusMap map[string]bool
mu sync.Mutex
}
func NewNetwork() *Network {
return &Network{
blockTimeMeter: metrics.NewMeter(),
txThroughputMeter: metrics.NewMeter(),
blockLatencyMeter: metrics.NewMeter(),
Health: FullHealth,
UptimeData: &UptimeData{
StartTime: time.Now(),
Uptime: 100.0,
},
nodeStatusMap: make(map[string]bool),
}
}
func (n *Network) NewBlock(b tmtypes.Header) {
n.mu.Lock()
defer n.mu.Unlock()
if n.Height >= b.Height {
return
}
n.Height = b.Height
n.blockTimeMeter.Mark(1)
if n.blockTimeMeter.Rate1() > 0.0 {
n.AvgBlockTime = (1.0 / n.blockTimeMeter.Rate1()) * 1000 // 1/s to ms
} else {
n.AvgBlockTime = 0.0
}
n.txThroughputMeter.Mark(int64(b.NumTxs))
n.AvgTxThroughput = n.txThroughputMeter.Rate1()
}
func (n *Network) NewBlockLatency(l float64) {
n.mu.Lock()
defer n.mu.Unlock()
n.blockLatencyMeter.Mark(int64(l))
n.AvgBlockLatency = n.blockLatencyMeter.Rate1() / 1000000.0 // ns to ms
}
// RecalculateUptime calculates uptime on demand.
func (n *Network) RecalculateUptime() {
n.mu.Lock()
defer n.mu.Unlock()
since := time.Since(n.UptimeData.StartTime)
uptime := since - n.UptimeData.totalDownTime
if n.Health != FullHealth {
uptime -= time.Since(n.UptimeData.wentDown)
}
n.UptimeData.Uptime = (float64(uptime) / float64(since)) * 100.0
}
// NodeIsDown is called when the node disconnects for whatever reason.
// Must be safe to call multiple times.
func (n *Network) NodeIsDown(name string) {
n.mu.Lock()
defer n.mu.Unlock()
if online, ok := n.nodeStatusMap[name]; !ok || online {
n.nodeStatusMap[name] = false
n.NumNodesMonitoredOnline--
n.UptimeData.wentDown = time.Now()
n.updateHealth()
}
}
// NodeIsOnline is called when connection to the node is restored.
// Must be safe to call multiple times.
func (n *Network) NodeIsOnline(name string) {
n.mu.Lock()
defer n.mu.Unlock()
if online, ok := n.nodeStatusMap[name]; ok && !online {
n.nodeStatusMap[name] = true
n.NumNodesMonitoredOnline++
n.UptimeData.totalDownTime += time.Since(n.UptimeData.wentDown)
n.updateHealth()
}
}
// NewNode is called when the new node is added to the monitor.
func (n *Network) NewNode(name string) {
n.NumNodesMonitored++
n.NumNodesMonitoredOnline++
}
// NodeDeleted is called when the node is deleted from under the monitor.
func (n *Network) NodeDeleted(name string) {
n.NumNodesMonitored--
n.NumNodesMonitoredOnline--
}
func (n *Network) updateHealth() {
// if we are connected to all validators, we're at full health
// TODO: make sure they're all at the same height (within a block)
// and all proposing (and possibly validating ) Alternatively, just
// check there hasn't been a new round in numValidators rounds
if n.NumValidators != 0 && n.NumNodesMonitoredOnline == n.NumValidators {
n.Health = FullHealth
} else if n.NumNodesMonitoredOnline > 0 && n.NumNodesMonitoredOnline <= n.NumNodesMonitored {
n.Health = ModerateHealth
} else {
n.Health = Dead
}
}
func (n *Network) UpdateNumValidatorsForHeight(num int, height int64) {
n.mu.Lock()
defer n.mu.Unlock()
if n.Height <= height {
n.NumValidators = num
}
}
func (n *Network) GetHealthString() string {
switch n.Health {
case FullHealth:
return "full"
case ModerateHealth:
return "moderate"
case Dead:
return "dead"
default:
return "undefined"
}
}
// Uptime returns network's uptime in percentages.
func (n *Network) Uptime() float64 {
n.mu.Lock()
defer n.mu.Unlock()
return n.UptimeData.Uptime
}
// StartTime returns time we started monitoring.
func (n *Network) StartTime() time.Time {
return n.UptimeData.StartTime
}

+ 79
- 0
tm-monitor/monitor/network_test.go View File

@ -0,0 +1,79 @@
package monitor_test
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
tmtypes "github.com/tendermint/tendermint/types"
monitor "github.com/tendermint/tools/tm-monitor/monitor"
)
func TestNetworkNewBlock(t *testing.T) {
n := monitor.NewNetwork()
n.NewBlock(tmtypes.Header{Height: 5, NumTxs: 100})
assert.Equal(t, int64(5), n.Height)
assert.Equal(t, 0.0, n.AvgBlockTime)
assert.Equal(t, 0.0, n.AvgTxThroughput)
}
func TestNetworkNewBlockLatency(t *testing.T) {
n := monitor.NewNetwork()
n.NewBlockLatency(9000000.0) // nanoseconds
assert.Equal(t, 0.0, n.AvgBlockLatency)
}
func TestNetworkNodeIsDownThenOnline(t *testing.T) {
n := monitor.NewNetwork()
n.NewNode("test")
n.NodeIsDown("test")
assert.Equal(t, 0, n.NumNodesMonitoredOnline)
assert.Equal(t, monitor.Dead, n.Health)
n.NodeIsDown("test")
assert.Equal(t, 0, n.NumNodesMonitoredOnline)
n.NodeIsOnline("test")
assert.Equal(t, 1, n.NumNodesMonitoredOnline)
assert.Equal(t, monitor.ModerateHealth, n.Health)
n.NodeIsOnline("test")
assert.Equal(t, 1, n.NumNodesMonitoredOnline)
}
func TestNetworkNewNode(t *testing.T) {
n := monitor.NewNetwork()
assert.Equal(t, 0, n.NumNodesMonitored)
assert.Equal(t, 0, n.NumNodesMonitoredOnline)
n.NewNode("test")
assert.Equal(t, 1, n.NumNodesMonitored)
assert.Equal(t, 1, n.NumNodesMonitoredOnline)
}
func TestNetworkNodeDeleted(t *testing.T) {
n := monitor.NewNetwork()
n.NewNode("test")
n.NodeDeleted("test")
assert.Equal(t, 0, n.NumNodesMonitored)
assert.Equal(t, 0, n.NumNodesMonitoredOnline)
}
func TestNetworkGetHealthString(t *testing.T) {
n := monitor.NewNetwork()
assert.Equal(t, "full", n.GetHealthString())
n.Health = monitor.ModerateHealth
assert.Equal(t, "moderate", n.GetHealthString())
n.Health = monitor.Dead
assert.Equal(t, "dead", n.GetHealthString())
}
func TestNetworkUptime(t *testing.T) {
n := monitor.NewNetwork()
assert.Equal(t, 100.0, n.Uptime())
}
func TestNetworkStartTime(t *testing.T) {
n := monitor.NewNetwork()
assert.True(t, n.StartTime().Before(time.Now()))
}

+ 260
- 0
tm-monitor/monitor/node.go View File

@ -0,0 +1,260 @@
package monitor
import (
"encoding/json"
"math"
"time"
"github.com/pkg/errors"
crypto "github.com/tendermint/go-crypto"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpc_client "github.com/tendermint/tendermint/rpc/lib/client"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/events"
"github.com/tendermint/tmlibs/log"
em "github.com/tendermint/tools/tm-monitor/eventmeter"
)
const maxRestarts = 25
type Node struct {
rpcAddr string
IsValidator bool `json:"is_validator"` // validator or non-validator?
pubKey crypto.PubKey `json:"pub_key"`
Name string `json:"name"`
Online bool `json:"online"`
Height int64 `json:"height"`
BlockLatency float64 `json:"block_latency" amino:"unsafe"` // ms, interval between block commits
// em holds the ws connection. Each eventMeter callback is called in a separate go-routine.
em eventMeter
// rpcClient is an client for making RPC calls to TM
rpcClient rpc_client.HTTPClient
blockCh chan<- tmtypes.Header
blockLatencyCh chan<- float64
disconnectCh chan<- bool
checkIsValidatorInterval time.Duration
quit chan struct{}
logger log.Logger
}
func NewNode(rpcAddr string, options ...func(*Node)) *Node {
em := em.NewEventMeter(rpcAddr, UnmarshalEvent)
rpcClient := rpc_client.NewURIClient(rpcAddr) // HTTP client by default
rpcClient.SetCodec(cdc)
return NewNodeWithEventMeterAndRpcClient(rpcAddr, em, rpcClient, options...)
}
func NewNodeWithEventMeterAndRpcClient(rpcAddr string, em eventMeter, rpcClient rpc_client.HTTPClient, options ...func(*Node)) *Node {
n := &Node{
rpcAddr: rpcAddr,
em: em,
rpcClient: rpcClient,
Name: rpcAddr,
quit: make(chan struct{}),
checkIsValidatorInterval: 5 * time.Second,
logger: log.NewNopLogger(),
}
for _, option := range options {
option(n)
}
return n
}
// SetCheckIsValidatorInterval lets you change interval for checking whenever
// node is still a validator or not.
func SetCheckIsValidatorInterval(d time.Duration) func(n *Node) {
return func(n *Node) {
n.checkIsValidatorInterval = d
}
}
func (n *Node) SendBlocksTo(ch chan<- tmtypes.Header) {
n.blockCh = ch
}
func (n *Node) SendBlockLatenciesTo(ch chan<- float64) {
n.blockLatencyCh = ch
}
func (n *Node) NotifyAboutDisconnects(ch chan<- bool) {
n.disconnectCh = ch
}
// SetLogger lets you set your own logger
func (n *Node) SetLogger(l log.Logger) {
n.logger = l
n.em.SetLogger(l)
}
func (n *Node) Start() error {
if err := n.em.Start(); err != nil {
return err
}
n.em.RegisterLatencyCallback(latencyCallback(n))
err := n.em.Subscribe(tmtypes.EventQueryNewBlockHeader.String(), newBlockCallback(n))
if err != nil {
return err
}
n.em.RegisterDisconnectCallback(disconnectCallback(n))
n.Online = true
n.checkIsValidator()
go n.checkIsValidatorLoop()
return nil
}
func (n *Node) Stop() {
n.Online = false
n.em.Stop()
close(n.quit)
}
// implements eventmeter.EventCallbackFunc
func newBlockCallback(n *Node) em.EventCallbackFunc {
return func(metric *em.EventMetric, data interface{}) {
block := data.(tmtypes.TMEventData).(tmtypes.EventDataNewBlockHeader).Header
n.Height = block.Height
n.logger.Info("new block", "height", block.Height, "numTxs", block.NumTxs)
if n.blockCh != nil {
n.blockCh <- *block
}
}
}
// implements eventmeter.EventLatencyFunc
func latencyCallback(n *Node) em.LatencyCallbackFunc {
return func(latency float64) {
n.BlockLatency = latency / 1000000.0 // ns to ms
n.logger.Info("new block latency", "latency", n.BlockLatency)
if n.blockLatencyCh != nil {
n.blockLatencyCh <- latency
}
}
}
// implements eventmeter.DisconnectCallbackFunc
func disconnectCallback(n *Node) em.DisconnectCallbackFunc {
return func() {
n.Online = false
n.logger.Info("status", "down")
if n.disconnectCh != nil {
n.disconnectCh <- true
}
}
}
func (n *Node) RestartEventMeterBackoff() error {
attempt := 0
for {
d := time.Duration(math.Exp2(float64(attempt)))
time.Sleep(d * time.Second)
if err := n.em.Start(); err != nil {
n.logger.Info("restart failed", "err", err)
} else {
// TODO: authenticate pubkey
return nil
}
attempt++
if attempt > maxRestarts {
return errors.New("Reached max restarts")
}
}
}
func (n *Node) NumValidators() (height int64, num int, err error) {
height, vals, err := n.validators()
if err != nil {
return 0, 0, err
}
return height, len(vals), nil
}
func (n *Node) validators() (height int64, validators []*tmtypes.Validator, err error) {
vals := new(ctypes.ResultValidators)
if _, err = n.rpcClient.Call("validators", nil, vals); err != nil {
return 0, make([]*tmtypes.Validator, 0), err
}
return vals.BlockHeight, vals.Validators, nil
}
func (n *Node) checkIsValidatorLoop() {
for {
select {
case <-n.quit:
return
case <-time.After(n.checkIsValidatorInterval):
n.checkIsValidator()
}
}
}
func (n *Node) checkIsValidator() {
_, validators, err := n.validators()
if err == nil {
for _, v := range validators {
key, err1 := n.getPubKey()
// TODO: use bytes.Equal
if err1 == nil && v.PubKey == key {
n.IsValidator = true
}
}
} else {
n.logger.Info("check is validator failed", "err", err)
}
}
func (n *Node) getPubKey() (crypto.PubKey, error) {
if n.pubKey != nil {
return n.pubKey, nil
}
status := new(ctypes.ResultStatus)
_, err := n.rpcClient.Call("status", nil, status)
if err != nil {
return nil, err
}
n.pubKey = status.ValidatorInfo.PubKey
return n.pubKey, nil
}
type eventMeter interface {
Start() error
Stop()
RegisterLatencyCallback(em.LatencyCallbackFunc)
RegisterDisconnectCallback(em.DisconnectCallbackFunc)
Subscribe(string, em.EventCallbackFunc) error
Unsubscribe(string) error
SetLogger(l log.Logger)
}
// UnmarshalEvent unmarshals a json event
func UnmarshalEvent(b json.RawMessage) (string, events.EventData, error) {
event := new(ctypes.ResultEvent)
if err := cdc.UnmarshalJSON(b, event); err != nil {
return "", nil, err
}
return event.Query, event.Data, nil
}

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

Loading…
Cancel
Save