├── .circleci └── config.yml ├── .codecov.yml ├── .editorconfig ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── app ├── app.go ├── app_test.go ├── helpers_test.go └── utils.go ├── cmd ├── ethermint │ ├── ethermint.go │ ├── init.go │ ├── main.go │ └── reset.go └── utils │ ├── config.go │ ├── config_test.go │ ├── flags.go │ ├── log.go │ ├── parse.go │ ├── parse_test.go │ ├── reset_test.go │ ├── testdata │ ├── blank-genesis.json │ ├── genesis1.json │ └── non-genesis.json │ └── utils.go ├── docs ├── Makefile ├── _static │ └── .gitkeep ├── architecture │ ├── future-architecture.rst │ ├── inter-blockchain-communication.rst │ └── peg-zones.rst ├── assets │ ├── contract-hash-remix.png │ ├── ethermint-default-address-remix.png │ ├── ethermint-logo.png │ ├── mist-ethermint-1.png │ ├── remix-create-contract.png │ └── remix-ide.png ├── conf.py ├── getting-started │ ├── getting-started.rst │ ├── install.rst │ └── using-mist.rst ├── index.rst ├── introduction │ └── what-is-ethermint.rst ├── make.bat ├── requirements.txt └── testnets │ └── venus.rst ├── ethereum ├── api.go ├── backend.go ├── eth_state.go ├── node.go ├── node_test.go └── txs.go ├── glide.lock ├── glide.yaml ├── scripts ├── dist.sh ├── docker │ ├── Dockerfile │ └── Dockerfile.develop ├── kubernetes │ ├── README.md │ ├── delete.sh │ ├── ethermint.template.yaml │ ├── expose.sh │ └── start.sh └── publish.sh ├── setup ├── genesis.json └── keystore │ └── UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc ├── strategies ├── miner │ └── reward_constant.go └── validators │ └── tx_based.go ├── tests ├── benchmarking │ ├── config │ │ └── default.json │ ├── multipleAccounts.js │ ├── oneAccount.js │ ├── package.json │ ├── utils.js │ └── web3pool.js ├── docker │ ├── Dockerfile │ └── build.sh ├── integration │ └── truffle │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── build.sh │ │ ├── compare.sh │ │ ├── config │ │ ├── default.js │ │ └── test.js │ │ ├── deploytools │ │ ├── clean.sh │ │ ├── ethgen.json │ │ ├── genesis.json │ │ ├── keystore │ │ │ ├── 0161e041aad467a890839d5b08b138c1e6373072 │ │ │ ├── 77e0d0f7b13e4b322c08f6e5b74ea1ef5844102a │ │ │ ├── 85418fff140231f43e7028af2588fa3e5b614c4d │ │ │ ├── 87da6a8c6e9eff15d703fc2773e32f6af8dbe301 │ │ │ ├── ac640610807f2f1a1ad1b88e0af869e9f7ff44bc │ │ │ ├── b51a16ed5000cbff1e9a0c9a497e94e6988b917c │ │ │ ├── b97de4b8c857e4f6bc354f226dc3249aaee49209 │ │ │ ├── cf6abdcbd268c680c58e20a146985b3a2d989c13 │ │ │ ├── d465119e045a107f7e62a8ed40ee7bf8c9d37109 │ │ │ ├── e11c86a37c4403c9a0e510815fe1200fea17d1a3 │ │ │ ├── e30c37dc10b70f642bfa52c9d3a05484a817339d │ │ │ ├── f85f61d4466c4808748e288ba1999fb5e834c38e │ │ │ └── f92adccbff93802b78e24a7a04186c9181c13d76 │ │ ├── passwords │ │ ├── priv_validator.json │ │ ├── run-ethereum.sh │ │ ├── run-ethermint.sh │ │ ├── run-tendermint.sh │ │ ├── run.sh │ │ ├── setup-ethereum.sh │ │ ├── setup-ethermint.sh │ │ ├── setup.sh │ │ └── vars.sh │ │ ├── diff.js │ │ ├── ether-test.sh │ │ ├── file-example │ │ ├── gasLimit │ │ ├── index.js │ │ └── test.sol │ │ ├── package-lock.json │ │ ├── package.json │ │ └── test-reporter.js ├── p2p │ ├── ip.sh │ ├── seeds.sh │ └── stop_tests.sh ├── scripts │ └── test_coverage.sh ├── tendermint_data │ ├── tendermint_1 │ │ ├── genesis.json │ │ └── priv_validator.json │ ├── tendermint_2 │ │ ├── genesis.json │ │ └── priv_validator.json │ ├── tendermint_3 │ │ ├── genesis.json │ │ └── priv_validator.json │ └── tendermint_4 │ │ ├── genesis.json │ │ └── priv_validator.json └── test.sh ├── types ├── mockClient.go └── types.go └── version └── version.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-go/ for more details 4 | version: 2 5 | jobs: 6 | build: 7 | docker: 8 | # specify the version 9 | - image: circleci/golang:1.9 10 | 11 | # Specify service dependencies here if necessary 12 | # CircleCI maintains a library of pre-built images 13 | # documented at https://circleci.com/docs/2.0/circleci-images/ 14 | # - image: circleci/postgres:9.4 15 | 16 | #### TEMPLATE_NOTE: go expects specific checkout path representing url 17 | #### expecting it in the form of 18 | #### /go/src/github.com/circleci/go-tool 19 | #### /go/src/bitbucket.org/circleci/go-tool 20 | working_directory: /go/src/github.com/tendermint/ethermint 21 | steps: 22 | - checkout 23 | 24 | # specify any bash command here prefixed with `run: ` 25 | - run: make get_vendor_deps 26 | - run: make test 27 | - run: make tools 28 | - run: make linter 29 | - run: make test_coverage && bash <(curl -s https://codecov.io/bash) -f coverage.txt 30 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This codecov.yml is the default configuration for 3 | # all repositories on Codecov. You may adjust the settings 4 | # below in your own codecov.yml in your repository. 5 | # 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 70...100 10 | 11 | status: 12 | # Learn more at https://codecov.io/docs#yaml_default_commit_status 13 | project: 14 | default: 15 | threshold: 1% # allow this much decrease on project 16 | changes: false 17 | 18 | comment: 19 | layout: "header, diff" 20 | behavior: default # update if exists else create new 21 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | 14 | [*.sh] 15 | indent_style = tab 16 | 17 | [*.proto] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS: https://help.github.com/articles/about-codeowners/ 2 | 3 | * @jaekwon 4 | *.md @zramsay 5 | *.rst @zramsay 6 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # The Tendermint Code of Conduct 2 | This code of conduct applies to all projects run by the Tendermint/COSMOS team and hence to Ethermint. 3 | 4 | 5 | ---- 6 | 7 | 8 | # Conduct 9 | ## Contact: adrian@tendermint.com 10 | 11 | * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. 12 | 13 | * On Slack, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. 14 | 15 | * Please be kind and courteous. There’s no need to be mean or rude. 16 | 17 | * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. 18 | 19 | * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. 20 | 21 | * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups. 22 | 23 | * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel admins or the person mentioned above immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back. 24 | 25 | * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome. 26 | 27 | 28 | ---- 29 | 30 | 31 | # Moderation 32 | These are the policies for upholding our community’s standards of conduct. If you feel that a thread needs moderation, please contact the above mentioned person. 33 | 34 | 1. Remarks that violate the Tendermint/COSMOS standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) 35 | 36 | 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. 37 | 38 | 3. Moderators will first respond to such remarks with a warning. 39 | 40 | 4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off. 41 | 42 | 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. 43 | 44 | 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. 45 | 46 | 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, in private. Complaints about bans in-channel are not allowed. 47 | 48 | 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. 49 | 50 | In the Tendermint/COSMOS community we strive to go the extra step to look out for each other. Don’t just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they’re off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. 51 | 52 | And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make your fellow Cosmonauts comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. 53 | 54 | The enforcement policies listed above apply to all official Tendermint/COSMOS venues.For other projects adopting the Tendermint/COSMOS Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. 55 | 56 | *Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling), the [Contributor Covenant v1.3.0](http://contributor-covenant.org/version/1/3/0/) and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). 57 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Pull Requests 2 | If you are working directly on this repository instead of forking it, please make sure 3 | that you follow the [git-flow](http://nvie.com/posts/a-successful-git-branching-model/) 4 | naming convention. 5 | Your branch should start with `feature/your_feature_name`. When there is a lot of development 6 | happening on the `develop` branch, you should `rebase` your feature branch onto develop 7 | regularly. 8 | 9 | If opening a PR against Ethermint, please use branch `develop` as the base branch 10 | for changes. 11 | `unstable` should only be used to integrate multiple PRs that might have effects on each other. 12 | 13 | Please make sure that all tests run with `make test` pass. 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | *Please describe the issue as detailed as possible.* 3 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Relates to: 3 | *Please provide a description of this PR.* 4 | 5 | ## Checklist 6 | - [ ] Update the documentation/architecture to reflect the changes 7 | - [ ] Merge this PR by squashing. 8 | - [ ] Close any related issues once this PR is merged. 9 | - [ ] Remove the original branch once the PR is merged. 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Custom ### 2 | # Allow developers to maintain a local todo file 3 | TODO.md 4 | 5 | # RTD/Sphinx 6 | docs/_build 7 | 8 | # glide 9 | vendor/ 10 | 11 | # Build artifacts 12 | build/ 13 | dist/ 14 | 15 | # CI/CD 16 | coverage.txt 17 | 18 | # Test artifacts 19 | tests/integration/truffle/build 20 | tests/tendermint_data/**/*.bak 21 | tests/tendermint_data/**/*.toml 22 | scripts/kubernetes/ethermint.yaml 23 | 24 | # Unsorted 25 | data 26 | dev/data 27 | *.swp 28 | *.swo 29 | ethstats/ 30 | dependency-graph.png 31 | 32 | 33 | ### Go ### 34 | # Binaries for programs and plugins 35 | *.exe 36 | *.dll 37 | *.so 38 | *.dylib 39 | 40 | # Test binary, build with `go test -c` 41 | *.test 42 | 43 | # Output of the go coverage tool, specifically when used with LiteIDE 44 | *.out 45 | 46 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 47 | .glide/ 48 | 49 | 50 | ### Vagrant ### 51 | .vagrant/ 52 | 53 | 54 | ### Node ### 55 | # Logs 56 | logs 57 | *.log 58 | npm-debug.log* 59 | yarn-debug.log* 60 | yarn-error.log* 61 | 62 | # Runtime data 63 | pids 64 | *.pid 65 | *.seed 66 | *.pid.lock 67 | 68 | # Directory for instrumented libs generated by jscoverage/JSCover 69 | lib-cov 70 | 71 | # Coverage directory used by tools like istanbul 72 | coverage 73 | 74 | # nyc test coverage 75 | .nyc_output 76 | 77 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 78 | .grunt 79 | 80 | # Bower dependency directory (https://bower.io/) 81 | bower_components 82 | 83 | # node-waf configuration 84 | .lock-wscript 85 | 86 | # Compiled binary addons (http://nodejs.org/api/addons.html) 87 | build/Release 88 | 89 | # Dependency directories 90 | node_modules/ 91 | jspm_packages/ 92 | 93 | # Typescript v1 declaration files 94 | typings/ 95 | 96 | # Optional npm cache directory 97 | .npm 98 | 99 | # Optional eslint cache 100 | .eslintcache 101 | 102 | # Optional REPL history 103 | .node_repl_history 104 | 105 | # Output of 'npm pack' 106 | *.tgz 107 | 108 | # Yarn Integrity file 109 | .yarn-integrity 110 | 111 | # dotenv environment variables file 112 | .env 113 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Ongoing 4 | ### Breaking 5 | * none so far 6 | 7 | ### Features 8 | * no empty blocks is implemented 9 | * Ethermint will halt until there are transactions 10 | 11 | ### Fixes 12 | * none so far 13 | 14 | 15 | ## v0.5.0 (2017-10-22) 16 | ### Breaking 17 | * none 18 | 19 | ### Features 20 | * temporary state is applied during CheckTx calls 21 | * allows multiple transactions per block from the same account 22 | * strict enforcement of nonce increases 23 | * a nonce has to be strictly increasing instead of just being greater 24 | * new `--with-tendermint` flag to start Tendermint in process 25 | * Tendermint binary needs to be installed in $PATH 26 | * NOTE: no library-level dependency on Tendermint core 27 | * networking is turned off for ethereum node 28 | * NOTE: allows running of go-ethereum and ethermint side-by-side 29 | * new `unsafe_reset_all` command to reset all initialisation files 30 | 31 | ### Improvements 32 | * semver guarantees to all exported APIs 33 | * documentation for readthedocs in docs folder 34 | * new getting started documentation for newcomers 35 | * update to go-ethereum 1.6.7 36 | * general dependency update 37 | * incremental performance optimisations 38 | * rework test suite for readability and completeness 39 | * add metalinter to build process for code quality 40 | * add maintainer and OWNER file 41 | 42 | 43 | ## 0.4.0 (2017-07-19) 44 | ### Breaking 45 | * pending.commit(...) takes an ethereum object instead of a blockchain object 46 | the change was necessary due to a change in the API exposed by geth 47 | 48 | ### Features 49 | * added benchmarking suite 50 | * added EMHOME variable 51 | 52 | ### Improvements 53 | * update to geth v1.6.6 54 | * add more money to the default account 55 | * increase the wait times in the contract tests to guard against false positives 56 | * binaries now correctly unzip to `ethermint` 57 | * general improvements to the build process 58 | * better documentation 59 | * proper linting and error detection 60 | * can be accessed with `make metalinter` and `make megacheck` 61 | 62 | 63 | ## v0.3.0 (2017-06-23) 64 | ### Breaking 65 | * none 66 | 67 | ### Features 68 | * Added multinode deployment possibilities with kubernetes and minikube 69 | * Add tests for deploying of smart contracts and transactions 70 | 71 | ### Improvements 72 | * Gas Limit improvements 73 | * Add initial work on the peg zone to the main ethereum network 74 | * Remove all deprecated files 75 | * Cleaned up the repo 76 | 77 | 78 | ## v0.2.0 (2017-06-02) 79 | ### Breaking 80 | * none 81 | 82 | ### Features 83 | * Add test suite 84 | 85 | ### Improvements 86 | * Update to tmlibs 87 | * Update to abci 88 | * Update to go-ethereum 1.6.1 89 | * Solve numerous bugs 90 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOTOOLS := \ 2 | github.com/karalabe/xgo \ 3 | github.com/alecthomas/gometalinter 4 | 5 | PACKAGES := $(shell glide novendor) 6 | 7 | BUILD_TAGS? := ethermint 8 | 9 | VERSION_TAG := 0.5.3 10 | 11 | BUILD_FLAGS = -ldflags "-X github.com/tendermint/ethermint/version.GitCommit=`git rev-parse --short HEAD`" 12 | 13 | 14 | ### Development ### 15 | all: get_vendor_deps install test 16 | 17 | install: 18 | CGO_ENABLED=1 go install $(BUILD_FLAGS) ./cmd/ethermint 19 | 20 | build: 21 | CGO_ENABLED=1 go build $(BUILD_FLAGS) -o ./build/ethermint ./cmd/ethermint 22 | 23 | test: 24 | @echo "--> Running go test" 25 | @go test $(PACKAGES) 26 | 27 | test_race: 28 | @echo "--> Running go test --race" 29 | @go test -v -race $(PACKAGES) 30 | 31 | test_integrations: 32 | @echo "--> Running integration tests" 33 | @bash ./tests/test.sh 34 | 35 | test_coverage: 36 | @echo "--> Running go test with coverage" 37 | bash ./tests/scripts/test_coverage.sh 38 | 39 | linter: 40 | @echo "--> Running metalinter" 41 | gometalinter --install 42 | gometalinter --vendor --tests --deadline=120s --disable-all \ 43 | --enable=unused \ 44 | --enable=lll --line-length=100 \ 45 | ./... 46 | 47 | clean: 48 | @echo "--> Cleaning the build and dependency files" 49 | rm -rf build/ 50 | rm -rf vendor/ 51 | rm -rf ethstats/ 52 | 53 | 54 | ### Tooling ### 55 | # requires brew install graphviz or apt-get install graphviz 56 | draw_deps: 57 | @echo "--> Drawing dependencies" 58 | go get github.com/RobotsAndPencils/goviz 59 | goviz -i github.com/tendermint/ethermint/cmd/ethermint -d 2 | dot -Tpng -o dependency-graph.png 60 | 61 | get_vendor_deps: 62 | @hash glide 2>/dev/null || go get github.com/Masterminds/glide 63 | @rm -rf vendor/ 64 | @echo "--> Running glide install" 65 | @glide install 66 | @# ethereum/node.go:53:23: cannot use ctx (type *"github.com/tendermint/ethermint/vendor/gopkg.in/urfave/cli.v1".Context) as type *"github.com/tendermint/ethermint/vendor/github.com/ethereum/go-ethereum/vendor/gopkg.in/urfave/cli.v1".Context in argument to utils.SetEthConfig 67 | @rm -rf vendor/github.com/ethereum/go-ethereum/vendor 68 | 69 | tools: 70 | @echo "--> Installing tools" 71 | go get $(GOTOOLS) 72 | 73 | update_tools: 74 | @echo "--> Updating tools" 75 | @go get -u $(GOTOOLS) 76 | 77 | ### Building and Publishing ### 78 | # dist builds binaries for all platforms and packages them for distribution 79 | dist: 80 | @echo "--> Building binaries" 81 | @BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/dist.sh'" 82 | 83 | publish: 84 | @echo "--> Publishing binaries" 85 | sh -c "'$(CURDIR)/scripts/publish.sh'" 86 | 87 | 88 | ### Docker ### 89 | docker_build_develop: 90 | docker build -t "tendermint/ethermint:develop" -t "adrianbrink/ethermint:develop" \ 91 | -f scripts/docker/Dockerfile.develop . 92 | 93 | docker_push_develop: 94 | docker push "tendermint/ethermint:develop" 95 | docker push "adrianbrink/ethermint:develop" 96 | 97 | docker_build: 98 | docker build -t "tendermint/ethermint" -t "tendermint/ethermint:$(VERSION_TAG)" \ 99 | -t "adrianbrink/ethermint" -t "adrianbrink/ethermint:$(VERSION_TAG)" -f scripts/docker/Dockerfile . 100 | 101 | docker_push: 102 | docker push "tendermint/ethermint:latest" 103 | docker push "tendermint/ethermint:$(VERSION_TAG)" 104 | docker push "adrianbrink/ethermint:latest" 105 | docker push "adrianbrink/ethermint:$(VERSION_TAG)" 106 | 107 | 108 | ### Ethstats ### 109 | ethstats: 110 | @git clone https://github.com/tendermint/eth-net-intelligence-api $(CURDIR)/ethstats 111 | 112 | ethstats_setup: ethstats 113 | @cd $(CURDIR)/ethstats && npm install && node scripts/configure.js 114 | 115 | ethstats_start: 116 | @cd $(CURDIR)/ethstats && pm2 start ./app.json 117 | 118 | ethstats_stop: 119 | @cd $(CURDIR)/ethstats && pm2 stop ./app.json 120 | 121 | .PHONY: all install build test test_race test_integrations test_coverage linter 122 | clean draw_deps get_vendor_deps tools update_tools dist publish 123 | docker_build_develop docker_push_develop docker_build docker_push ethstats 124 | ethstats_setup ethstats_start ethstats_stop 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ethermint 2 | 3 | **DEPRECATED:** See the link below for latest Ethermint. 4 | 5 | - https://github.com/cosmos/ethermint 6 | 7 | [![Build Status](https://travis-ci.org/tendermint/ethermint.svg?branch=develop)](https://travis-ci.org/tendermint/ethermint) [![License](https://img.shields.io/badge/license-GPLv3.0%2B-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.html) [![Documentation Status](https://readthedocs.org/projects/ethermint/badge/?version=master)](http://ethermint.readthedocs.io/en/latest/?badge=master) 8 | 9 | # !!! NOTICE !!! 10 | 11 | PLEASE READ THE [PROJECT 12 | UPDATE](https://github.com/tendermint/ethermint/issues/407). 13 | 14 | If you're interested in working full-time on Ethermint, [please consider applying](https://tendermint.com/careers/senior-software-engineer-ethermint). 15 | 16 | The latest version of Ethermint only works with Tendermint up to 0.14.0. 17 | 18 | ## Features 19 | 20 | Ethermint is fully compatible with the standard Ethereum tooling such as [geth](https://github.com/ethereum/go-ethereum), [mist](https://github.com/ethereum/mist) and [truffle](https://github.com/trufflesuite/truffle). Please 21 | install whichever tooling suits you best and [check out the documentation](http://ethermint.readthedocs.io/en/master) for more information. 22 | 23 | ## Installation 24 | 25 | See the [install documentation](http://ethermint.readthedocs.io/en/master/getting-started/install.html). For developers: 26 | 27 | ``` 28 | go get -u -d github.com/tendermint/ethermint 29 | go get -u -d github.com/tendermint/tendermint 30 | cd $GOPATH/src/github.com/tendermint/ethermint 31 | make install 32 | cd ../tendermint 33 | make install 34 | ``` 35 | 36 | ### Running Ethermint 37 | 38 | #### Initialisation 39 | To get started, you need to initialise the genesis block for tendermint core and go-ethereum. We provide initialisation 40 | files with reasonable defaults and money allocated into a predefined account. If you installed from binary or docker 41 | please download these default files [here](https://github.com/tendermint/ethermint/tree/develop/setup). 42 | 43 | You can choose where to store the ethermint files with `--datadir`. For this guide, we will use `~/.ethermint`, which is a reasonable default in most cases. 44 | 45 | Before you can run ethermint you need to initialise tendermint and ethermint with their respective genesis states. 46 | Please switch into the folder where you have the initialisation files. If you installed from source you can just follow 47 | these instructions. 48 | 49 | ```bash 50 | ethermint --datadir ~/.ethermint --with-tendermint init 51 | ``` 52 | 53 | which will also invoke `tendermint init --home ~/.ethermint/tendermint`. You can prevent Tendermint from 54 | being starting by excluding the flag `--with-tendermint` for example: 55 | 56 | ```bash 57 | ethermint --datadir ~/.ethermint init 58 | ``` 59 | 60 | and then you will have to invoke `tendermint` in another shell with the command: 61 | 62 | ```bash 63 | tendermint init --home ~/.ethermint/tendermint 64 | ``` 65 | 66 | For simplicity, we'll have ethermint start tendermint as a subprocess with the 67 | flag `--with-tendermint`: 68 | 69 | ```bash 70 | ethermint --with-tendermint --datadir ~/.ethermint --rpc --rpcaddr=0.0.0.0 --ws --wsaddr=0.0.0.0 --rpcapi eth,net,web3,personal,admin 71 | ``` 72 | 73 | Note: The **password** for the default account is *1234*. 74 | 75 | There you have it, Ethereum on Tendermint! For details on what to do next, 76 | check out [the documentation](http://ethermint.readthedocs.io/en/master/) 77 | 78 | ## Contributing 79 | 80 | Thank you for considering making contributions to Ethermint! 81 | 82 | Check out the [contributing guidelines](.github/CONTRIBUTING.md) for information 83 | on getting starting with contributing. 84 | 85 | See the [open issues](https://github.com/tendermint/ethermint/issues) for 86 | things we need help with! 87 | 88 | ## Support 89 | 90 | Check out the [community page](https://tendermint.com/community) for various resources. 91 | 92 | ## License 93 | 94 | [GPLv3](LICENSE) 95 | -------------------------------------------------------------------------------- /app/app_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "io/ioutil" 5 | "math/big" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | 12 | "golang.org/x/net/context" 13 | 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core" 16 | "github.com/ethereum/go-ethereum/rlp" 17 | 18 | "github.com/tendermint/ethermint/ethereum" 19 | 20 | abciTypes "github.com/tendermint/abci/types" 21 | "github.com/cosmos/cosmos-sdk/errors" 22 | ) 23 | 24 | var ( 25 | amount = big.NewInt(1000) 26 | gasLimit = big.NewInt(21000) 27 | gasPrice = big.NewInt(10) 28 | ) 29 | 30 | func setupTestCase(t *testing.T, addresses []common.Address) (tearDown func(t *testing.T), 31 | app *EthermintApplication, backend *ethereum.Backend, mockClient *MockClient) { 32 | t.Log("Setup test case") 33 | 34 | // Setup the temporary directory for a test case 35 | temporaryDirectory, err := ioutil.TempDir("", "ethermint_test") 36 | if err != nil { 37 | t.Errorf("Unable to create the temporary directory for the tests: %v", err) 38 | } 39 | 40 | // Setup the app and backend for a test case 41 | mockClient = NewMockClient() 42 | node, backend, app, err := makeTestApp(temporaryDirectory, addresses, mockClient) 43 | if err != nil { 44 | t.Errorf("Error making test EthermintApplication: %v", err) 45 | } 46 | 47 | tearDown = func(t *testing.T) { 48 | t.Log("Tearing down test case") 49 | node.Stop() 50 | os.RemoveAll(temporaryDirectory) 51 | } 52 | 53 | return 54 | } 55 | 56 | // TestStrictlyIncrementingNonces tests that nonces have to increment by 1 57 | // instead of just being greater than the previous nonce. 58 | func TestStrictlyIncrementingNonces(t *testing.T) { 59 | privateKey, address := generateKeyPair(t) 60 | teardownTestCase, app, _, _ := setupTestCase(t, []common.Address{address}) 61 | defer teardownTestCase(t) 62 | 63 | height := int64(1) 64 | 65 | // create txs with different nonces 66 | tx1 := createTxBytes(t, privateKey, 0, 67 | receiverAddress, amount, gasLimit, gasPrice, nil) 68 | tx2 := createTxBytes(t, privateKey, 1, 69 | receiverAddress, amount, gasLimit, gasPrice, nil) 70 | tx3 := createTxBytes(t, privateKey, 2, 71 | receiverAddress, amount, gasLimit, gasPrice, nil) 72 | 73 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx1).Code) 74 | // expect a failure here since the nonce is not strictly increasing 75 | assert.Equal(t, errors.CodeTypeBadNonce, app.CheckTx(tx3).Code) 76 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx2).Code) 77 | 78 | app.BeginBlock( 79 | abciTypes.RequestBeginBlock { 80 | Hash: []byte{}, 81 | Header: &abciTypes.Header{Height: height, Time: 1}}) 82 | 83 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx1).Code) 84 | // expect a failure here since the nonce is not strictly increasing 85 | assert.Equal(t, errors.CodeTypeInternalErr, app.DeliverTx(tx3).Code) 86 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx2).Code) 87 | 88 | app.EndBlock(abciTypes.RequestEndBlock{height}) 89 | 90 | assert.Equal(t, abciTypes.CodeTypeOK, app.Commit().Code) 91 | } 92 | 93 | // TestBumpingNoncesWithRawTransaction sends a transaction over the RPC 94 | // interface of Tendermint. 95 | func TestBumpingNoncesViaRPC(t *testing.T) { 96 | ctx := context.Background() 97 | privateKey, address := generateKeyPair(t) 98 | teardownTestCase, app, backend, mockClient := setupTestCase(t, []common.Address{address}) 99 | defer teardownTestCase(t) 100 | 101 | height := int64(1) 102 | nonceOne := uint64(0) 103 | nonceTwo := uint64(1) 104 | 105 | rawTx1 := createTx(t, privateKey, nonceOne, 106 | receiverAddress, amount, gasLimit, gasPrice, nil) 107 | tx1, err := rlp.EncodeToBytes(rawTx1) 108 | if err != nil { 109 | t.Errorf("Error encoding the transaction: %v", err) 110 | } 111 | 112 | rawTx2 := createTx(t, privateKey, nonceTwo, 113 | receiverAddress, amount, gasLimit, gasPrice, nil) 114 | 115 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx1).Code) 116 | 117 | app.BeginBlock( 118 | abciTypes.RequestBeginBlock { 119 | Hash: []byte{}, 120 | Header: &abciTypes.Header{Height: height, Time: 1}}) 121 | 122 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx1).Code) 123 | 124 | app.EndBlock(abciTypes.RequestEndBlock{height}) 125 | 126 | assert.Equal(t, abciTypes.CodeTypeOK, app.Commit().Code) 127 | 128 | // replays should fail - we're checking if the transaction got through earlier, by 129 | // replaying the nonce 130 | assert.Equal(t, errors.CodeTypeBadNonce, app.CheckTx(tx1).Code) 131 | // ...on both interfaces of the app 132 | assert.Equal(t, core.ErrNonceTooLow, backend.Ethereum().ApiBackend.SendTx(ctx, rawTx1)) 133 | 134 | assert.Equal(t, nil, backend.Ethereum().ApiBackend.SendTx(ctx, rawTx2)) 135 | 136 | ticker := time.NewTicker(5 * time.Second) 137 | select { 138 | case <-ticker.C: 139 | assert.Fail(t, "Timeout waiting for transaction on the tendermint rpc") 140 | case <-mockClient.SentBroadcastTx: 141 | } 142 | } 143 | 144 | // TestMultipleTxOneAcc sends multiple txs from the same account in the same block 145 | func TestMultipleTxOneAcc(t *testing.T) { 146 | privateKeyOne, address := generateKeyPair(t) 147 | teardownTestCase, app, _, _ := setupTestCase(t, []common.Address{address}) 148 | defer teardownTestCase(t) 149 | 150 | height := int64(1) 151 | nonceOne := uint64(0) 152 | nonceTwo := uint64(1) 153 | 154 | tx1 := createTxBytes(t, privateKeyOne, nonceOne, 155 | receiverAddress, amount, gasLimit, gasPrice, nil) 156 | tx2 := createTxBytes(t, privateKeyOne, nonceTwo, 157 | receiverAddress, amount, gasLimit, gasPrice, nil) 158 | 159 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx1).Code) 160 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx2).Code) 161 | 162 | app.BeginBlock( 163 | abciTypes.RequestBeginBlock { 164 | Hash: []byte{}, 165 | Header: &abciTypes.Header{Height: height, Time: 1}}) 166 | 167 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx1).Code) 168 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx2).Code) 169 | 170 | app.EndBlock(abciTypes.RequestEndBlock{height}) 171 | 172 | assert.Equal(t, abciTypes.CodeTypeOK, app.Commit().Code) 173 | } 174 | 175 | // TestMultipleTxFromTwoAcc sends multiple txs from two different accounts 176 | func TestMultipleTxFromTwoAcc(t *testing.T) { 177 | privateKeyOne, addressOne := generateKeyPair(t) 178 | privateKeyTwo, addressTwo := generateKeyPair(t) 179 | teardownTestCase, app, _, _ := setupTestCase(t, []common.Address{addressOne, addressTwo}) 180 | defer teardownTestCase(t) 181 | 182 | height := int64(1) 183 | nonceOne := uint64(0) 184 | nonceTwo := uint64(0) 185 | 186 | tx1 := createTxBytes(t, privateKeyOne, nonceOne, 187 | receiverAddress, amount, gasLimit, gasPrice, nil) 188 | tx2 := createTxBytes(t, privateKeyTwo, nonceTwo, 189 | receiverAddress, amount, gasLimit, gasPrice, nil) 190 | 191 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx1).Code) 192 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx2).Code) 193 | 194 | app.BeginBlock( 195 | abciTypes.RequestBeginBlock { 196 | Hash: []byte{}, 197 | Header: &abciTypes.Header{Height: height, Time: 1}}) 198 | 199 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx1).Code) 200 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx2).Code) 201 | 202 | app.EndBlock(abciTypes.RequestEndBlock{height}) 203 | 204 | assert.Equal(t, abciTypes.CodeTypeOK, app.Commit().Code) 205 | } 206 | 207 | // TestFromAccToAcc sends a transaction from account A to account B and from 208 | // account B to account C 209 | func TestFromAccToAcc(t *testing.T) { 210 | privateKeyOne, addressOne := generateKeyPair(t) 211 | privateKeyTwo, addressTwo := generateKeyPair(t) 212 | teardownTestCase, app, _, _ := setupTestCase(t, []common.Address{addressOne}) 213 | defer teardownTestCase(t) 214 | 215 | height := int64(1) 216 | nonceOne := uint64(0) 217 | nonceTwo := uint64(0) 218 | 219 | tx1 := createTxBytes(t, privateKeyOne, nonceOne, 220 | addressTwo, big.NewInt(1000000), gasLimit, gasPrice, nil) 221 | tx2 := createTxBytes(t, privateKeyTwo, nonceTwo, 222 | receiverAddress, big.NewInt(50000), gasLimit, gasPrice, nil) 223 | 224 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx1).Code) 225 | assert.Equal(t, abciTypes.CodeTypeOK, app.CheckTx(tx2).Code) 226 | 227 | app.BeginBlock( 228 | abciTypes.RequestBeginBlock { 229 | Hash: []byte{}, 230 | Header: &abciTypes.Header{Height: height, Time: 1}}) 231 | 232 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx1).Code) 233 | assert.Equal(t, abciTypes.CodeTypeOK, app.DeliverTx(tx2).Code) 234 | 235 | app.EndBlock(abciTypes.RequestEndBlock{height}) 236 | 237 | assert.Equal(t, abciTypes.CodeTypeOK, app.Commit().Code) 238 | } 239 | -------------------------------------------------------------------------------- /app/helpers_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/json" 6 | "errors" 7 | "math/big" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/tendermint/tmlibs/log" 14 | 15 | ctypes "github.com/tendermint/tendermint/rpc/core/types" 16 | 17 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 18 | "github.com/ethereum/go-ethereum/common" 19 | "github.com/ethereum/go-ethereum/core" 20 | ethTypes "github.com/ethereum/go-ethereum/core/types" 21 | "github.com/ethereum/go-ethereum/crypto" 22 | "github.com/ethereum/go-ethereum/eth" 23 | "github.com/ethereum/go-ethereum/node" 24 | "github.com/ethereum/go-ethereum/rlp" 25 | 26 | "github.com/tendermint/ethermint/cmd/utils" 27 | "github.com/tendermint/ethermint/ethereum" 28 | ) 29 | 30 | var ( 31 | receiverAddress = common.StringToAddress("0x1234123412341234123412341234123412341234") 32 | ) 33 | 34 | // implements: tendermint.rpc.client.HTTPClient 35 | type MockClient struct { 36 | SentBroadcastTx chan struct{} // fires when we call broadcast_tx_sync 37 | } 38 | 39 | func NewMockClient() *MockClient { return &MockClient{make(chan struct{})} } 40 | 41 | func (mc *MockClient) Call(method string, params map[string]interface{}, 42 | result interface{}) (interface{}, error) { 43 | 44 | _ = result 45 | switch method { 46 | case "status": 47 | result = &ctypes.ResultStatus{} 48 | return result, nil 49 | case "broadcast_tx_sync": 50 | close(mc.SentBroadcastTx) 51 | result = &ctypes.ResultBroadcastTx{} 52 | return result, nil 53 | } 54 | 55 | return nil, errors.New("Shouldn't happen.") 56 | } 57 | 58 | func generateKeyPair(t *testing.T) (*ecdsa.PrivateKey, common.Address) { 59 | privateKey, err := crypto.GenerateKey() 60 | if err != nil { 61 | t.Errorf("Error generating a private key: %v", err) 62 | } 63 | 64 | address := crypto.PubkeyToAddress(privateKey.PublicKey) 65 | 66 | return privateKey, address 67 | } 68 | 69 | func createTx(t *testing.T, key *ecdsa.PrivateKey, nonce uint64, to common.Address, amount, 70 | gasLimit, gasPrice *big.Int, data []byte) *ethTypes.Transaction { 71 | 72 | signer := ethTypes.HomesteadSigner{} 73 | 74 | transaction, err := ethTypes.SignTx( 75 | ethTypes.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data), 76 | signer, 77 | key, 78 | ) 79 | if err != nil { 80 | t.Errorf("Error creating the transaction: %v", err) 81 | } 82 | 83 | return transaction 84 | 85 | } 86 | 87 | func createTxBytes(t *testing.T, key *ecdsa.PrivateKey, nonce uint64, 88 | to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) []byte { 89 | 90 | transaction := createTx(t, key, nonce, to, amount, gasLimit, gasPrice, data) 91 | 92 | encodedTransaction, err := rlp.EncodeToBytes(transaction) 93 | if err != nil { 94 | t.Errorf("Error encoding the transaction: %v", err) 95 | } 96 | 97 | return encodedTransaction 98 | } 99 | 100 | // TODO: [adrian] Change node.Node to use ethereum.Node, which is our own node 101 | // without the networking stack. This should be held off until we decide on 102 | // the new design. 103 | 104 | // mimics abciEthereumAction from cmd/ethermint/main.go 105 | func makeTestApp(tempDatadir string, addresses []common.Address, 106 | mockClient *MockClient) (*node.Node, *ethereum.Backend, *EthermintApplication, error) { 107 | stack, err := makeTestSystemNode(tempDatadir, addresses, mockClient) 108 | if err != nil { 109 | return nil, nil, nil, err 110 | } 111 | ethUtils.StartNode(stack) 112 | 113 | var backend *ethereum.Backend 114 | if err = stack.Service(&backend); err != nil { 115 | return nil, nil, nil, err 116 | } 117 | 118 | app, err := NewEthermintApplication(backend, nil, nil) 119 | app.SetLogger(log.TestingLogger()) 120 | 121 | return stack, backend, app, err 122 | } 123 | 124 | // mimics MakeSystemNode from ethereum/node.go 125 | func makeTestSystemNode(tempDatadir string, addresses []common.Address, 126 | mockClient *MockClient) (*node.Node, error) { 127 | // Configure the node's service container 128 | nodeConf := utils.DefaultNodeConfig() 129 | utils.SetEthermintNodeConfig(&nodeConf) 130 | nodeConf.DataDir = tempDatadir 131 | 132 | // Configure the Ethereum service 133 | ethConf := eth.DefaultConfig 134 | utils.SetEthermintEthConfig(ðConf) 135 | 136 | genesis, err := makeTestGenesis(addresses) 137 | if err != nil { 138 | return nil, err 139 | } 140 | 141 | ethConf.Genesis = genesis 142 | 143 | // Assemble and return the protocol stack 144 | stack, err := node.New(&nodeConf) 145 | if err != nil { 146 | return nil, err 147 | } 148 | return stack, stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 149 | return ethereum.NewBackend(ctx, ðConf, mockClient) 150 | }) 151 | } 152 | 153 | func makeTestGenesis(addresses []common.Address) (*core.Genesis, error) { 154 | currentDir, err := os.Getwd() 155 | if err != nil { 156 | return nil, err 157 | } 158 | genesisPath := filepath.Join(strings.TrimSuffix(currentDir, "/app"), "setup/genesis.json") 159 | 160 | file, err := os.Open(genesisPath) 161 | if err != nil { 162 | return nil, err 163 | } 164 | defer file.Close() // nolint: errcheck 165 | 166 | genesis := new(core.Genesis) 167 | if err := json.NewDecoder(file).Decode(genesis); err != nil { 168 | ethUtils.Fatalf("invalid genesis file: %v", err) 169 | } 170 | 171 | balance, result := new(big.Int).SetString("10000000000000000000000000000000000", 10) 172 | if !result { 173 | return nil, errors.New("BigInt conversion error") 174 | } 175 | 176 | for _, addr := range addresses { 177 | genesis.Alloc[addr] = core.GenesisAccount{Balance: balance} 178 | } 179 | 180 | return genesis, nil 181 | } 182 | -------------------------------------------------------------------------------- /app/utils.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/core/types" 9 | "github.com/ethereum/go-ethereum/rlp" 10 | 11 | abciTypes "github.com/tendermint/abci/types" 12 | ) 13 | 14 | // format of query data 15 | type jsonRequest struct { 16 | Method string `json:"method"` 17 | ID json.RawMessage `json:"id,omitempty"` 18 | Params []interface{} `json:"params,omitempty"` 19 | } 20 | 21 | // rlp decode an etherum transaction 22 | func decodeTx(txBytes []byte) (*types.Transaction, error) { 23 | tx := new(types.Transaction) 24 | rlpStream := rlp.NewStream(bytes.NewBuffer(txBytes), 0) 25 | if err := tx.DecodeRLP(rlpStream); err != nil { 26 | return nil, err 27 | } 28 | return tx, nil 29 | } 30 | 31 | //------------------------------------------------------- 32 | // convenience methods for validators 33 | 34 | // Receiver returns the receiving address based on the selected strategy 35 | // #unstable 36 | func (app *EthermintApplication) Receiver() common.Address { 37 | if app.strategy != nil { 38 | return app.strategy.Receiver() 39 | } 40 | return common.Address{} 41 | } 42 | 43 | // SetValidators sets new validators on the strategy 44 | // #unstable 45 | func (app *EthermintApplication) SetValidators(validators []*abciTypes.Validator) { 46 | if app.strategy != nil { 47 | app.strategy.SetValidators(validators) 48 | } 49 | } 50 | 51 | // GetUpdatedValidators returns an updated validator set from the strategy 52 | // #unstable 53 | func (app *EthermintApplication) GetUpdatedValidators() abciTypes.ResponseEndBlock { 54 | if app.strategy != nil { 55 | return abciTypes.ResponseEndBlock{ValidatorUpdates: app.strategy.GetUpdatedValidators()} 56 | } 57 | return abciTypes.ResponseEndBlock{} 58 | } 59 | 60 | // CollectTx invokes CollectTx on the strategy 61 | // #unstable 62 | func (app *EthermintApplication) CollectTx(tx *types.Transaction) { 63 | if app.strategy != nil { 64 | app.strategy.CollectTx(tx) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cmd/ethermint/ethermint.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "time" 8 | 9 | "gopkg.in/urfave/cli.v1" 10 | 11 | "github.com/ethereum/go-ethereum/accounts" 12 | "github.com/ethereum/go-ethereum/accounts/keystore" 13 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 14 | "github.com/ethereum/go-ethereum/console" 15 | "github.com/ethereum/go-ethereum/ethclient" 16 | "github.com/ethereum/go-ethereum/log" 17 | 18 | "github.com/tendermint/abci/server" 19 | 20 | cmn "github.com/tendermint/tmlibs/common" 21 | 22 | abciApp "github.com/tendermint/ethermint/app" 23 | emtUtils "github.com/tendermint/ethermint/cmd/utils" 24 | "github.com/tendermint/ethermint/ethereum" 25 | ) 26 | 27 | func ethermintCmd(ctx *cli.Context) error { 28 | // Step 1: Setup the go-ethereum node and start it 29 | node := emtUtils.MakeFullNode(ctx) 30 | startNode(ctx, node) 31 | 32 | // Step 2: If we can invoke `tendermint node`, let's do so 33 | // in order to make ethermint as self contained as possible. 34 | // See Issue https://github.com/tendermint/ethermint/issues/244 35 | canInvokeTendermintNode := canInvokeTendermint(ctx) 36 | if canInvokeTendermintNode { 37 | tendermintHome := tendermintHomeFromEthermint(ctx) 38 | tendermintArgs := []string{"--home", tendermintHome, "node"} 39 | go func() { 40 | if _, err := invokeTendermintNoTimeout(tendermintArgs...); err != nil { 41 | // We shouldn't go *Fatal* because 42 | // `tendermint node` might have already been invoked. 43 | log.Info("tendermint init", "error", err) 44 | } else { 45 | log.Info("Successfully invoked `tendermint node`", "args", 46 | tendermintArgs) 47 | } 48 | }() 49 | pauseDuration := 3 * time.Second 50 | log.Info(fmt.Sprintf("Invoked `tendermint node` sleeping for %s", pauseDuration), 51 | "args", tendermintArgs) 52 | time.Sleep(pauseDuration) 53 | } 54 | 55 | // Setup the ABCI server and start it 56 | addr := ctx.GlobalString(emtUtils.ABCIAddrFlag.Name) 57 | abci := ctx.GlobalString(emtUtils.ABCIProtocolFlag.Name) 58 | 59 | // Fetch the registered service of this type 60 | var backend *ethereum.Backend 61 | if err := node.Service(&backend); err != nil { 62 | ethUtils.Fatalf("ethereum backend service not running: %v", err) 63 | } 64 | 65 | // In-proc RPC connection so ABCI.Query can be forwarded over the ethereum rpc 66 | rpcClient, err := node.Attach() 67 | if err != nil { 68 | ethUtils.Fatalf("Failed to attach to the inproc geth: %v", err) 69 | } 70 | 71 | // Create the ABCI app 72 | ethApp, err := abciApp.NewEthermintApplication(backend, rpcClient, nil) 73 | if err != nil { 74 | fmt.Println(err) 75 | os.Exit(1) 76 | } 77 | ethApp.SetLogger(emtUtils.EthermintLogger().With("module", "ethermint")) 78 | 79 | // Start the app on the ABCI server 80 | srv, err := server.NewServer(addr, abci, ethApp) 81 | if err != nil { 82 | fmt.Println(err) 83 | os.Exit(1) 84 | } 85 | 86 | srv.SetLogger(emtUtils.EthermintLogger().With("module", "abci-server")) 87 | 88 | if err := srv.Start(); err != nil { 89 | fmt.Println(err) 90 | os.Exit(1) 91 | } 92 | 93 | cmn.TrapSignal(func() { 94 | srv.Stop() 95 | }) 96 | 97 | return nil 98 | } 99 | 100 | // nolint 101 | // startNode copies the logic from go-ethereum 102 | func startNode(ctx *cli.Context, stack *ethereum.Node) { 103 | emtUtils.StartNode(stack) 104 | 105 | // Unlock any account specifically requested 106 | ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 107 | 108 | passwords := ethUtils.MakePasswordList(ctx) 109 | unlocks := strings.Split(ctx.GlobalString(ethUtils.UnlockedAccountFlag.Name), ",") 110 | for i, account := range unlocks { 111 | if trimmed := strings.TrimSpace(account); trimmed != "" { 112 | unlockAccount(ctx, ks, trimmed, i, passwords) 113 | } 114 | } 115 | // Register wallet event handlers to open and auto-derive wallets 116 | events := make(chan accounts.WalletEvent, 16) 117 | stack.AccountManager().Subscribe(events) 118 | 119 | go func() { 120 | // Create an chain state reader for self-derivation 121 | rpcClient, err := stack.Attach() 122 | if err != nil { 123 | ethUtils.Fatalf("Failed to attach to self: %v", err) 124 | } 125 | stateReader := ethclient.NewClient(rpcClient) 126 | 127 | // Open and self derive any wallets already attached 128 | for _, wallet := range stack.AccountManager().Wallets() { 129 | if err := wallet.Open(""); err != nil { 130 | log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) 131 | } else { 132 | wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader) 133 | } 134 | } 135 | // Listen for wallet event till termination 136 | for event := range events { 137 | if event.Arrive { 138 | if err := event.Wallet.Open(""); err != nil { 139 | log.Warn("New wallet appeared, failed to open", "url", 140 | event.Wallet.URL(), "err", err) 141 | } else { 142 | log.Info("New wallet appeared", "url", event.Wallet.URL(), 143 | "status", event.Wallet.Status()) 144 | event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, 145 | stateReader) 146 | } 147 | } else { 148 | log.Info("Old wallet dropped", "url", event.Wallet.URL()) 149 | event.Wallet.Close() 150 | } 151 | } 152 | }() 153 | } 154 | 155 | // tries unlocking the specified account a few times. 156 | // nolint: unparam 157 | func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, 158 | passwords []string) (accounts.Account, string) { 159 | 160 | account, err := ethUtils.MakeAddress(ks, address) 161 | if err != nil { 162 | ethUtils.Fatalf("Could not list accounts: %v", err) 163 | } 164 | for trials := 0; trials < 3; trials++ { 165 | prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) 166 | password := getPassPhrase(prompt, false, i, passwords) 167 | err = ks.Unlock(account, password) 168 | if err == nil { 169 | log.Info("Unlocked account", "address", account.Address.Hex()) 170 | return account, password 171 | } 172 | if err, ok := err.(*keystore.AmbiguousAddrError); ok { 173 | log.Info("Unlocked account", "address", account.Address.Hex()) 174 | return ambiguousAddrRecovery(ks, err, password), password 175 | } 176 | if err != keystore.ErrDecrypt { 177 | // No need to prompt again if the error is not decryption-related. 178 | break 179 | } 180 | } 181 | // All trials expended to unlock account, bail out 182 | ethUtils.Fatalf("Failed to unlock account %s (%v)", address, err) 183 | 184 | return accounts.Account{}, "" 185 | } 186 | 187 | // getPassPhrase retrieves the passwor associated with an account, either fetched 188 | // from a list of preloaded passphrases, or requested interactively from the user. 189 | // nolint: unparam 190 | func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string { 191 | // If a list of passwords was supplied, retrieve from them 192 | if len(passwords) > 0 { 193 | if i < len(passwords) { 194 | return passwords[i] 195 | } 196 | return passwords[len(passwords)-1] 197 | } 198 | // Otherwise prompt the user for the password 199 | if prompt != "" { 200 | fmt.Println(prompt) 201 | } 202 | password, err := console.Stdin.PromptPassword("Passphrase: ") 203 | if err != nil { 204 | ethUtils.Fatalf("Failed to read passphrase: %v", err) 205 | } 206 | if confirmation { 207 | confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") 208 | if err != nil { 209 | ethUtils.Fatalf("Failed to read passphrase confirmation: %v", err) 210 | } 211 | if password != confirm { 212 | ethUtils.Fatalf("Passphrases do not match") 213 | } 214 | } 215 | return password 216 | } 217 | 218 | func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, 219 | auth string) accounts.Account { 220 | 221 | fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) 222 | for _, a := range err.Matches { 223 | fmt.Println(" ", a.URL) 224 | } 225 | fmt.Println("Testing your passphrase against all of them...") 226 | var match *accounts.Account 227 | for _, a := range err.Matches { 228 | if err := ks.Unlock(a, auth); err == nil { 229 | match = &a 230 | break 231 | } 232 | } 233 | if match == nil { 234 | ethUtils.Fatalf("None of the listed files could be unlocked.") 235 | } 236 | fmt.Printf("Your passphrase unlocked %s\n", match.URL) 237 | fmt.Println("In order to avoid this warning, remove the following duplicate key files:") 238 | for _, a := range err.Matches { 239 | if a != *match { 240 | fmt.Println(" ", a.URL) 241 | } 242 | } 243 | return *match 244 | } 245 | -------------------------------------------------------------------------------- /cmd/ethermint/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "time" 9 | 10 | "gopkg.in/urfave/cli.v1" 11 | 12 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 13 | "github.com/ethereum/go-ethereum/core" 14 | "github.com/ethereum/go-ethereum/ethdb" 15 | "github.com/ethereum/go-ethereum/log" 16 | "github.com/tendermint/ethermint/cmd/utils" 17 | 18 | emtUtils "github.com/tendermint/ethermint/cmd/utils" 19 | ) 20 | 21 | // nolint: gocyclo 22 | func initCmd(ctx *cli.Context) error { 23 | genesisPath := ctx.Args().First() 24 | genesis, err := emtUtils.ParseGenesisOrDefault(genesisPath) 25 | if err != nil { 26 | ethUtils.Fatalf("genesisJSON err: %v", err) 27 | } 28 | 29 | ethermintDataDir := emtUtils.MakeDataDir(ctx) 30 | 31 | // Step 1: 32 | // If requested, invoke: tendermint init --home ethermintDataDir/tendermint 33 | // See https://github.com/tendermint/ethermint/issues/244 34 | canInvokeTendermintInit := canInvokeTendermint(ctx) 35 | if canInvokeTendermintInit { 36 | tendermintHome := filepath.Join(ethermintDataDir, "tendermint") 37 | tendermintArgs := []string{"init", "--home", tendermintHome} 38 | _, err = invokeTendermint(tendermintArgs...) 39 | if err != nil { 40 | ethUtils.Fatalf("tendermint init error: %v", err) 41 | } 42 | log.Info("successfully invoked `tendermint`", "args", tendermintArgs) 43 | } 44 | 45 | chainDb, err := ethdb.NewLDBDatabase(filepath.Join(ethermintDataDir, 46 | "ethermint/chaindata"), 0, 0) 47 | if err != nil { 48 | ethUtils.Fatalf("could not open database: %v", err) 49 | } 50 | 51 | _, hash, err := core.SetupGenesisBlock(chainDb, genesis) 52 | if err != nil { 53 | ethUtils.Fatalf("failed to write genesis block: %v", err) 54 | } 55 | 56 | log.Info("successfully wrote genesis block and/or chain rule set", "hash", hash) 57 | 58 | // As per https://github.com/tendermint/ethermint/issues/244#issuecomment-322024199 59 | // Let's implicitly add in the respective keystore files 60 | // to avoid manually doing this step: 61 | // $ cp -r $GOPATH/src/github.com/tendermint/ethermint/setup/keystore $(DATADIR) 62 | keystoreDir := filepath.Join(ethermintDataDir, "keystore") 63 | if err := os.MkdirAll(keystoreDir, 0777); err != nil { 64 | ethUtils.Fatalf("mkdirAll keyStoreDir: %v", err) 65 | } 66 | 67 | for filename, content := range keystoreFilesMap { 68 | storeFileName := filepath.Join(keystoreDir, filename) 69 | f, err := os.Create(storeFileName) 70 | if err != nil { 71 | log.Error("create %q err: %v", storeFileName, err) 72 | continue 73 | } 74 | if _, err := f.Write([]byte(content)); err != nil { 75 | log.Error("write content %q err: %v", storeFileName, err) 76 | } 77 | if err := f.Close(); err != nil { 78 | return err 79 | } 80 | } 81 | 82 | return nil 83 | } 84 | 85 | // nolint=lll 86 | var keystoreFilesMap = map[string]string{ 87 | // https://github.com/tendermint/ethermint/blob/edc95f9d47ba1fb7c8161182533b5f5d5c5d619b/setup/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc 88 | // OR 89 | // $GOPATH/src/github.com/ethermint/setup/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc 90 | "UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc": ` 91 | { 92 | "address":"7eff122b94897ea5b0e2a9abf47b86337fafebdc", 93 | "id":"f86a62b4-0621-4616-99af-c4b7f38fcc48","version":3, 94 | "crypto":{ 95 | "cipher":"aes-128-ctr","ciphertext":"19de8a919e2f4cbdde2b7352ebd0be8ead2c87db35fc8e4c9acaf74aaaa57dad", 96 | "cipherparams":{"iv":"ba2bd370d6c9d5845e92fbc6f951c792"}, 97 | "kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c7cc2380a96adc9eb31d20bd8d8a7827199e8b16889582c0b9089da6a9f58e84"}, 98 | "mac":"ff2c0caf051ca15d8c43b6f321ec10bd99bd654ddcf12dd1a28f730cc3c13730" 99 | } 100 | } 101 | `, 102 | } 103 | 104 | // nolint: unparam 105 | func invokeTendermintNoTimeout(args ...string) ([]byte, error) { 106 | return _invokeTendermint(context.TODO(), args...) 107 | } 108 | 109 | func _invokeTendermint(ctx context.Context, args ...string) ([]byte, error) { 110 | log.Info("Invoking `tendermint`", "args", args) 111 | cmd := exec.CommandContext(ctx, "tendermint", args...) 112 | return cmd.CombinedOutput() 113 | } 114 | 115 | // nolint: unparam 116 | func invokeTendermint(args ...string) ([]byte, error) { 117 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 118 | defer cancel() 119 | return _invokeTendermint(ctx, args...) 120 | } 121 | 122 | func canInvokeTendermint(ctx *cli.Context) bool { 123 | return ctx.GlobalBool(utils.WithTendermintFlag.Name) 124 | } 125 | 126 | func tendermintHomeFromEthermint(ctx *cli.Context) string { 127 | ethermintDataDir := emtUtils.MakeDataDir(ctx) 128 | return filepath.Join(ethermintDataDir, "tendermint") 129 | } 130 | -------------------------------------------------------------------------------- /cmd/ethermint/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "gopkg.in/urfave/cli.v1" 8 | 9 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 10 | "github.com/ethereum/go-ethereum/params" 11 | 12 | "github.com/tendermint/ethermint/cmd/utils" 13 | "github.com/tendermint/ethermint/version" 14 | ) 15 | 16 | var ( 17 | // The app that holds all commands and flags. 18 | app = ethUtils.NewApp(version.Version, "the ethermint command line interface") 19 | // flags that configure the go-ethereum node 20 | nodeFlags = []cli.Flag{ 21 | ethUtils.DataDirFlag, 22 | ethUtils.KeyStoreDirFlag, 23 | ethUtils.NoUSBFlag, 24 | // Performance tuning 25 | ethUtils.CacheFlag, 26 | ethUtils.TrieCacheGenFlag, 27 | // Account settings 28 | ethUtils.UnlockedAccountFlag, 29 | ethUtils.PasswordFileFlag, 30 | ethUtils.VMEnableDebugFlag, 31 | // Logging and debug settings 32 | ethUtils.NoCompactionFlag, 33 | // Gas price oracle settings 34 | ethUtils.GpoBlocksFlag, 35 | ethUtils.GpoPercentileFlag, 36 | utils.TargetGasLimitFlag, 37 | // Gas Price 38 | ethUtils.GasPriceFlag, 39 | } 40 | 41 | rpcFlags = []cli.Flag{ 42 | ethUtils.RPCEnabledFlag, 43 | ethUtils.RPCListenAddrFlag, 44 | ethUtils.RPCPortFlag, 45 | ethUtils.RPCCORSDomainFlag, 46 | ethUtils.RPCApiFlag, 47 | ethUtils.IPCDisabledFlag, 48 | ethUtils.WSEnabledFlag, 49 | ethUtils.WSListenAddrFlag, 50 | ethUtils.WSPortFlag, 51 | ethUtils.WSApiFlag, 52 | ethUtils.WSAllowedOriginsFlag, 53 | } 54 | 55 | // flags that configure the ABCI app 56 | ethermintFlags = []cli.Flag{ 57 | utils.TendermintAddrFlag, 58 | utils.ABCIAddrFlag, 59 | utils.ABCIProtocolFlag, 60 | utils.VerbosityFlag, 61 | utils.ConfigFileFlag, 62 | utils.WithTendermintFlag, 63 | } 64 | ) 65 | 66 | func init() { 67 | app.Action = ethermintCmd 68 | app.HideVersion = true 69 | app.Commands = []cli.Command{ 70 | { 71 | Action: initCmd, 72 | Name: "init", 73 | Usage: "init genesis.json", 74 | Description: "Initialize the files", 75 | }, 76 | { 77 | Action: versionCmd, 78 | Name: "version", 79 | Usage: "", 80 | Description: "Print the version", 81 | }, 82 | { 83 | Action: resetCmd, 84 | Name: "unsafe_reset_all", 85 | Usage: "(unsafe) Remove ethermint database", 86 | }, 87 | } 88 | 89 | app.Flags = append(app.Flags, nodeFlags...) 90 | app.Flags = append(app.Flags, rpcFlags...) 91 | app.Flags = append(app.Flags, ethermintFlags...) 92 | 93 | app.Before = func(ctx *cli.Context) error { 94 | if err := utils.Setup(ctx); err != nil { 95 | return err 96 | } 97 | 98 | ethUtils.SetupNetwork(ctx) 99 | 100 | return nil 101 | } 102 | } 103 | 104 | func versionCmd(ctx *cli.Context) error { 105 | fmt.Println("ethermint: ", version.Version) 106 | fmt.Println("go-ethereum: ", params.Version) 107 | return nil 108 | } 109 | 110 | func main() { 111 | if err := app.Run(os.Args); err != nil { 112 | fmt.Fprintln(os.Stderr, err) 113 | os.Exit(1) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /cmd/ethermint/reset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gopkg.in/urfave/cli.v1" 5 | 6 | emtUtils "github.com/tendermint/ethermint/cmd/utils" 7 | ) 8 | 9 | func resetCmd(ctx *cli.Context) error { 10 | return emtUtils.ResetAll(ctx) 11 | } 12 | -------------------------------------------------------------------------------- /cmd/utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/big" 5 | "os" 6 | 7 | cli "gopkg.in/urfave/cli.v1" 8 | 9 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 10 | "github.com/ethereum/go-ethereum/eth" 11 | "github.com/ethereum/go-ethereum/node" 12 | "github.com/ethereum/go-ethereum/params" 13 | 14 | "github.com/tendermint/ethermint/ethereum" 15 | 16 | rpcClient "github.com/tendermint/tendermint/rpc/lib/client" 17 | ) 18 | 19 | const ( 20 | // Client identifier to advertise over the network 21 | clientIdentifier = "ethermint" 22 | // Environment variable for home dir 23 | emHome = "EMHOME" 24 | ) 25 | 26 | var ( 27 | // GenesisTargetGasLimit is the target gas limit of the Genesis block. 28 | // #unstable 29 | GenesisTargetGasLimit = big.NewInt(100000000) 30 | ) 31 | 32 | type ethstatsConfig struct { 33 | URL string `toml:",omitempty"` 34 | } 35 | 36 | type gethConfig struct { 37 | Eth eth.Config 38 | Node node.Config 39 | Ethstats ethstatsConfig 40 | } 41 | 42 | // MakeFullNode creates a full go-ethereum node 43 | // #unstable 44 | func MakeFullNode(ctx *cli.Context) *ethereum.Node { 45 | stack, cfg := makeConfigNode(ctx) 46 | 47 | tendermintLAddr := ctx.GlobalString(TendermintAddrFlag.Name) 48 | if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { 49 | return ethereum.NewBackend(ctx, &cfg.Eth, rpcClient.NewURIClient(tendermintLAddr)) 50 | }); err != nil { 51 | ethUtils.Fatalf("Failed to register the ABCI application service: %v", err) 52 | } 53 | 54 | return stack 55 | } 56 | 57 | func makeConfigNode(ctx *cli.Context) (*ethereum.Node, gethConfig) { 58 | cfg := gethConfig{ 59 | Eth: eth.DefaultConfig, 60 | Node: DefaultNodeConfig(), 61 | } 62 | 63 | ethUtils.SetNodeConfig(ctx, &cfg.Node) 64 | SetEthermintNodeConfig(&cfg.Node) 65 | stack, err := ethereum.New(&cfg.Node) 66 | if err != nil { 67 | ethUtils.Fatalf("Failed to create the protocol stack: %v", err) 68 | } 69 | 70 | ethUtils.SetEthConfig(ctx, &stack.Node, &cfg.Eth) 71 | SetEthermintEthConfig(&cfg.Eth) 72 | 73 | return stack, cfg 74 | } 75 | 76 | // DefaultNodeConfig returns the default configuration for a go-ethereum node 77 | // #unstable 78 | func DefaultNodeConfig() node.Config { 79 | cfg := node.DefaultConfig 80 | cfg.Name = clientIdentifier 81 | cfg.Version = params.Version 82 | cfg.HTTPModules = append(cfg.HTTPModules, "eth") 83 | cfg.WSModules = append(cfg.WSModules, "eth") 84 | cfg.IPCPath = "geth.ipc" 85 | 86 | emHome := os.Getenv(emHome) 87 | if emHome != "" { 88 | cfg.DataDir = emHome 89 | } 90 | 91 | return cfg 92 | } 93 | 94 | // SetEthermintNodeConfig takes a node configuration and applies ethermint specific configuration 95 | // #unstable 96 | func SetEthermintNodeConfig(cfg *node.Config) { 97 | cfg.P2P.MaxPeers = 0 98 | cfg.P2P.NoDiscovery = true 99 | } 100 | 101 | // SetEthermintEthConfig takes a ethereum configuration and applies ethermint specific configuration 102 | // #unstable 103 | func SetEthermintEthConfig(cfg *eth.Config) { 104 | cfg.MaxPeers = 0 105 | cfg.PowFake = true 106 | } 107 | 108 | // MakeDataDir retrieves the currently requested data directory 109 | // #unstable 110 | func MakeDataDir(ctx *cli.Context) string { 111 | path := node.DefaultDataDir() 112 | 113 | emHome := os.Getenv(emHome) 114 | if emHome != "" { 115 | path = emHome 116 | } 117 | 118 | if ctx.GlobalIsSet(ethUtils.DataDirFlag.Name) { 119 | path = ctx.GlobalString(ethUtils.DataDirFlag.Name) 120 | } 121 | 122 | if path == "" { 123 | ethUtils.Fatalf("Cannot determine default data directory, please set manually (--datadir)") 124 | } 125 | 126 | return path 127 | } 128 | -------------------------------------------------------------------------------- /cmd/utils/config_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | "testing" 7 | 8 | "gopkg.in/urfave/cli.v1" 9 | 10 | "github.com/ethereum/go-ethereum/node" 11 | ) 12 | 13 | // we use EMHOME env variable if don't set datadir flag 14 | func TestEMHome(t *testing.T) { 15 | // set env variable 16 | emHomedir := "/tmp/dir1" 17 | os.Setenv(emHome, emHomedir) // nolint: errcheck 18 | 19 | // context with empty flag set 20 | context := getContextNoFlag() 21 | 22 | _, config := makeConfigNode(context) 23 | 24 | if config.Node.DataDir != emHomedir { 25 | t.Errorf("DataDir is wrong: %s", config.Node.DataDir) 26 | } 27 | } 28 | 29 | func TestEmHomeDataDir(t *testing.T) { 30 | // set env variable 31 | emHomedir := "/tmp/dir1" 32 | os.Setenv(emHome, emHomedir) // nolint: errcheck 33 | 34 | // context with datadir flag 35 | dir := "/tmp/dir2" 36 | context := getContextDataDirFlag(dir) 37 | 38 | _, config := makeConfigNode(context) 39 | 40 | if config.Node.DataDir != dir { 41 | t.Errorf("DataDir is wrong: %s", config.Node.DataDir) 42 | } 43 | } 44 | 45 | // init cli.context with empty flag set 46 | func getContextNoFlag() *cli.Context { 47 | set := flag.NewFlagSet("test", 0) 48 | globalSet := flag.NewFlagSet("test", 0) 49 | 50 | globalCtx := cli.NewContext(nil, globalSet, nil) 51 | ctx := cli.NewContext(nil, set, globalCtx) 52 | 53 | return ctx 54 | } 55 | 56 | // nolint: unparam 57 | func getContextDataDirFlag(dir string) *cli.Context { 58 | set := flag.NewFlagSet("test", 0) 59 | globalSet := flag.NewFlagSet("test", 0) 60 | globalSet.String("datadir", node.DefaultDataDir(), "doc") 61 | 62 | globalCtx := cli.NewContext(nil, globalSet, nil) 63 | ctx := cli.NewContext(nil, set, globalCtx) 64 | 65 | globalSet.Parse([]string{"--datadir", dir}) // nolint: errcheck 66 | 67 | return ctx 68 | } 69 | -------------------------------------------------------------------------------- /cmd/utils/flags.go: -------------------------------------------------------------------------------- 1 | // nolint=lll 2 | package utils 3 | 4 | import ( 5 | "gopkg.in/urfave/cli.v1" 6 | ) 7 | 8 | var ( 9 | // ---------------------------- 10 | // ABCI Flags 11 | 12 | // TendermintAddrFlag is the address that ethermint will use to connect to the tendermint core node 13 | // #stable - 0.4.0 14 | TendermintAddrFlag = cli.StringFlag{ 15 | Name: "tendermint_addr", 16 | Value: "tcp://localhost:46657", 17 | Usage: "This is the address that ethermint will use to connect to the tendermint core node. Please provide a port.", 18 | } 19 | 20 | // ABCIAddrFlag is the address that ethermint will use to listen to incoming ABCI connections 21 | // #stable - 0.4.0 22 | ABCIAddrFlag = cli.StringFlag{ 23 | Name: "abci_laddr", 24 | Value: "tcp://0.0.0.0:46658", 25 | Usage: "This is the address that the ABCI server will use to listen to incoming connection from tendermint core.", 26 | } 27 | 28 | // ABCIProtocolFlag defines whether GRPC or SOCKET should be used for the ABCI connections 29 | // #stable - 0.4.0 30 | ABCIProtocolFlag = cli.StringFlag{ 31 | Name: "abci_protocol", 32 | Value: "socket", 33 | Usage: "socket | grpc", 34 | } 35 | 36 | // VerbosityFlag defines the verbosity of the logging 37 | // #unstable 38 | VerbosityFlag = cli.IntFlag{ 39 | Name: "verbosity", 40 | Value: 3, 41 | Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail", 42 | } 43 | 44 | // ConfigFileFlag defines the path to a TOML config for go-ethereum 45 | // #unstable 46 | ConfigFileFlag = cli.StringFlag{ 47 | Name: "config", 48 | Usage: "TOML configuration file", 49 | } 50 | 51 | // TargetGasLimitFlag defines gas limit of the Genesis block 52 | // #unstable 53 | TargetGasLimitFlag = cli.Uint64Flag{ 54 | Name: "targetgaslimit", 55 | Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", 56 | Value: GenesisTargetGasLimit.Uint64(), 57 | } 58 | 59 | // WithTendermintFlag asks to start Tendermint 60 | // `tendermint init` and `tendermint node` when `ethermint init` 61 | // and `ethermint` are invoked respectively. 62 | WithTendermintFlag = cli.BoolFlag{ 63 | Name: "with-tendermint", 64 | Usage: "If set, it will invoke `tendermint init` and `tendermint node` " + 65 | "when `ethermint init` and `ethermint` are invoked respectively", 66 | } 67 | ) 68 | -------------------------------------------------------------------------------- /cmd/utils/log.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "gopkg.in/urfave/cli.v1" 8 | 9 | colorable "github.com/mattn/go-colorable" 10 | 11 | "github.com/ethereum/go-ethereum/log" 12 | "github.com/ethereum/go-ethereum/log/term" 13 | 14 | tmlog "github.com/tendermint/tmlibs/log" 15 | ) 16 | 17 | var glogger *log.GlogHandler 18 | 19 | func init() { 20 | usecolor := term.IsTty(os.Stderr.Fd()) && os.Getenv("TERM") != "dumb" 21 | output := io.Writer(os.Stderr) 22 | if usecolor { 23 | output = colorable.NewColorableStderr() 24 | } 25 | glogger = log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor))) 26 | } 27 | 28 | // Setup sets up the logging infrastructure 29 | // #unstable 30 | func Setup(ctx *cli.Context) error { 31 | glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) 32 | log.Root().SetHandler(glogger) 33 | 34 | return nil 35 | } 36 | 37 | // --------------------------- 38 | // EthermintLogger - wraps the logger in tmlibs 39 | 40 | // Interface assertions 41 | var _ tmlog.Logger = (*ethermintLogger)(nil) 42 | 43 | type ethermintLogger struct { 44 | keyvals []interface{} 45 | } 46 | 47 | // EthermintLogger returns a new instance of an ethermint logger. With() should 48 | // be called upon the returned instance to set default keys 49 | // #unstable 50 | func EthermintLogger() tmlog.Logger { 51 | logger := ethermintLogger{keyvals: make([]interface{}, 0)} 52 | return logger 53 | } 54 | 55 | // Debug proxies everything to the go-ethereum logging facilities 56 | // #unstable 57 | func (l ethermintLogger) Debug(msg string, ctx ...interface{}) { 58 | ctx = append(l.keyvals, ctx...) 59 | log.Debug(msg, ctx...) 60 | } 61 | 62 | // Info proxies everything to the go-ethereum logging facilities 63 | // #unstable 64 | func (l ethermintLogger) Info(msg string, ctx ...interface{}) { 65 | ctx = append(l.keyvals, ctx...) 66 | log.Info(msg, ctx...) 67 | } 68 | 69 | // Error proxies everything to the go-ethereum logging facilities 70 | // #unstable 71 | func (l ethermintLogger) Error(msg string, ctx ...interface{}) { 72 | ctx = append(l.keyvals, ctx...) 73 | log.Error(msg, ctx...) 74 | } 75 | 76 | // With proxies everything to the go-ethereum logging facilities 77 | // #unstable 78 | func (l ethermintLogger) With(ctx ...interface{}) tmlog.Logger { 79 | l.keyvals = append(l.keyvals, ctx...) 80 | 81 | return l 82 | } 83 | -------------------------------------------------------------------------------- /cmd/utils/parse.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io/ioutil" 7 | "os" 8 | "reflect" 9 | 10 | "github.com/ethereum/go-ethereum/core" 11 | ) 12 | 13 | // defaultGenesisBlob is the JSON representation of the default 14 | // genesis file in $GOPATH/src/github.com/tendermint/ethermint/setup/genesis.json 15 | // nolint=lll 16 | var defaultGenesisBlob = []byte(` 17 | { 18 | "config": { 19 | "chainId": 15, 20 | "homesteadBlock": 0, 21 | "eip155Block": 0, 22 | "eip158Block": 0 23 | }, 24 | "nonce": "0xdeadbeefdeadbeef", 25 | "timestamp": "0x00", 26 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 27 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 28 | "difficulty": "0x40", 29 | "gasLimit": "0x8000000", 30 | "alloc": { 31 | "0x7eff122b94897ea5b0e2a9abf47b86337fafebdc": { "balance": "10000000000000000000000000000000000" }, 32 | "0xc6713982649D9284ff56c32655a9ECcCDA78422A": { "balance": "10000000000000000000000000000000000" } 33 | } 34 | }`) 35 | 36 | var blankGenesis = new(core.Genesis) 37 | 38 | var errBlankGenesis = errors.New("could not parse a valid/non-blank Genesis") 39 | 40 | // ParseGenesisOrDefault tries to read the content from provided 41 | // genesisPath. If the path is empty or doesn't exist, it will 42 | // use defaultGenesisBytes as the fallback genesis source. Otherwise, 43 | // it will open that path and if it encounters an error that doesn't 44 | // satisfy os.IsNotExist, it returns that error. 45 | func ParseGenesisOrDefault(genesisPath string) (*core.Genesis, error) { 46 | var genesisBlob = defaultGenesisBlob[:] 47 | if len(genesisPath) > 0 { 48 | blob, err := ioutil.ReadFile(genesisPath) 49 | if err != nil && !os.IsNotExist(err) { 50 | return nil, err 51 | } 52 | if len(blob) >= 2 { // Expecting atleast "{}" 53 | genesisBlob = blob 54 | } 55 | } 56 | 57 | genesis := new(core.Genesis) 58 | if err := json.Unmarshal(genesisBlob, genesis); err != nil { 59 | return nil, err 60 | } 61 | 62 | if reflect.DeepEqual(blankGenesis, genesis) { 63 | return nil, errBlankGenesis 64 | } 65 | 66 | return genesis, nil 67 | } 68 | -------------------------------------------------------------------------------- /cmd/utils/parse_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "math/big" 8 | "math/rand" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | 13 | ethCommon "github.com/ethereum/go-ethereum/common" 14 | "github.com/ethereum/go-ethereum/core" 15 | ) 16 | 17 | var defaultGenesis = func() *core.Genesis { 18 | g := new(core.Genesis) 19 | if err := json.Unmarshal(defaultGenesisBlob, g); err != nil { 20 | log.Fatalf("parsing defaultGenesis: %v", err) 21 | } 22 | return g 23 | }() 24 | 25 | func bigString(s string) *big.Int { // nolint: unparam 26 | b, _ := big.NewInt(0).SetString(s, 10) 27 | return b 28 | } 29 | 30 | var genesis1 = &core.Genesis{ 31 | Difficulty: big.NewInt(0x40), 32 | GasLimit: 0x8000000, 33 | Alloc: core.GenesisAlloc{ 34 | ethCommon.HexToAddress("0x7eff122b94897ea5b0e2a9abf47b86337fafebdc"): { 35 | Balance: bigString("10000000000000000000000000000000000"), 36 | }, 37 | ethCommon.HexToAddress("0xc6713982649D9284ff56c32655a9ECcCDA78422A"): { 38 | Balance: bigString("10000000000000000000000000000000000"), 39 | }, 40 | }, 41 | } 42 | 43 | func TestParseGenesisOrDefault(t *testing.T) { 44 | tests := [...]struct { 45 | path string 46 | want *core.Genesis 47 | wantErr bool 48 | }{ 49 | 0: {path: "", want: defaultGenesis}, 50 | 1: {want: defaultGenesis}, 51 | 2: {path: fmt.Sprintf("non-existent-%d", rand.Int()), want: defaultGenesis}, 52 | 3: {path: "./testdata/blank-genesis.json", want: defaultGenesis}, 53 | 4: {path: "./testdata/genesis1.json", want: genesis1}, 54 | 5: {path: "./testdata/non-genesis.json", wantErr: true}, 55 | } 56 | 57 | for i, tt := range tests { 58 | gen, err := ParseGenesisOrDefault(tt.path) 59 | if tt.wantErr { 60 | assert.NotNil(t, err, "#%d: cannot be nil", i) 61 | continue 62 | } 63 | 64 | if err != nil { 65 | t.Errorf("#%d: path=%q unexpected error: %v", i, tt.path, err) 66 | continue 67 | } 68 | 69 | assert.NotEqual(t, blankGenesis, gen, true, "#%d: expecting a non-blank", i) 70 | assert.Equal(t, gen, tt.want, "#%d: expected them to be the same", i) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /cmd/utils/reset_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/ethereum/go-ethereum/core" 10 | "github.com/ethereum/go-ethereum/ethdb" 11 | ) 12 | 13 | // 1. set data dir via EMHOME env variable 14 | // 2. init genesis 15 | // 3. reset all 16 | // 4. check dir is empty 17 | func TestResetAll(t *testing.T) { 18 | // setup temp data dir 19 | tempDatadir, err := ioutil.TempDir("", "ethermint_test") 20 | if err != nil { 21 | t.Error("unable to create temporary datadir") 22 | } 23 | defer os.RemoveAll(tempDatadir) // nolint: errcheck 24 | 25 | // set EMHOME env variable 26 | if err = os.Setenv(emHome, tempDatadir); err != nil { 27 | t.Errorf("could not set env: %v", err) 28 | } 29 | defer func() { 30 | if err = os.Unsetenv(emHome); err != nil { 31 | t.Errorf("could not unset env: %v", err) 32 | } 33 | }() 34 | 35 | // context with empty flag set 36 | context := getContextNoFlag() 37 | 38 | dataDir := filepath.Join(MakeDataDir(context), "ethermint/chaindata") 39 | 40 | chainDb, err := ethdb.NewLDBDatabase(dataDir, 0, 0) 41 | if err != nil { 42 | t.Errorf("could not open database: %v", err) 43 | } 44 | 45 | // setup genesis 46 | _, _, err = core.SetupGenesisBlock(chainDb, core.DefaultTestnetGenesisBlock()) 47 | if err != nil { 48 | t.Errorf("failed to write genesis block: %v", err) 49 | } 50 | 51 | // check dir exists 52 | if _, err = os.Stat(dataDir); err != nil { 53 | t.Errorf("database doesn't exist: %v", err) 54 | 55 | } 56 | 57 | // clear 58 | if err = ResetAll(context); err != nil { 59 | t.Errorf("Failed to remove ethermint home directory: %+v", err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /cmd/utils/testdata/blank-genesis.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/cmd/utils/testdata/blank-genesis.json -------------------------------------------------------------------------------- /cmd/utils/testdata/genesis1.json: -------------------------------------------------------------------------------- 1 | { 2 | "difficulty": "0x40", 3 | "gasLimit": "0x8000000", 4 | "alloc": { 5 | "0x7eff122b94897ea5b0e2a9abf47b86337fafebdc": { "balance": "10000000000000000000000000000000000" }, 6 | "0xc6713982649D9284ff56c32655a9ECcCDA78422A": { "balance": "10000000000000000000000000000000000" } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /cmd/utils/testdata/non-genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "tendermint": true, 3 | "introduction": "https://tendermint.com" 4 | } 5 | -------------------------------------------------------------------------------- /cmd/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "os/user" 7 | "path/filepath" 8 | "runtime" 9 | 10 | "gopkg.in/urfave/cli.v1" 11 | 12 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 13 | "github.com/ethereum/go-ethereum/log" 14 | 15 | "github.com/tendermint/ethermint/ethereum" 16 | ) 17 | 18 | // StartNode will start up the node. 19 | func StartNode(stack *ethereum.Node) { 20 | if err := stack.Start(); err != nil { 21 | ethUtils.Fatalf("Error starting protocol stack: %v", err) 22 | } 23 | go func() { 24 | sigc := make(chan os.Signal, 1) 25 | signal.Notify(sigc, os.Interrupt) 26 | defer signal.Stop(sigc) 27 | <-sigc 28 | log.Info("Got interrupt, shutting down...") 29 | go stack.Stop() // nolint: errcheck 30 | for i := 10; i > 0; i-- { 31 | <-sigc 32 | if i > 1 { 33 | log.Warn("Already shutting down, interrupt more to panic.", "times", i-1) 34 | } 35 | } 36 | }() 37 | } 38 | 39 | // HomeDir returns the user's home most likely home directory 40 | // #unstable 41 | func HomeDir() string { 42 | if home := os.Getenv("HOME"); home != "" { 43 | return home 44 | } 45 | if usr, err := user.Current(); err == nil { 46 | return usr.HomeDir 47 | } 48 | return "" 49 | } 50 | 51 | // DefaultDataDir tries to guess the default directory for ethermint data 52 | // #unstable 53 | func DefaultDataDir() string { 54 | // Try to place the data folder in the user's home dir 55 | home := HomeDir() 56 | if home != "" { 57 | if runtime.GOOS == "darwin" { 58 | return filepath.Join(home, "Library", "Ethermint") 59 | } else if runtime.GOOS == "windows" { 60 | return filepath.Join(home, "AppData", "Roaming", "Ethermint") 61 | } else { 62 | return filepath.Join(home, ".ethermint") 63 | } 64 | } 65 | // As we cannot guess a stable location, return empty and handle later 66 | return "" 67 | } 68 | 69 | // ResetAll will remove the data directory. 70 | func ResetAll(ctx *cli.Context) error { 71 | dbDir := filepath.Join(MakeDataDir(ctx), "ethermint") 72 | if err := os.RemoveAll(dbDir); err != nil { 73 | log.Debug("Could not reset ethermint. Failed to remove %+v", dbDir) 74 | return err 75 | } 76 | 77 | log.Info("Successfully removed all data", "dir", dbDir) 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = Ethermint 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/_static/.gitkeep -------------------------------------------------------------------------------- /docs/architecture/inter-blockchain-communication.rst: -------------------------------------------------------------------------------- 1 | .. _inter-blockchain-communication: 2 | 3 | IBC Support in Ethermint 4 | ======================== 5 | 6 | Goals 7 | ----- 8 | 9 | * Ethermint can send and receive native and ERC20 tokens via IBC 10 | * No changes to native Ethereum transaction format 11 | * Use cosmos-sdk implementation of IBC 12 | 13 | Design 14 | ------ 15 | 16 | * A native contract at address 0x00...000494243 (ie. "IBC") 17 | * Contains its own independent merkle IAVL tree 18 | * Hash of the IAVL tree is appended to ethereum block hash in return value of ABCI Commit message 19 | * Contract has functions for: 20 | 21 | * registering a blockchain (stored in iavl tree) 22 | * updating blockchain headers (stored in iavl tree) 23 | * sending outgoing IBC (stored in iavl tree) 24 | * receiving incoming IBC (validated against data in iavl tree) 25 | 26 | * Contract has the ability to inflate token supply and send to ethereum accounts in the trie 27 | * Receiving: 28 | 29 | * serialized IBC packet received in transaction data 30 | * deserialize, validate against known state of chain 31 | * if valid, new tokens created and sent to destination address 32 | * NOTE ^ we need to be able to inflate both native token and ERC20 tokens! 33 | * Sending: 34 | 35 | * send function is called by some ethereum account with tokens 36 | * IBC packet is formed and serialized and stored in the iavl tree 37 | 38 | Additional Notes 39 | ---------------- 40 | 41 | We want to support more general extensions to Ethermint; for instance, a native contract that handles validators. 42 | If we reuse cosmos-sdk libs, we may want to use just one IAVL tree for all extensions. 43 | Thus, all native contracts would be accounts in the one exstension IAVL tree, and this one trees' root would be appended to the Tendermint AppHash. 44 | -------------------------------------------------------------------------------- /docs/architecture/peg-zones.rst: -------------------------------------------------------------------------------- 1 | .. _peg-zones.rst: 2 | 3 | Ethereum Peg Zones 4 | ================== 5 | **It is paramount that every CETH is backed by an ETH because otherwise, speculators will immediately attack the peg.** 6 | 7 | The goal of the `Ethereum <./future-architecture.html>`__ peg zone is to provide a way for people to exchange ETH into CETH (COSMOS ETH) and vice versa. 8 | The implementation should happen within smart contracts written in solidity. Implementing the peg zone within the Ethermint codebase is dangerous because it will make it less flexible to future changes and will lead to greater divergence of the codebase from its upstream base. 9 | 10 | Process 11 | ------- 12 | 13 | Sending Ether to Cether (Ethereum -> Ethermint) 14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 15 | 16 | To transfer money from ethereum to ethermint you have to send ether and a destination address to the exchange smart contract on ethereum. This smart contract will raise an event when the transfer function is invoked. Every validator 17 | that supports ethermint will also run a full ethereum node that is connected to the mainnet. This node will listen for events on the smart contract and send data of the event to ethermint. Ethermint will then receive the amount and 18 | destination address and will mint new CETH (through a reward strategy) for the specified destination address. 19 | 20 | Sending Cether to Ether (Ethermint -> Ethereum) 21 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 22 | 23 | To transfer money from ethermint to ethereum you have to send cether and a destination address to the exchange smart contract on ethermint. This smart contract will raise an event when the transfer function is invoked. It will burn 24 | any CETH that it has received. Ethermint listens for these events. Once an event is received, ethermint will create, sign and broadcast a transaction that invokes the release function on the ethereum smart contract. In this function 25 | call it will include the proof and the signatures. The smart contract on ethereum will verify the proof and signatures and conditionally release money to the destination address. 26 | 27 | Design 28 | ------ 29 | 30 | Every validator runs tendermint, ethermint and an ethereum node. Ethermint has a `--peg` flag, which will cause it to also act as a pegzone. 31 | The ethereum node is configured to send a notification to ethermint every time an event 32 | is raised by the exchange contract on ethereum. This notification causes ethermint to mint new CETH out of thin air through its reward strategy. 33 | The ethermint smart contract raises an event when it receives a deposit. After receiving a deposit it burns all deposited CETH. Ethermint listens for this event in a go-routine and after receiving the notification it will create, 34 | sign and broadcast a transaction which invokes the release function on the ethereum smart contract. The smart contract on ethereum then verifies the signatures and the proof and only then releases the ETH to the destination address. 35 | 36 | Components 37 | ---------- 38 | 39 | Ethereum Full Node 40 | ^^^^^^^^^^^^^^^^^^ 41 | 42 | * Sends notification to ethermint when an event is raised by a specified smart contract on ethereum 43 | 44 | Ethereum Smart Contract 45 | ^^^^^^^^^^^^^^^^^^^^^^^ 46 | *Tendermint light client implemented in Solidity* 47 | 48 | * Locks up ETH 49 | * Triggers event upon receiving ETH 50 | * Is a light client for tendermint and verifies the calls to its release function 51 | 52 | Ethermint Smart Contract 53 | ^^^^^^^^^^^^^^^^^^^^^^^^ 54 | 55 | * Burns CETH 56 | * Triggers event upon receiving CETH 57 | 58 | Ethermint 59 | ^^^^^^^^^ 60 | 61 | * Responds to notifications from ethereum full node by minting fresh CETH 62 | * Sends transaction that invoke release function on ethereum 63 | 64 | Economic Incentive 65 | ------------------ 66 | 67 | * Ethermint can take a percentage of the CETH is a transaction fee. 68 | * Ethermint can take a percentage of the ETH when releasing them. 69 | 70 | Questions 71 | --------- 72 | 73 | * How does a validator look that runs ethermint and basecoin at the same time? 74 | * Are the economic incentives good enough and can there be a market mechanism around establishing the correct percentage cut? 75 | * Is CETH used to pay for execution of the ethermint EVM? 76 | * What will happen if speculators attack the peg zone, because they believe that 1 CETH is not worth exactly 1 ETH? 77 | * Can there be a native currency on Ethermint that is not pegged against Ethereum? 78 | * What is the market mechanism behind the pricing of relaying a transaction? 79 | 80 | * Maybe there is none and the fee is set as a percentage by the COSMOS validators 81 | 82 | * How will IBC work in relation to ethermint? 83 | 84 | Invariants 85 | ---------- 86 | 87 | * The amount of CETH in circulation is equal to the amount of ETH held by the smart contract on the Ethereum chain 88 | -------------------------------------------------------------------------------- /docs/assets/contract-hash-remix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/assets/contract-hash-remix.png -------------------------------------------------------------------------------- /docs/assets/ethermint-default-address-remix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/assets/ethermint-default-address-remix.png -------------------------------------------------------------------------------- /docs/assets/ethermint-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/assets/ethermint-logo.png -------------------------------------------------------------------------------- /docs/assets/mist-ethermint-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/assets/mist-ethermint-1.png -------------------------------------------------------------------------------- /docs/assets/remix-create-contract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/assets/remix-create-contract.png -------------------------------------------------------------------------------- /docs/assets/remix-ide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint_abci/c0f67298e7a01b3d91a09e8804e7943790b4b66e/docs/assets/remix-ide.png -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Ethermint documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Aug 7 04:55:09 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | import sphinx_rtd_theme 24 | 25 | 26 | # -- General configuration ------------------------------------------------ 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | # 30 | # needs_sphinx = '1.0' 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = [] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix(es) of source filenames. 41 | # You can specify multiple suffix as a list of string: 42 | # 43 | source_suffix = ['.rst', '.md'] 44 | # source_suffix = '.rst' 45 | 46 | # The master toctree document. 47 | master_doc = 'index' 48 | 49 | # General information about the project. 50 | project = u'Ethermint' 51 | copyright = u'2017, Tendermint' 52 | author = u'Tendermint' 53 | 54 | # The version info for the project you're documenting, acts as replacement for 55 | # |version| and |release|, also used in various other places throughout the 56 | # built documents. 57 | # 58 | # The short X.Y version. 59 | version = u'' 60 | # The full version, including alpha/beta/rc tags. 61 | release = u'' 62 | 63 | # The language for content autogenerated by Sphinx. Refer to documentation 64 | # for a list of supported languages. 65 | # 66 | # This is also used if you do content translation via gettext catalogs. 67 | # Usually you set "language" from the command line for these cases. 68 | language = None 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | # This patterns also effect to html_static_path and html_extra_path 73 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'to-sort'] 74 | 75 | # The name of the Pygments (syntax highlighting) style to use. 76 | pygments_style = 'sphinx' 77 | 78 | # If true, `todo` and `todoList` produce output, else they produce nothing. 79 | todo_include_todos = False 80 | 81 | 82 | # -- Options for HTML output ---------------------------------------------- 83 | 84 | # The theme to use for HTML and HTML Help pages. See the documentation for 85 | # a list of builtin themes. 86 | # 87 | html_theme = 'sphinx_rtd_theme' 88 | # html_theme = 'alabaster' 89 | 90 | # Theme options are theme-specific and customize the look and feel of a theme 91 | # further. For a list of options available for each theme, see the 92 | # documentation. 93 | # 94 | # html_theme_options = {} 95 | 96 | # Add any paths that contain custom static files (such as style sheets) here, 97 | # relative to this directory. They are copied after the builtin static files, 98 | # so a file named "default.css" will overwrite the builtin "default.css". 99 | html_static_path = ['_static'] 100 | 101 | # Custom sidebar templates, must be a dictionary that maps document names 102 | # to template names. 103 | # 104 | # This is required for the alabaster theme 105 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 106 | html_sidebars = { 107 | '**': [ 108 | 'about.html', 109 | 'navigation.html', 110 | 'relations.html', # needs 'show_related': True theme option to display 111 | 'searchbox.html', 112 | 'donate.html', 113 | ] 114 | } 115 | 116 | 117 | # -- Options for HTMLHelp output ------------------------------------------ 118 | 119 | # Output file base name for HTML help builder. 120 | htmlhelp_basename = 'Ethermintdoc' 121 | 122 | 123 | # -- Options for LaTeX output --------------------------------------------- 124 | 125 | latex_elements = { 126 | # The paper size ('letterpaper' or 'a4paper'). 127 | # 128 | # 'papersize': 'letterpaper', 129 | 130 | # The font size ('10pt', '11pt' or '12pt'). 131 | # 132 | # 'pointsize': '10pt', 133 | 134 | # Additional stuff for the LaTeX preamble. 135 | # 136 | # 'preamble': '', 137 | 138 | # Latex figure (float) alignment 139 | # 140 | # 'figure_align': 'htbp', 141 | } 142 | 143 | # Grouping the document tree into LaTeX files. List of tuples 144 | # (source start file, target name, title, 145 | # author, documentclass [howto, manual, or own class]). 146 | latex_documents = [ 147 | (master_doc, 'Ethermint.tex', u'Ethermint Documentation', 148 | u'Adrian Brink, Ethan Buchmann, Jae Kwon', 'manual'), 149 | ] 150 | 151 | 152 | # -- Options for manual page output --------------------------------------- 153 | 154 | # One entry per manual page. List of tuples 155 | # (source start file, name, description, authors, manual section). 156 | man_pages = [ 157 | (master_doc, 'ethermint', u'Ethermint Documentation', 158 | [author], 1) 159 | ] 160 | 161 | 162 | # -- Options for Texinfo output ------------------------------------------- 163 | 164 | # Grouping the document tree into Texinfo files. List of tuples 165 | # (source start file, target name, title, author, 166 | # dir menu entry, description, category) 167 | texinfo_documents = [ 168 | (master_doc, 'Ethermint', u'Ethermint Documentation', 169 | author, 'Ethermint', 'One line description of project.', 170 | 'Miscellaneous'), 171 | ] 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/getting-started/getting-started.rst: -------------------------------------------------------------------------------- 1 | .. _getting-started: 2 | 3 | Getting Started 4 | =============== 5 | 6 | Running Ethermint requires both ``ethermint`` and ``tendermint`` installed. Review the installation instructions below: 7 | 8 | Setup Ethermint 9 | --------------- 10 | 11 | To get started, you need to initialise the genesis block for tendermint core and go-ethereum. We provide initialisation 12 | files with reasonable defaults and money allocated into a predefined account. If you installed from binary or docker 13 | please download `the default files here `_. 14 | 15 | You can choose where to store the ethermint files with ``--datadir``. For this guide, we will use ``~/.ethermint``, which is a reasonable default in most cases. 16 | 17 | Before you can run ethermint you need to initialise tendermint and ethermint with their respective genesis states. 18 | Please switch into the folder where you have the initialisation files. If you installed from source you can just follow 19 | these instructions. 20 | 21 | :: 22 | 23 | tendermint init --home ~/.ethermint/tendermint 24 | 25 | ethermint --datadir ~/.ethermint init 26 | 27 | Run Ethermint 28 | ------------- 29 | 30 | To execute ethermint we need to start two processes. The first one is for tendermint, which handles the P2P 31 | communication as well as the consensus process, while the second one is actually ethermint, which provides the 32 | go-ethereum functionality. 33 | 34 | :: 35 | 36 | tendermint --home ~/.ethermint/tendermint node 37 | 38 | ethermint --datadir ~/.ethermint --rpc --rpcaddr=0.0.0.0 --ws --wsaddr=0.0.0.0 --rpcapi eth,net,web3,personal,admin 39 | 40 | The **password** for the default account is *1234*, which we'll need below. 41 | 42 | For development purposes, you can enable tendermint to only produce blocks when there are transactions. Add to the 43 | ``~/.ethermint/tendermint/config.toml`` file: 44 | 45 | :: 46 | 47 | [consensus] 48 | create_empty_blocks = false 49 | 50 | # create_empty_blocks_interval = 5 51 | 52 | Notice the alternative feature which allows you to set an empty block interval. However, blocks will be produced without waiting 53 | for the interval if there are transactions. 54 | 55 | Connect to Geth 56 | --------------- 57 | 58 | First, install `geth `_, then: 59 | 60 | :: 61 | 62 | geth attach http://localhost:8545 63 | 64 | which drops you into a web3 console. 65 | 66 | Now we have access to all the functions from web3 at our fingertips, try: 67 | 68 | :: 69 | 70 | > eth 71 | 72 | to see all the options. 73 | 74 | Let's start by looking at our default accounts with: 75 | 76 | :: 77 | 78 | > eth.accounts 79 | 80 | There will be only one account and it should match the account given with: 81 | 82 | :: 83 | 84 | cat ~/.ethermint/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc 85 | 86 | and note that the last portion of that file name is your account (except for ``0x``) as with the first field of the file itself. 87 | 88 | We can also view the block number: 89 | 90 | :: 91 | 92 | > eth.blockNumber 93 | 94 | which will be in sync with the logs of ``ethermint``: 95 | 96 | :: 97 | 98 | INFO [08-07|22:32:30] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=1.705ms mgasps=0.000 number=248 hash=7fbd05…a231a8 99 | INFO [08-07|22:32:31] BeginBlock 100 | INFO [08-07|22:32:31] EndBlock 101 | INFO [08-07|22:32:31] Commit 102 | INFO [08-07|22:32:31] Committing block stateHash=c0d88d…3a474a blockHash=83b9df…5fc4cb 103 | 104 | and of ``tendermint``: 105 | 106 | :: 107 | 108 | I[08-08|02:32:30.000] Executed block module=state height=248 validTxs=0 invalidTxs=0 109 | I[08-08|02:32:30.042] Committed state module=state height=248 txs=0 hash=A524F17E9E1EDE3438B2B8DB231B719BCA8A38B5872C48E43A6B29BB189FA749 110 | 111 | note that the block height is increasing approximately once per second. Next, we can see the balance of our accounts with: 112 | 113 | :: 114 | 115 | > eth.getBalance("0x7eff122b94897ea5b0e2a9abf47b86337fafebdc") 116 | 117 | which should ``1e+34`` if you haven't yet sent a transaction or deployed a contract 118 | 119 | Before deploying a contract, we must unlock the key. First, note that it is locked: 120 | 121 | :: 122 | 123 | > web3.personal 124 | 125 | and you'll see ``status: "Locked"`` a few lines down. But wait, why did we go from ``eth`` to ``web3``? We're not sure but that's how it works so follow along. 126 | 127 | :: 128 | 129 | > web3.personal.unlockAccount("0x7eff122b94897ea5b0e2a9abf47b86337fafebdc", "1234", 100000) 130 | 131 | where the first argument is your account, the second your password (see above), and the third - the amount of time in seconds to keep key unlocked. 132 | 133 | 134 | Now we can deploy a contract. Since ``eth.compile`` wasn't quite working for me, we can use `browser solidity `_. Let's use a short contract like: 135 | 136 | :: 137 | 138 | pragma solidity ^0.4.0; 139 | 140 | contract Test { 141 | function double(int a) constant returns(int) { 142 | return 2*a; 143 | } 144 | } 145 | 146 | then look for the ``Contract details (bytecode, interface etc.)`` on the right sidebar. Copy the code from the "Web3 deploy" section, which will be similar to: 147 | 148 | :: 149 | 150 | var browser_double_sol_testContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"a","type":"int256"}],"name":"double","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"}]); 151 | var browser_double_sol_test = browser_double_sol_testContract.new( 152 | { 153 | from: web3.eth.accounts[0], 154 | data: '0x6060604052341561000f57600080fd5b5b60ab8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820c5fd101c8bd62761d1803c865fd4af5c57f3752e6212d7ccebd5b4a23fcd23180029', 155 | gas: '4300000' 156 | }, function (e, contract){ 157 | console.log(e, contract); 158 | if (typeof contract.address !== 'undefined') { 159 | console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); 160 | } 161 | }) 162 | 163 | and paste it directly in the ``geth`` console. A handful of dots will accrue on each line but the code should run and deploy the contract. You'll see something like: 164 | 165 | :: 166 | 167 | null [object Object] 168 | undefined 169 | Contract mined! address: 0xab119259ff325f845f8ce59de8ccf63e597a74cd transactionHash: 0xf3031c975ef55d14a0382df748b3e66a22c61922b80075ee244c493db5f80c5c 170 | 171 | which has the information you need to call this contract on the chain. 172 | 173 | From the ``ethermint`` logs we'll see a big stream of data while the ``tendermint`` logs will show the ``validTxs`` and ``txs`` fields increase from 0 to 1. 174 | 175 | That's it, you've deployed a contract to ethermint! Next, we can call a contract or setup a testnet. 176 | -------------------------------------------------------------------------------- /docs/getting-started/install.rst: -------------------------------------------------------------------------------- 1 | Install Ethermint 2 | ================= 3 | 4 | Via Package Manager 5 | -------------------- 6 | 7 | Note: these commands will also install ``tendermint`` as a required binary. 8 | 9 | Linux 10 | ~~~~~ 11 | 12 | :: 13 | 14 | apt-get install ethermint 15 | 16 | MacOS 17 | ~~~~~ 18 | 19 | :: 20 | 21 | brew install ethermint 22 | 23 | Windows 24 | ~~~~~~~ 25 | 26 | :: 27 | 28 | choco install ethermint 29 | 30 | 31 | Download Binary 32 | --------------- 33 | 34 | See the `tendermint website `__ to download the binaries for each platform. 35 | 36 | 37 | From Source 38 | ----------- 39 | 40 | On all platforms, you'll need ``golang`` `installed `__. Then you can do: 41 | 42 | :: 43 | 44 | go get -u -d github.com/tendermint/ethermint 45 | go get -u -d github.com/tendermint/tendermint 46 | cd $GOPATH/src/github.com/tendermint/ethermint 47 | make install 48 | cd ../tendermint 49 | make install 50 | 51 | Hang tight, the build may take awhile. A tool called ``glide`` will also be installed to manage the dependencies. 52 | -------------------------------------------------------------------------------- /docs/getting-started/using-mist.rst: -------------------------------------------------------------------------------- 1 | .. _using-mist: 2 | 3 | Using Mist 4 | ========== 5 | 6 | This guide and the previous one (Using Geth) replace `the original Medium post `__. 7 | 8 | Start by reviewing the `getting started guide <./getting-started.html>`__. Ensure you have both ``tendermint`` and ``ethermint`` installed. See their versions: 9 | 10 | :: 11 | 12 | tendermint version 13 | ethermint version 14 | 15 | This tutorial was written with tendermint v0.10.4 and ethermint v0.5.0. 16 | 17 | Initialization 18 | -------------- 19 | 20 | Ethermint 21 | ^^^^^^^^^ 22 | 23 | We'll use a recently introduced flag to simultaneously initialize ``tendermint`` under the hood: 24 | 25 | :: 26 | 27 | ethermint --datadir ~/.ethermint --with-tendermint init 28 | 29 | 30 | The above command will create a handful of files in ``~/.ethermint``; you should investigate them. Of particular note is the default account in ``~/.ethermint/keystore``. Its password is ``1234`` and we'll be using that later. 31 | 32 | Mist 33 | ^^^^ 34 | 35 | The fastest way to get started is by `downloading the right package for your OS `__. For example, on Linux, the ``Mist-linux64-0-9-0.deb`` file was downloaded followed by ``sudo dpkg -i Mist-linux64-0-9-0.deb``. Check that ``mist`` is installed: 36 | 37 | :: 38 | 39 | mist --version 40 | 41 | The ``README`` in the `Mist repository `__ is helpful for debugging installation or installing from source. For this tutorial, mist v0.9.0 was used. 42 | 43 | 44 | Run It 45 | ------ 46 | 47 | In one window, start ``ethermint``: 48 | 49 | :: 50 | 51 | ethermint --with-tendermint --datadir ~/.ethermint --rpc --rpcaddr=0.0.0.0 --ws --wsaddr=0.0.0.0 --rpcapi eth,net,web3,personal,admin 52 | 53 | and note the ``--with-tendermint`` flag, which also starts the ``tendermint`` process under the hood. 54 | 55 | In another terminal window, start ``mist``: 56 | 57 | :: 58 | 59 | mist --rpc http://localhost:8545 60 | 61 | The ``--rpc http://localhost:8545`` option will connect mist to the ``ethermint`` node rather than the main net. You can see this from the ``PRIVATE-NET`` displayed in the app starting up: 62 | 63 | .. image:: ../assets/mist-ethermint-1.png 64 | :align: center 65 | 66 | Deploy It 67 | --------- 68 | 69 | The previous ``mist`` command will open one or more windows as apps on your screen. We haven't downloaded the wallet yet, so its functionality is not available. There is, however, still lots we can do. To start, we can go to the Mist Menu Bar and select ``Develop => Open Remix IDE`` which will open browser solidity for compiling and deploying smart contracts. It'll look something like: 70 | 71 | .. image:: ../assets/remix-ide.png 72 | :align: center 73 | 74 | Looking at the right-hand sidebar under ``Account``, we should see: 75 | 76 | .. image:: ../assets/ethermint-default-address-remix.png 77 | :align: center 78 | 79 | which is the default account mentioned at the beginning of this tutorial. Let's work with this account since it already has ether. 80 | 81 | The ``ballot.sol`` contract is pre-loaded into Remix and we can deploy it as-is for illustration purposes. This can be done by clicking the ``Create`` button which will launch a popup: 82 | 83 | .. image:: ../assets/remix-create-contract.png 84 | :align: center 85 | 86 | Go ahead and enter ``1234`` as the password; the contract will be mined! Recall that this is the default account mentioned at the beginning of the tutorial. 87 | 88 | In the logs for ``mist``, you'll see something like: 89 | 90 | :: 91 | 92 | [2017-09-20 11:22:26.897] [INFO] method/eth_sendTransaction - Ask user for password 93 | [2017-09-20 11:22:26.898] [INFO] method/eth_sendTransaction - { from: '0x7eff122b94897ea5b0e2a9abf47b86337fafebdc', 94 | data: '0x606060...74a70029', 95 | value: '0x0', 96 | gas: '0x925ea' } 97 | [2017-09-20 11:22:26.918] [INFO] Windows - Create popup window: sendTransactionConfirmation 98 | [2017-09-20 11:22:26.919] [INFO] Windows - Create secondary window: sendTransactionConfirmation, owner: notset 99 | [2017-09-20 11:22:27.266] [INFO] Sockets/5 - Connect to {"hostPort":"http://localhost:8545"} 100 | [2017-09-20 11:22:27.269] [INFO] Sockets/5 - Connected! 101 | [2017-09-20 11:22:27.885] [INFO] (ui: popupWindow) - Web3 already initialized, re-using provider. 102 | [2017-09-20 11:22:27.963] [INFO] (ui: popupWindow) - Meteor starting up... 103 | [2017-09-20 11:22:28.180] [INFO] (ui: popupWindow) - Estimated gas: 599530 null 104 | [2017-09-20 11:22:28.241] [INFO] (ui: popupWindow) - Estimated gas: 599530 null 105 | [2017-09-20 11:26:24.275] [INFO] (ui: popupWindow) - Choosen Gas: 0x925ea 0x925ea 106 | [2017-09-20 11:26:25.193] [INFO] method/eth_sendTransaction - Transaction sent 0x9d052d2557e13e9d5841ac1b5c1f49784f7aa0555e3617a140a4d5b46ea3f7b1 107 | 108 | and in the logs for ``ethermint`` you'll see: 109 | 110 | :: 111 | 112 | INFO [09-20|11:26:25] Submitted contract creation fullhash=0x9d052d2557e13e9d5841ac1b5c1f49784f7aa0555e3617a140a4d5b46ea3f7b1 contract=0xab119259ff325f845f8ce59de8ccf63e597a74cd 113 | INFO [09-20|11:26:25] Imported new chain segment blocks=1 txs=1 mgas=0.600 elapsed=6.141ms mgasps=97.616 number=6039 hash=6773dd…a28204 114 | 115 | Note that the last line for the mist logs show the ``Transaction sent``, who's hash matches the ``fullhash=`` value in ethermint's logs. The ``contract=`` field in ethermint's logs will also match the contract hash from Remix: 116 | 117 | .. image:: ../assets/contract-hash-remix.png 118 | 119 | That's it, you've succesfully deployed a contract to ``ethermint`` using ``mist``! Next, let's look at the basics of account creation. 120 | 121 | Create Accounts 122 | --------------- 123 | 124 | A follow up tutorial will cover the wallet integration. Right now we're focusing on a few, simple connections between the ``ethermint`` tooling and the ``mist`` application. On the Mist Menu Bar, go to ``File => New Account`` and follow the popup instructions by entering a password. Your new account won't have any ether but you'll be able to see it in two places: 1) in the Remix IDE ``Account`` dropdown and 2) at ``~/.ethermint/keystore/`` where the account address is the file suffix plus `0x`. 125 | 126 | The above examples show how ``ethermint`` integrates easily with existing Ethereum development tooling. 127 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Ethermint documentation master file, created by 2 | sphinx-quickstart on Mon Aug 7 04:55:09 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Ethermint 7 | ========= 8 | 9 | .. image:: assets/ethermint-logo.png 10 | :height: 200px 11 | :width: 200px 12 | :align: center 13 | 14 | 15 | Introduction 16 | ============ 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | 21 | introduction/what-is-ethermint.rst 22 | 23 | Getting Started 24 | =============== 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | 29 | getting-started/install.rst 30 | getting-started/getting-started.rst 31 | getting-started/using-mist.rst 32 | 33 | Testnets 34 | ======== 35 | 36 | .. toctree:: 37 | :maxdepth: 2 38 | 39 | testnets/venus.rst 40 | 41 | Architecture 42 | ============ 43 | 44 | .. toctree:: 45 | :maxdepth: 2 46 | 47 | architecture/future-architecture.rst 48 | architecture/inter-blockchain-communication.rst 49 | architecture/peg-zones.rst 50 | -------------------------------------------------------------------------------- /docs/introduction/what-is-ethermint.rst: -------------------------------------------------------------------------------- 1 | .. _what-is-ethermint: 2 | 3 | What is Ethermint? 4 | ================== 5 | 6 | Ethermint is a blazing fast Proof-of-Stake blockchain that is fully compatible with Ethereum. 7 | 8 | We `first introduced `__ Ethermint in May 2017 and it continues to evolve as an Ethereum-compatible high-speed Proof-of-Stake blockchain client that can run any application already built for Ethereum. 9 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=Ethermint 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx-autobuild 3 | recommonmark 4 | sphinx_rtd_theme 5 | -------------------------------------------------------------------------------- /docs/testnets/venus.rst: -------------------------------------------------------------------------------- 1 | .. _venus.rst: 2 | 3 | Connect to a testnet 4 | ==================== 5 | 6 | Join the ethermint testnet with a few commands. This guide is based on the `original Medium article `_. 7 | 8 | Pre-requisites 9 | -------------- 10 | 11 | You'll need the ``ethermint`` and ``tendermint`` binaries installed with the correct version. Ensure you are using compatible versions (how?). See their respective repos for install instructions. 12 | 13 | Check that everything is installed correctly: 14 | 15 | :: 16 | 17 | tendermint version 18 | v0.10.3 19 | 20 | ethermint version 21 | v0.3.0 22 | 23 | First, we need some directories for our files: 24 | 25 | :: 26 | 27 | mkdir --parents ~/.venus/tendermint 28 | mkdir ~/.venus/ethermint 29 | 30 | Second, we're going to need the required initialization files. These consist of a ``genesis.json`` for each ethermint and tendermint and a ``config.toml`` for tendermint. These can be got using the ``--testnet`` flag on each respective program or by cloning the testnets repo. The ``genesis.json`` for tendermint will look like: 31 | 32 | :: 33 | 34 | { 35 | "genesis_time":"2017-06-28T13:39:18Z", 36 | "chain_id":"venus", 37 | "validators": 38 | [ 39 | 40 | { 41 | "pub_key": { 42 | "data": "E24396CFDCBAF3BD0F2C5CF510551F70B4634E4E5EBF9655B1FB57F451ABB344", 43 | "type": "ed25519" 44 | }, 45 | "amount":10, 46 | "name":"venus-node3" 47 | } 48 | , 49 | { 50 | "pub_key": { 51 | "data": "2C97BEA0B5A4D9EAE04C242C7AD2A6D2BA989E3C4A7B276AB137300C37EB22F7", 52 | "type": "ed25519" 53 | }, 54 | "amount":10, 55 | "name":"venus-node4" 56 | } 57 | , 58 | { 59 | "pub_key": { 60 | "data": "CA53D568F6EDC245D80D887F068F8FE7E03D540B2F7D2212CA436FC962394EA3", 61 | "type": "ed25519" 62 | }, 63 | "amount":10, 64 | "name":"venus-node0" 65 | } 66 | , 67 | { 68 | "pub_key": { 69 | "data": "CE4877B1E25EDF845EC8F13FAC3B74E0B3A863EBA5B24D10FCDA4A239065E86A", 70 | "type": "ed25519" 71 | }, 72 | "amount":10, 73 | "name":"venus-node5" 74 | } 75 | , 76 | { 77 | "pub_key": { 78 | "data": "8A825277C6A71C89B1F3E9AE5C3853E282F73B6F77A4798C03291EDB1F1F5CA5", 79 | "type": "ed25519" 80 | }, 81 | "amount":10, 82 | "name":"venus-node2" 83 | } 84 | , 85 | { 86 | "pub_key": { 87 | "data": "AF8D2B55E6FAD5DCF6000752E2A05A19B3F42E3072B75BBB2217C43B574ACE99", 88 | "type": "ed25519" 89 | }, 90 | "amount":10, 91 | "name":"venus-node1" 92 | } 93 | , 94 | { 95 | "pub_key": { 96 | "data": "B127402B86C673807AF407B15E324C4565B1A1DC77C85881E7D8D8640C17EBBB", 97 | "type": "ed25519" 98 | }, 99 | "amount":10, 100 | "name":"venus-node6" 101 | } 102 | ], 103 | "app_hash":"", 104 | "app_options": {} 105 | } 106 | 107 | which shows the validators each with 10 bonded tokens. The name of each validator can be used to view the node's information at, for example: http://venus-node0.testnets.interblock.io/ 108 | 109 | Let's take a look at the ``config.toml`` for tendermint: 110 | 111 | :: 112 | 113 | # This is a TOML config file. 114 | # For more information, see https://github.com/toml-lang/toml 115 | 116 | proxy_app = "tcp://127.0.0.1:46658" 117 | moniker = "bob_the_farmer" 118 | fast_sync = true 119 | db_backend = "leveldb" 120 | log_level = "debug" 121 | 122 | [rpc] 123 | laddr = "tcp://0.0.0.0:46657" 124 | 125 | [p2p] 126 | laddr = "tcp://0.0.0.0:46656" 127 | seeds = "138.197.113.220:46656,138.68.12.252:46656,128.199.179.178:46656,139.59.184.2:46656,207.154.246.77:46656,138.197.175.237:46656" 128 | 129 | The main relevant part is the ``seeds =`` field which has the peers to we'll be dialing to join the network. These IPs should match the URL of each node. The ``moniker =`` can be anything you'd like to name your node. 130 | 131 | Finally, we have a ``genesis.json`` for ``ethermint``. It looks pretty much like a ``genesis.json`` for ethereum: 132 | 133 | :: 134 | 135 | { 136 | "config": { 137 | "chainId": 15, 138 | "homesteadBlock": 0, 139 | "eip155Block": 0, 140 | "eip158Block": 0 141 | }, 142 | "nonce": "0xdeadbeefdeadbeef", 143 | "timestamp": "0x00", 144 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 145 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 146 | "difficulty": "0x40", 147 | "gasLimit": "0x8000000", 148 | "alloc": { 149 | "0x7eff122b94897ea5b0e2a9abf47b86337fafebdc": { "balance": "100000000000000" }, 150 | "0xc6713982649D9284ff56c32655a9ECcCDA78422A": { "balance": "10000000000000000000000000000000000" } 151 | } 152 | } 153 | 154 | At this point you should have a ``genesis.json`` and ``config.toml`` in ``~/.venus/tendermint`` and a ``genesis.json`` in ``~/.venus/ethermint``. 155 | 156 | Initialize 157 | ---------- 158 | 159 | Next, we will initialize ethermint: 160 | 161 | :: 162 | 163 | ethermint --datadir ~/.venus/ethermint init ~/.venus/ethermint/genesis.json 164 | 165 | where the ``--datadir`` specifies the correct directory and ``init`` takes a path to the ``genesis.json``. Look inside ``~/.venus/ethermint/ethermint`` to see the files that were created. 166 | 167 | Run Tendermint 168 | -------------- 169 | 170 | Then we start up the tendermint node: 171 | 172 | :: 173 | 174 | tendermint --home ~/.venus/tendermint node 175 | 176 | where ``--home`` is basically like the ``--datadir`` flag from running ethermint, and ``node`` is command that starts up the node. You'll see the following output: 177 | 178 | :: 179 | 180 | I[08-18|17:13:25.880] Generated PrivValidator module=node file=/home/zach/.venus/tendermint/priv_validator.json 181 | I[08-18|17:13:26.100] Starting multiAppConn module=proxy impl=multiAppConn 182 | I[08-18|17:13:26.101] Starting socketClient module=abci-client connection=query impl=socketClient 183 | E[08-18|17:13:26.102] abci.socketClient failed to connect to tcp://127.0.0.1:46658. Retrying... module=abci-client connection=query 184 | E[08-18|17:13:29.102] abci.socketClient failed to connect to tcp://127.0.0.1:46658. Retrying... module=abci-client connection=query 185 | 186 | with the last two lines in red. You'll see a steady stream of that error message every three seconds. Notice the first line; you should now have a ``priv_validator.json`` written to disk. 187 | 188 | Run Ethermint 189 | ------------- 190 | 191 | Now you can start the ethermint process: 192 | 193 | :: 194 | 195 | ethermint --datadir ~/.venus/ethermint/ --rpc --rpcaddr=0.0.0.0 --ws --wsaddr=0.0.0.0 --rpcapi eth,net,web3,personal,admin 196 | 197 | There will be about a dozen lines of initialization information, then the output will look similar to: 198 | 199 | :: 200 | 201 | INFO [08-18|13:35:40] Accepted a new connection module=abci-server 202 | INFO [08-18|13:35:40] Waiting for new connection... module=abci-server 203 | INFO [08-18|13:35:40] Info 204 | INFO [08-18|13:35:40] BeginBlock 205 | INFO [08-18|13:35:40] EndBlock 206 | INFO [08-18|13:35:40] Commit 207 | INFO [08-18|13:35:40] Committing block stateHash=fbccc1…f0e986 blockHash=3cddd3…97eb13 208 | INFO [08-18|13:35:40] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=2.516ms mgasps=0.000 number=3404 hash=3cddd3…97eb13 209 | INFO [08-18|13:35:40] Mapped network port proto=tcp extport=30303 intport=30303 interface="UPNP IGDv1-PPP1" 210 | INFO [08-18|13:35:41] BeginBlock 211 | INFO [08-18|13:35:41] EndBlock 212 | INFO [08-18|13:35:41] Commit 213 | INFO [08-18|13:35:41] Committing block stateHash=2eb09c…58f60f blockHash=df0411…8c7321 214 | INFO [08-18|13:35:41] Imported new chain segment blocks=1 txs=0 mgas=0.000 elapsed=1.315ms mgasps=0.000 number=3405 hash=df0411…8c7321 215 | INFO [08-18|13:35:41] BeginBlock 216 | 217 | The above is output after the syncing had been stopped at block height 3403 (by terminating the process). Look at ``Imported new chain segment`` => ``number=3404``, which increases by one as your node syncs with the testnet. Your output will start from number 1 unless you have been starting and stopping the nodes. 218 | 219 | Congratulation! You are currently syncing up with the testnet. Next, you'll need testnet coins, then try using ``geth`` to create contracts on the testnet. 220 | -------------------------------------------------------------------------------- /ethereum/api.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | ) 8 | 9 | // We must implement our own net service since we don't have access to `internal/ethapi` 10 | 11 | // NetRPCService mirrors the implementation of `internal/ethapi` 12 | // #unstable 13 | type NetRPCService struct { 14 | networkVersion uint64 15 | } 16 | 17 | // NewNetRPCService creates a new net API instance. 18 | // #unstable 19 | func NewNetRPCService(networkVersion uint64) *NetRPCService { 20 | return &NetRPCService{networkVersion} 21 | } 22 | 23 | // Listening returns an indication if the node is listening for network connections. 24 | // #unstable 25 | func (n *NetRPCService) Listening() bool { 26 | return true // always listening 27 | } 28 | 29 | // PeerCount returns the number of connected peers 30 | // #unstable 31 | func (n *NetRPCService) PeerCount() hexutil.Uint { 32 | return hexutil.Uint(0) 33 | } 34 | 35 | // Version returns the current ethereum protocol version. 36 | // #unstable 37 | func (n *NetRPCService) Version() string { 38 | return fmt.Sprintf("%d", n.networkVersion) 39 | } 40 | -------------------------------------------------------------------------------- /ethereum/backend.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/core" 8 | "github.com/ethereum/go-ethereum/core/state" 9 | ethTypes "github.com/ethereum/go-ethereum/core/types" 10 | "github.com/ethereum/go-ethereum/eth" 11 | "github.com/ethereum/go-ethereum/event" 12 | "github.com/ethereum/go-ethereum/node" 13 | "github.com/ethereum/go-ethereum/p2p" 14 | "github.com/ethereum/go-ethereum/rpc" 15 | 16 | abciTypes "github.com/tendermint/abci/types" 17 | 18 | rpcClient "github.com/tendermint/tendermint/rpc/lib/client" 19 | 20 | emtTypes "github.com/tendermint/ethermint/types" 21 | ) 22 | 23 | //---------------------------------------------------------------------- 24 | // Backend manages the underlying ethereum state for storage and processing, 25 | // and maintains the connection to Tendermint for forwarding txs 26 | 27 | // Backend handles the chain database and VM 28 | // #stable - 0.4.0 29 | type Backend struct { 30 | // backing ethereum structures 31 | ethereum *eth.Ethereum 32 | ethConfig *eth.Config 33 | 34 | // txBroadcastLoop subscription 35 | txSub *event.TypeMuxSubscription 36 | 37 | // EthState 38 | es *EthState 39 | 40 | // client for forwarding txs to Tendermint 41 | client rpcClient.HTTPClient 42 | } 43 | 44 | // NewBackend creates a new Backend 45 | // #stable - 0.4.0 46 | func NewBackend(ctx *node.ServiceContext, ethConfig *eth.Config, 47 | client rpcClient.HTTPClient) (*Backend, error) { 48 | 49 | // Create working ethereum state. 50 | es := NewEthState() 51 | 52 | // eth.New takes a ServiceContext for the EventMux, the AccountManager, 53 | // and some basic functions around the DataDir. 54 | ethereum, err := eth.New(ctx, ethConfig, es) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | es.SetEthereum(ethereum) 60 | es.SetEthConfig(ethConfig) 61 | 62 | // send special event to go-ethereum to switch homestead=true. 63 | currentBlock := ethereum.BlockChain().CurrentBlock() 64 | ethereum.EventMux().Post(core.ChainHeadEvent{currentBlock}) // nolint: vet, errcheck 65 | 66 | // We don't need PoW/Uncle validation. 67 | ethereum.BlockChain().SetValidator(NullBlockProcessor{}) 68 | 69 | ethBackend := &Backend{ 70 | ethereum: ethereum, 71 | ethConfig: ethConfig, 72 | es: es, 73 | client: client, 74 | } 75 | return ethBackend, nil 76 | } 77 | 78 | // Ethereum returns the underlying the ethereum object. 79 | // #stable 80 | func (b *Backend) Ethereum() *eth.Ethereum { 81 | return b.ethereum 82 | } 83 | 84 | // Config returns the eth.Config. 85 | // #stable 86 | func (b *Backend) Config() *eth.Config { 87 | return b.ethConfig 88 | } 89 | 90 | //---------------------------------------------------------------------- 91 | // Handle block processing 92 | 93 | // DeliverTx appends a transaction to the current block 94 | // #stable 95 | func (b *Backend) DeliverTx(tx *ethTypes.Transaction) abciTypes.ResponseDeliverTx { 96 | return b.es.DeliverTx(tx) 97 | } 98 | 99 | // AccumulateRewards accumulates the rewards based on the given strategy 100 | // #unstable 101 | func (b *Backend) AccumulateRewards(strategy *emtTypes.Strategy) { 102 | b.es.AccumulateRewards(strategy) 103 | } 104 | 105 | // Commit finalises the current block 106 | // #unstable 107 | func (b *Backend) Commit(receiver common.Address) (common.Hash, error) { 108 | return b.es.Commit(receiver) 109 | } 110 | 111 | // InitEthState initializes the EthState 112 | // #unstable 113 | func (b *Backend) InitEthState(receiver common.Address) error { 114 | return b.es.ResetWorkState(receiver) 115 | } 116 | 117 | // UpdateHeaderWithTimeInfo uses the tendermint header to update the ethereum header 118 | // #unstable 119 | func (b *Backend) UpdateHeaderWithTimeInfo(tmHeader *abciTypes.Header) { 120 | b.es.UpdateHeaderWithTimeInfo(b.ethereum.ApiBackend.ChainConfig(), uint64(tmHeader.Time), 121 | uint64(tmHeader.GetNumTxs())) 122 | } 123 | 124 | // GasLimit returns the maximum gas per block 125 | // #unstable 126 | func (b *Backend) GasLimit() big.Int { 127 | return b.es.GasLimit() 128 | } 129 | 130 | //---------------------------------------------------------------------- 131 | // Implements: node.Service 132 | 133 | // APIs returns the collection of RPC services the ethereum package offers. 134 | // #stable - 0.4.0 135 | func (b *Backend) APIs() []rpc.API { 136 | apis := b.Ethereum().APIs() 137 | retApis := []rpc.API{} 138 | for _, v := range apis { 139 | if v.Namespace == "net" { 140 | v.Service = NewNetRPCService(b.ethConfig.NetworkId) 141 | } 142 | if v.Namespace == "miner" { 143 | continue 144 | } 145 | if _, ok := v.Service.(*eth.PublicMinerAPI); ok { 146 | continue 147 | } 148 | retApis = append(retApis, v) 149 | } 150 | return retApis 151 | } 152 | 153 | // Start implements node.Service, starting all internal goroutines needed by the 154 | // Ethereum protocol implementation. 155 | // #stable 156 | func (b *Backend) Start(_ *p2p.Server) error { 157 | go b.txBroadcastLoop() 158 | return nil 159 | } 160 | 161 | // Stop implements node.Service, terminating all internal goroutines used by the 162 | // Ethereum protocol. 163 | // #stable 164 | func (b *Backend) Stop() error { 165 | b.txSub.Unsubscribe() 166 | b.ethereum.Stop() // nolint: errcheck 167 | return nil 168 | } 169 | 170 | // Protocols implements node.Service, returning all the currently configured 171 | // network protocols to start. 172 | // #stable 173 | func (b *Backend) Protocols() []p2p.Protocol { 174 | return nil 175 | } 176 | 177 | //---------------------------------------------------------------------- 178 | // We need a block processor that just ignores PoW and uncles and so on 179 | 180 | // NullBlockProcessor does not validate anything 181 | // #unstable 182 | type NullBlockProcessor struct{} 183 | 184 | // ValidateBody does not validate anything 185 | // #unstable 186 | func (NullBlockProcessor) ValidateBody(*ethTypes.Block) error { return nil } 187 | 188 | // ValidateState does not validate anything 189 | // #unstable 190 | func (NullBlockProcessor) ValidateState(block, parent *ethTypes.Block, state *state.StateDB, 191 | receipts ethTypes.Receipts, usedGas *big.Int) error { 192 | return nil 193 | } 194 | -------------------------------------------------------------------------------- /ethereum/eth_state.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "math/big" 5 | "sync" 6 | 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/ethereum/go-ethereum/consensus/ethash" 9 | "github.com/ethereum/go-ethereum/core" 10 | "github.com/ethereum/go-ethereum/core/state" 11 | ethTypes "github.com/ethereum/go-ethereum/core/types" 12 | "github.com/ethereum/go-ethereum/core/vm" 13 | "github.com/ethereum/go-ethereum/eth" 14 | //"github.com/ethereum/go-ethereum/log" 15 | "github.com/ethereum/go-ethereum/ethdb" 16 | "github.com/ethereum/go-ethereum/params" 17 | 18 | abciTypes "github.com/tendermint/abci/types" 19 | 20 | emtTypes "github.com/tendermint/ethermint/types" 21 | "github.com/cosmos/cosmos-sdk/errors" 22 | ) 23 | 24 | //---------------------------------------------------------------------- 25 | // EthState manages concurrent access to the intermediate workState object 26 | // The ethereum tx pool fires TxPreEvent in a go-routine, 27 | // and the miner subscribes to this in another go-routine and processes the tx onto 28 | // an intermediate state. We used to use `unsafe` to overwrite the miner, but this 29 | // didn't work because it didn't affect the already launched go-routines. 30 | // So instead we introduce the Pending API in a small commit in go-ethereum 31 | // so we don't even start the miner there, and instead manage the intermediate state from here. 32 | // In the same commit we also fire the TxPreEvent synchronously so the order is preserved, 33 | // instead of using a go-routine. 34 | 35 | type EthState struct { 36 | ethereum *eth.Ethereum 37 | ethConfig *eth.Config 38 | 39 | mtx sync.Mutex 40 | work workState // latest working state 41 | } 42 | 43 | // After NewEthState, call SetEthereum and SetEthConfig. 44 | func NewEthState() *EthState { 45 | return &EthState{ 46 | ethereum: nil, // set with SetEthereum 47 | ethConfig: nil, // set with SetEthConfig 48 | } 49 | } 50 | 51 | func (es *EthState) SetEthereum(ethereum *eth.Ethereum) { 52 | es.ethereum = ethereum 53 | } 54 | 55 | func (es *EthState) SetEthConfig(ethConfig *eth.Config) { 56 | es.ethConfig = ethConfig 57 | } 58 | 59 | // Execute the transaction. 60 | func (es *EthState) DeliverTx(tx *ethTypes.Transaction) abciTypes.ResponseDeliverTx { 61 | es.mtx.Lock() 62 | defer es.mtx.Unlock() 63 | 64 | blockchain := es.ethereum.BlockChain() 65 | chainConfig := es.ethereum.ApiBackend.ChainConfig() 66 | blockHash := common.Hash{} 67 | return es.work.deliverTx(blockchain, es.ethConfig, chainConfig, blockHash, tx) 68 | } 69 | 70 | // Accumulate validator rewards. 71 | func (es *EthState) AccumulateRewards(strategy *emtTypes.Strategy) { 72 | es.mtx.Lock() 73 | defer es.mtx.Unlock() 74 | 75 | es.work.accumulateRewards(strategy) 76 | } 77 | 78 | // Commit and reset the work. 79 | func (es *EthState) Commit(receiver common.Address) (common.Hash, error) { 80 | es.mtx.Lock() 81 | defer es.mtx.Unlock() 82 | 83 | blockHash, err := es.work.commit(es.ethereum.BlockChain(), es.ethereum.ChainDb()) 84 | if err != nil { 85 | return common.Hash{}, err 86 | } 87 | 88 | err = es.resetWorkState(receiver) 89 | if err != nil { 90 | return common.Hash{}, err 91 | } 92 | 93 | return blockHash, err 94 | } 95 | 96 | func (es *EthState) ResetWorkState(receiver common.Address) error { 97 | es.mtx.Lock() 98 | defer es.mtx.Unlock() 99 | 100 | return es.resetWorkState(receiver) 101 | } 102 | 103 | func (es *EthState) resetWorkState(receiver common.Address) error { 104 | 105 | blockchain := es.ethereum.BlockChain() 106 | state, err := blockchain.State() 107 | if err != nil { 108 | return err 109 | } 110 | 111 | currentBlock := blockchain.CurrentBlock() 112 | ethHeader := newBlockHeader(receiver, currentBlock) 113 | 114 | es.work = workState{ 115 | header: ethHeader, 116 | parent: currentBlock, 117 | state: state, 118 | txIndex: 0, 119 | totalUsedGas: big.NewInt(0), 120 | gp: new(core.GasPool).AddGas(ethHeader.GasLimit), 121 | } 122 | return nil 123 | } 124 | 125 | func (es *EthState) UpdateHeaderWithTimeInfo( 126 | config *params.ChainConfig, parentTime uint64, numTx uint64) { 127 | 128 | es.mtx.Lock() 129 | defer es.mtx.Unlock() 130 | 131 | es.work.updateHeaderWithTimeInfo(config, parentTime, numTx) 132 | } 133 | 134 | func (es *EthState) GasLimit() big.Int { 135 | return big.Int(*es.work.gp) 136 | } 137 | 138 | //---------------------------------------------------------------------- 139 | // Implements: miner.Pending API (our custom patch to go-ethereum) 140 | 141 | // Return a new block and a copy of the state from the latest work. 142 | // #unstable 143 | func (es *EthState) Pending() (*ethTypes.Block, *state.StateDB) { 144 | es.mtx.Lock() 145 | defer es.mtx.Unlock() 146 | 147 | return ethTypes.NewBlock( 148 | es.work.header, 149 | es.work.transactions, 150 | nil, 151 | es.work.receipts, 152 | ), es.work.state.Copy() 153 | } 154 | 155 | //---------------------------------------------------------------------- 156 | // 157 | 158 | // The work struct handles block processing. 159 | // It's updated with each DeliverTx and reset on Commit. 160 | type workState struct { 161 | header *ethTypes.Header 162 | parent *ethTypes.Block 163 | state *state.StateDB 164 | 165 | txIndex int 166 | transactions []*ethTypes.Transaction 167 | receipts ethTypes.Receipts 168 | allLogs []*ethTypes.Log 169 | 170 | totalUsedGas *big.Int 171 | gp *core.GasPool 172 | } 173 | 174 | // nolint: unparam 175 | func (ws *workState) accumulateRewards(strategy *emtTypes.Strategy) { 176 | 177 | ethash.AccumulateRewards(ws.state, ws.header, []*ethTypes.Header{}) 178 | ws.header.GasUsed = ws.totalUsedGas 179 | } 180 | 181 | // Runs ApplyTransaction against the ethereum blockchain, fetches any logs, 182 | // and appends the tx, receipt, and logs. 183 | func (ws *workState) deliverTx(blockchain *core.BlockChain, config *eth.Config, 184 | chainConfig *params.ChainConfig, blockHash common.Hash, 185 | tx *ethTypes.Transaction) abciTypes.ResponseDeliverTx { 186 | 187 | ws.state.Prepare(tx.Hash(), blockHash, ws.txIndex) 188 | receipt, _, err := core.ApplyTransaction( 189 | chainConfig, 190 | blockchain, 191 | nil, // defaults to address of the author of the header 192 | ws.gp, 193 | ws.state, 194 | ws.header, 195 | tx, 196 | ws.totalUsedGas, 197 | vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}, 198 | ) 199 | if err != nil { 200 | return abciTypes.ResponseDeliverTx{Code: errors.CodeTypeInternalErr, Log: err.Error()} 201 | } 202 | 203 | logs := ws.state.GetLogs(tx.Hash()) 204 | 205 | ws.txIndex++ 206 | 207 | // The slices are allocated in updateHeaderWithTimeInfo 208 | ws.transactions = append(ws.transactions, tx) 209 | ws.receipts = append(ws.receipts, receipt) 210 | ws.allLogs = append(ws.allLogs, logs...) 211 | 212 | return abciTypes.ResponseDeliverTx{Code: abciTypes.CodeTypeOK} 213 | } 214 | 215 | // Commit the ethereum state, update the header, make a new block and add it to 216 | // the ethereum blockchain. The application root hash is the hash of the 217 | // ethereum block. 218 | func (ws *workState) commit(blockchain *core.BlockChain, db ethdb.Database) (common.Hash, error) { 219 | 220 | // Commit ethereum state and update the header. 221 | hashArray, err := ws.state.CommitTo(db.NewBatch(), false) // XXX: ugh hardforks 222 | if err != nil { 223 | return common.Hash{}, err 224 | } 225 | ws.header.Root = hashArray 226 | 227 | for _, log := range ws.allLogs { 228 | log.BlockHash = hashArray 229 | } 230 | 231 | // Create block object and compute final commit hash (hash of the ethereum 232 | // block). 233 | block := ethTypes.NewBlock(ws.header, ws.transactions, nil, ws.receipts) 234 | blockHash := block.Hash() 235 | 236 | // Save the block to disk. 237 | // log.Info("Committing block", "stateHash", hashArray, "blockHash", blockHash) 238 | _, err = blockchain.InsertChain([]*ethTypes.Block{block}) 239 | if err != nil { 240 | // log.Info("Error inserting ethereum block in chain", "err", err) 241 | return common.Hash{}, err 242 | } 243 | return blockHash, err 244 | } 245 | 246 | func (ws *workState) updateHeaderWithTimeInfo( 247 | config *params.ChainConfig, parentTime uint64, numTx uint64) { 248 | 249 | lastBlock := ws.parent 250 | parentHeader := ðTypes.Header{ 251 | Difficulty: lastBlock.Difficulty(), 252 | Number: lastBlock.Number(), 253 | Time: lastBlock.Time(), 254 | } 255 | ws.header.Time = new(big.Int).SetUint64(parentTime) 256 | ws.header.Difficulty = ethash.CalcDifficulty(config, parentTime, parentHeader) 257 | ws.transactions = make([]*ethTypes.Transaction, 0, numTx) 258 | ws.receipts = make([]*ethTypes.Receipt, 0, numTx) 259 | ws.allLogs = make([]*ethTypes.Log, 0, numTx) 260 | } 261 | 262 | //---------------------------------------------------------------------- 263 | 264 | // Create a new block header from the previous block. 265 | func newBlockHeader(receiver common.Address, prevBlock *ethTypes.Block) *ethTypes.Header { 266 | return ðTypes.Header{ 267 | Number: prevBlock.Number().Add(prevBlock.Number(), big.NewInt(1)), 268 | ParentHash: prevBlock.Hash(), 269 | GasLimit: core.CalcGasLimit(prevBlock), 270 | Coinbase: receiver, 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /ethereum/node.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "gopkg.in/urfave/cli.v1" 5 | 6 | ethUtils "github.com/ethereum/go-ethereum/cmd/utils" 7 | "github.com/ethereum/go-ethereum/eth" 8 | "github.com/ethereum/go-ethereum/node" 9 | ) 10 | 11 | // Node is the main object. 12 | type Node struct { 13 | node.Node 14 | } 15 | 16 | // New creates a new node. 17 | func New(conf *node.Config) (*Node, error) { 18 | stack, err := node.New(conf) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return &Node{*stack}, nil // nolint: vet 24 | } 25 | 26 | // Start starts base node and stop p2p server 27 | func (n *Node) Start() error { 28 | // start p2p server 29 | err := n.Node.Start() 30 | if err != nil { 31 | return err 32 | } 33 | 34 | // stop it 35 | n.Node.Server().Stop() 36 | 37 | return nil 38 | } 39 | 40 | // NewNodeConfig for p2p and network layer 41 | // #unstable 42 | func NewNodeConfig(ctx *cli.Context) *node.Config { 43 | nodeConfig := new(node.Config) 44 | ethUtils.SetNodeConfig(ctx, nodeConfig) 45 | 46 | return nodeConfig 47 | } 48 | 49 | // NewEthConfig for the ethereum services 50 | // #unstable 51 | func NewEthConfig(ctx *cli.Context, stack *node.Node) *eth.Config { 52 | ethConfig := new(eth.Config) 53 | ethUtils.SetEthConfig(ctx, stack, ethConfig) 54 | 55 | return ethConfig 56 | } 57 | -------------------------------------------------------------------------------- /ethereum/node_test.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "flag" 5 | "net" 6 | "testing" 7 | 8 | "gopkg.in/urfave/cli.v1" 9 | 10 | "github.com/stretchr/testify/assert" 11 | 12 | "github.com/ethereum/go-ethereum/node" 13 | ) 14 | 15 | var dummyApp = &cli.App{ 16 | Name: "test", 17 | Author: "Tendermint", 18 | } 19 | 20 | var dummyContext = cli.NewContext(dummyApp, flag.NewFlagSet("test", flag.ContinueOnError), nil) 21 | var dummyNode, _ = node.New(&node.DefaultConfig) 22 | 23 | func TestNewNodeConfig(t *testing.T) { 24 | defer func() { 25 | err := recover() 26 | assert.Nil(t, err, "expecting no panics") 27 | }() 28 | 29 | ncfg := NewNodeConfig(dummyContext) 30 | assert.NotNil(t, ncfg, "expecting a non-nil config") 31 | } 32 | 33 | func TestNewEthConfig(t *testing.T) { 34 | defer func() { 35 | err := recover() 36 | assert.Nil(t, err, "expecting no panics") 37 | }() 38 | 39 | ecfg := NewEthConfig(dummyContext, dummyNode) 40 | assert.NotNil(t, ecfg, "expecting a non-nil config") 41 | } 42 | 43 | func TestEnsureDisabledEthereumP2PStack(t *testing.T) { 44 | cfg := new(node.Config) 45 | *cfg = node.DefaultConfig 46 | cfg.P2P.ListenAddr = ":34555" 47 | node, err := New(cfg) 48 | if err != nil { 49 | t.Fatalf("cannot initialise new node from config: %v", err) 50 | } 51 | 52 | if err := node.Start(); err != nil { 53 | t.Fatalf("cannot start node: %v", err) 54 | } 55 | // Make a listener and ensure that ListenAddr can be bound to 56 | // i.e that no other service is listening on it 57 | addr := cfg.P2P.ListenAddr 58 | listener, err := net.Listen("tcp", addr) 59 | if err != nil { 60 | t.Fatalf("failed to bind to %q: %v", addr, err) 61 | } 62 | if err := listener.Close(); err != nil { 63 | t.Fatalf("close: %v", err) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ethereum/txs.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "bytes" 5 | "time" 6 | 7 | "github.com/ethereum/go-ethereum/core" 8 | ethTypes "github.com/ethereum/go-ethereum/core/types" 9 | "github.com/ethereum/go-ethereum/log" 10 | 11 | rpcClient "github.com/tendermint/tendermint/rpc/lib/client" 12 | ) 13 | 14 | //---------------------------------------------------------------------- 15 | // Transactions sent via the go-ethereum rpc need to be routed to tendermint 16 | 17 | // listen for txs and forward to tendermint 18 | func (b *Backend) txBroadcastLoop() { 19 | b.txSub = b.ethereum.EventMux().Subscribe(core.TxPreEvent{}) 20 | 21 | waitForServer(b.client) 22 | 23 | for obj := range b.txSub.Chan() { 24 | event := obj.Data.(core.TxPreEvent) 25 | if err := b.BroadcastTx(event.Tx); err != nil { 26 | log.Error("Broadcast error", "err", err) 27 | } 28 | } 29 | } 30 | 31 | // BroadcastTx broadcasts a transaction to tendermint core 32 | // #unstable 33 | func (b *Backend) BroadcastTx(tx *ethTypes.Transaction) error { 34 | var result interface{} 35 | 36 | buf := new(bytes.Buffer) 37 | if err := tx.EncodeRLP(buf); err != nil { 38 | return err 39 | } 40 | params := map[string]interface{}{ 41 | "tx": buf.Bytes(), 42 | } 43 | 44 | _, err := b.client.Call("broadcast_tx_sync", params, &result) 45 | return err 46 | } 47 | 48 | //---------------------------------------------------------------------- 49 | // wait for Tendermint to open the socket and run http endpoint 50 | 51 | func waitForServer(c rpcClient.HTTPClient) { 52 | var result interface{} 53 | for { 54 | _, err := c.Call("status", map[string]interface{}{}, &result) 55 | if err == nil { 56 | break 57 | } 58 | 59 | log.Info("Waiting for tendermint endpoint to start", "err", err) 60 | time.Sleep(time.Second * 3) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/tendermint/ethermint 2 | import: 3 | - package: github.com/tendermint/tendermint 4 | version: 0.15.x 5 | - package: github.com/tendermint/tmlibs 6 | version: 0.6.x 7 | - package: github.com/tendermint/abci 8 | version: 0.9.x 9 | - package: github.com/ethereum/go-ethereum 10 | version: f1.6.7 11 | repo: https://github.com/tendermint/go-ethereum.git 12 | - package: github.com/spf13/pflag 13 | version: master 14 | - package: gopkg.in/urfave/cli.v1 15 | version: 1.x 16 | - package: github.com/mattn/go-colorable 17 | version: 0.x 18 | - package: google.golang.org/grpc 19 | repo: https://github.com/grpc/grpc-go 20 | vcs: git 21 | - package: google.golang.org/genproto 22 | repo: https://github.com/google/go-genproto 23 | vcs: git 24 | -------------------------------------------------------------------------------- /scripts/dist.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Get the version from the environment, or try to figure it out. 5 | if [ -z $VERSION ]; then 6 | VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) 7 | fi 8 | if [ -z "$VERSION" ]; then 9 | echo "Please specify a version." 10 | exit 1 11 | fi 12 | echo "==> Building version $VERSION..." 13 | 14 | # Get the parent directory of where this script is. 15 | SOURCE="${BASH_SOURCE[0]}" 16 | while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done 17 | DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" 18 | 19 | # Change into that dir because we expect that. 20 | cd "$DIR" 21 | 22 | # xgo requires GOPATH 23 | if [ ! -z "$GOPATH" ]; then 24 | GOPATH=$(go env GOPATH) 25 | fi 26 | 27 | # Get the git commit 28 | GIT_COMMIT="$(git rev-parse --short HEAD)" 29 | GIT_IMPORT="github.com/tendermint/ethermint/version" 30 | 31 | # Determine the arch/os combos we're building for 32 | XC_ARCH=${XC_ARCH:-"386 amd64 arm-5 arm-6 arm-7 mips mipsle mips64 mips64le"} 33 | XC_OS=${XC_OS:-"darwin linux windows"} 34 | IGNORE=("darwin/arm darwin/386") 35 | 36 | TARGETS="" 37 | for os in $XC_OS; do 38 | for arch in $XC_ARCH; do 39 | target="$os/$arch" 40 | 41 | case ${IGNORE[@]} in *$target*) continue;; esac 42 | TARGETS="$target,$TARGETS" 43 | done 44 | done 45 | 46 | # Delete the old dir 47 | echo "==> Removing old directory..." 48 | rm -rf build/pkg 49 | mkdir -p build/pkg 50 | 51 | # Make sure build tools are available. 52 | make tools 53 | 54 | # Get VENDORED dependencies 55 | make get_vendor_deps 56 | 57 | # Build! 58 | # ldflags: -s Omit the symbol table and debug information. 59 | # -w Omit the DWARF symbol table. 60 | echo "==> Building..." 61 | TARGETS=${TARGETS::${#TARGETS}-1} 62 | xgo -go="1.8.3" \ 63 | -targets="${TARGETS}" \ 64 | -ldflags "-s -w -X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}" \ 65 | -dest "build/pkg" \ 66 | -tags="${BUILD_TAGS}" \ 67 | "${DIR}/cmd/ethermint" 68 | 69 | echo "==> Renaming exe files..." 70 | for FILE in $(ls ./build/pkg); do 71 | f=${FILE#*-} 72 | if [[ $f == *"exe" ]] 73 | then 74 | f=${f%.*} 75 | fi 76 | echo "$f" 77 | mkdir -p "./build/pkg/$f" 78 | 79 | name="ethermint" 80 | if [[ $FILE == *"exe" ]] 81 | then 82 | name="ethermint.exe" 83 | fi 84 | echo $name 85 | 86 | pushd ./build/pkg 87 | mv "$FILE" "$f/$name" 88 | popd 89 | done 90 | 91 | # Zip all the files. 92 | echo "==> Packaging..." 93 | for PLATFORM in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type d); do 94 | OSARCH=$(basename "${PLATFORM}") 95 | echo "--> ${OSARCH}" 96 | 97 | pushd "$PLATFORM" >/dev/null 2>&1 98 | zip "../${OSARCH}.zip" ./* 99 | popd >/dev/null 2>&1 100 | done 101 | 102 | # Add "ethermint" and $VERSION prefix to package name. 103 | rm -rf ./build/dist 104 | mkdir -p ./build/dist 105 | for FILENAME in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type f); do 106 | FILENAME=$(basename "$FILENAME") 107 | cp "./build/pkg/${FILENAME}" "./build/dist/ethermint_${VERSION}_${FILENAME}" 108 | done 109 | 110 | # Make the checksums. 111 | pushd ./build/dist 112 | shasum -a256 ./* > "./ethermint_${VERSION}_SHA256SUMS" 113 | popd 114 | 115 | # Done 116 | echo 117 | echo "==> Results:" 118 | ls -hl ./build/dist 119 | 120 | exit 0 121 | -------------------------------------------------------------------------------- /scripts/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.6 2 | 3 | # This is the release of ethermint to pull in. 4 | ENV EM_VERSION 0.5.3 5 | # ENV EM_SHA256SUM c746b4485967af9f3bfd66238b1ca718a5d77baf2382cde027348313f502d34b 6 | 7 | ENV DATA_ROOT /ethermint 8 | ENV TENDERMINT_ADDR tcp://0.0.0.0:46657 9 | 10 | # Set user right away for determinism 11 | RUN addgroup emuser && \ 12 | adduser -S -G emuser emuser 13 | 14 | # Create directory for persistence and give our user ownership 15 | RUN mkdir -p $DATA_ROOT && \ 16 | chown -R emuser:emuser $DATA_ROOT 17 | 18 | # It is nice to have bash so the users can execute bash commands. 19 | RUN apk add --no-cache bash 20 | 21 | RUN apk add --no-cache openssl && \ 22 | wget https://s3.eu-central-1.amazonaws.com/ethermint/${EM_VERSION}/ethermint_${EM_VERSION}_linux-amd64.zip && \ 23 | # echo "${EM_SHA256SUM} ethermint_${EM_VERSION}_ethermint-linux-amd64.zip" | sha256sum -c && \ 24 | unzip -d /bin ethermint_${EM_VERSION}_linux-amd64.zip && \ 25 | rm -f ethermint_${EM_VERSION}_linux-amd64.zip 26 | 27 | # Expose the data directory as a volume since there's mutable state in there 28 | VOLUME $DATA_ROOT 29 | 30 | EXPOSE 46658 31 | 32 | CMD ethermint --datadir $DATA_ROOT --tendermint_addr $TENDERMINT_ADDR -------------------------------------------------------------------------------- /scripts/docker/Dockerfile.develop: -------------------------------------------------------------------------------- 1 | FROM alpine:3.6 2 | 3 | ENV DATA_ROOT /ethermint 4 | ENV TENDERMINT_ADDR tcp://0.0.0.0:46657 5 | 6 | RUN addgroup emuser && \ 7 | adduser -S -G emuser emuser 8 | 9 | RUN mkdir -p $DATA_ROOT && \ 10 | chown -R emuser:emuser $DATA_ROOT 11 | 12 | RUN apk add --no-cache bash 13 | 14 | ENV GOPATH /go 15 | ENV PATH "$PATH:/go/bin" 16 | RUN mkdir -p /go/src/github.com/tendermint/ethermint && \ 17 | apk add --no-cache go build-base git linux-headers && \ 18 | cd /go/src/github.com/tendermint/ethermint && \ 19 | git clone https://github.com/tendermint/ethermint . && \ 20 | git checkout develop && \ 21 | make get_vendor_deps && \ 22 | make install && \ 23 | glide cc && \ 24 | cd - && \ 25 | rm -rf /go/src/github.com/tendermint/ethermint && \ 26 | apk del go build-base git 27 | 28 | VOLUME $DATA_ROOT 29 | 30 | EXPOSE 46658 31 | 32 | #ENTRYPOINT ["ethermint"] 33 | 34 | CMD ethermint --datadir $DATA_ROOT --tendermint_addr $TENDERMINT_ADDR 35 | -------------------------------------------------------------------------------- /scripts/kubernetes/README.md: -------------------------------------------------------------------------------- 1 | Kuberenetes 2 | === 3 | 4 | ### Minikube 5 | `./start.sh N V S` will start N number of nodes, with V validators and S seed nodes. 6 | 7 | if you want to expose nodes, you can use `./expose.sh N` which will expose N number of nodes from running pods. 8 | It will assign random ports to each services. 9 | You can get these ports by running `minikube service tm-0 --url`, it will list links or `minikube service tm-0 --format {{.IP}}:{{.Port}}` where `tm-0` is first node. First IP/Port will be for GethRPC and second for Tendermint RPC. 10 | For example, after you've run `./expose N`: 11 | ``` 12 | $ minikube service tm-0 --url 13 | http://192.168.99.100:32451 # This will be Ethereum RPC Port 14 | http://192.168.99.100:31154 # This will be Tendermint RPC Port 15 | $ geth attach http://192.168.99.100:32451 # will lend you on tm-0 Ethereum RPC. 16 | ``` 17 | You can check exposed services by running `kubectl get service`. 18 | -------------------------------------------------------------------------------- /scripts/kubernetes/delete.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=`dirname ${BASH_SOURCE[0]}` 4 | 5 | echo "Deleting..." 6 | kubectl delete -f $DIR/ethermint.yaml 7 | 8 | echo "Deleteing Volume Claims" 9 | kubectl delete persistentvolumeclaim --selector="app=tm" 10 | kubectl delete service --selector "app=tm" 11 | -------------------------------------------------------------------------------- /scripts/kubernetes/ethermint.template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | annotations: 6 | service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" 7 | name: ethermint 8 | labels: 9 | app: ethermint 10 | spec: 11 | ports: 12 | - port: 46656 13 | name: p2p 14 | - port: 46657 15 | name: rpc 16 | - port: 8545 17 | name: geth-rpc 18 | clusterIP: None 19 | selector: 20 | app: tm 21 | --- 22 | apiVersion: v1 23 | kind: ConfigMap 24 | metadata: 25 | name: tm-config 26 | data: 27 | seeds: "{SEEDS}" 28 | validators: "{VALIDATORS}" 29 | validator.power: "10" 30 | genesis.json: |- 31 | { 32 | "genesis_time": "2017-01-02T10:10:10.164Z", 33 | "chain_id": "chain-B5XXm5", 34 | "validators": [], 35 | "app_hash": "" 36 | } 37 | pub_key_nginx.conf: |- 38 | server { 39 | listen 80 default_server; 40 | listen [::]:80 default_server ipv6only=on; 41 | location /pub_key.json { root /usr/share/nginx/; } 42 | } 43 | --- 44 | apiVersion: policy/v1beta1 45 | kind: PodDisruptionBudget 46 | metadata: 47 | name: tm-budget 48 | spec: 49 | selector: 50 | matchLabels: 51 | app: tm 52 | minAvailable: {V} 53 | --- 54 | apiVersion: apps/v1beta1 55 | kind: StatefulSet 56 | metadata: 57 | name: tm 58 | spec: 59 | serviceName: ethermint 60 | replicas: {REPLICAS} 61 | template: 62 | metadata: 63 | labels: 64 | app: tm 65 | version: v1 66 | annotations: 67 | pod.beta.kubernetes.io/init-containers: '[{ 68 | "name": "tm-gen-validator", 69 | "image": "adrianbrink/tendermint:develop", 70 | "imagePullPolicy": "IfNotPresent", 71 | "command": ["bash", "-c", " 72 | set -ex\n 73 | if [ ! -f /tendermint/priv_validator.json ]; then\n 74 | tendermint gen_validator > /tendermint/priv_validator.json\n 75 | # pub_key.json will be served by pub-key container\n 76 | cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n 77 | fi\n 78 | "], 79 | "volumeMounts": [ 80 | {"name": "tmdir", "mountPath": "/tendermint"} 81 | ] 82 | },{ 83 | "name": "eth-init", 84 | "image": "adrianbrink/ethermint:develop", 85 | "imagePullPolicy": "IfNotPresent", 86 | "command": ["bash", "-c", " 87 | set -ex\n 88 | cp -rv /ethinit/* /ethermint/\n 89 | ethermint --datadir /ethermint init /ethermint/genesis.json\n 90 | "], 91 | "volumeMounts": [ 92 | {"name": "ethroot", "mountPath": "/ethermint"}, 93 | {"name": "ethermint-setup", "mountPath": "/ethinit"} 94 | ] 95 | }]' 96 | spec: 97 | containers: 98 | - name: tm 99 | imagePullPolicy: IfNotPresent 100 | image: adrianbrink/tendermint:develop 101 | resources: 102 | requests: 103 | cpu: 50m 104 | memory: 128Mi 105 | limits: 106 | cpu: 100m 107 | memory: 256Mi 108 | ports: 109 | - containerPort: 46656 110 | name: p2p 111 | - containerPort: 46657 112 | name: rpc 113 | env: 114 | - name: SEEDS 115 | valueFrom: 116 | configMapKeyRef: 117 | name: tm-config 118 | key: seeds 119 | - name: VALIDATOR_POWER 120 | valueFrom: 121 | configMapKeyRef: 122 | name: tm-config 123 | key: validator.power 124 | - name: VALIDATORS 125 | valueFrom: 126 | configMapKeyRef: 127 | name: tm-config 128 | key: validators 129 | - name: TMHOME 130 | value: /tendermint 131 | command: 132 | - bash 133 | - "-c" 134 | - | 135 | set -ex 136 | 137 | # copy template 138 | cp /etc/tendermint/genesis.json /tendermint/genesis.json 139 | 140 | # fill genesis file with validators 141 | IFS=',' read -ra VALS_ARR <<< "$VALIDATORS" 142 | fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#') 143 | for v in "${VALS_ARR[@]}"; do 144 | # wait until validator generates priv/pub key pair 145 | set +e 146 | 147 | curl -s "http://$v.$fqdn_suffix/pub_key.json" > /dev/null 148 | ERR=$? 149 | while [ "$ERR" != 0 ]; do 150 | sleep 5 151 | curl -s "http://$v.$fqdn_suffix/pub_key.json" > /dev/null 152 | ERR=$? 153 | done 154 | set -e 155 | 156 | # add validator to genesis file along with its pub_key 157 | curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json 158 | cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json 159 | rm pub_validator.json 160 | done 161 | 162 | # construct seeds 163 | IFS=',' read -ra SEEDS_ARR <<< "$SEEDS" 164 | seeds=() 165 | for s in "${SEEDS_ARR[@]}"; do 166 | seeds+=("$s.$fqdn_suffix:46656") 167 | done 168 | seeds=$(IFS=','; echo "${seeds[*]}") 169 | 170 | tendermint node --p2p.seeds "$seeds" --moniker "`hostname`" --rpc.laddr "unix:///socks/app.sock" 171 | volumeMounts: 172 | - name: tmdir 173 | mountPath: /tendermint 174 | - mountPath: /etc/tendermint/genesis.json 175 | name: configdir 176 | subPath: genesis.json 177 | - name: socksdir 178 | mountPath: /socks 179 | 180 | - name: app 181 | imagePullPolicy: IfNotPresent 182 | image: adrianbrink/ethermint:develop 183 | ports: 184 | - containerPort: 8545 185 | name: geth-rpc 186 | volumeMounts: 187 | - name: socksdir 188 | mountPath: /socks 189 | - name: ethroot 190 | mountPath: /ethermint 191 | args: [ "ethermint", 192 | "--datadir", "/ethermint", 193 | "--tendermint_addr", "unix:///socks/app.sock", 194 | "--rpc", "--rpcapi", "eth,net,web3,personal,miner,admin,txpool", 195 | "--rpcaddr", "0.0.0.0", "--rpcport", "8545" 196 | ] 197 | 198 | - name: pub-key 199 | imagePullPolicy: IfNotPresent 200 | image: nginx:1.11.9 201 | resources: 202 | requests: 203 | cpu: 10m 204 | memory: 12Mi 205 | limits: 206 | cpu: 20m 207 | memory: 24Mi 208 | ports: 209 | - containerPort: 80 210 | name: pub-key 211 | command: 212 | - bash 213 | - "-c" 214 | - | 215 | set -ex 216 | # fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied)) 217 | # => we cannot serve from /tendermint, so we copy the file 218 | mkdir -p /usr/share/nginx 219 | cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json 220 | nginx -g "daemon off;" 221 | volumeMounts: 222 | - name: tmdir 223 | mountPath: /tendermint 224 | - mountPath: /etc/nginx/conf.d/pub_key.conf 225 | name: configdir 226 | subPath: pub_key_nginx.conf 227 | 228 | volumes: 229 | - name: configdir 230 | configMap: 231 | name: tm-config 232 | - name: socksdir 233 | emptyDir: {} 234 | - name: ethermint-setup 235 | hostPath: 236 | path: {HOSTPATH} 237 | 238 | volumeClaimTemplates: 239 | - metadata: 240 | name: tmdir 241 | annotations: 242 | volume.alpha.kubernetes.io/storage-class: anything 243 | spec: 244 | accessModes: ["ReadWriteOnce"] 245 | resources: 246 | requests: 247 | storage: 2Gi 248 | - metadata: 249 | name: ethroot 250 | annotations: 251 | volume.alpha.kubernetes.io/storage-class: anything 252 | spec: 253 | accessModes: ["ReadWriteOnce"] 254 | resources: 255 | requests: 256 | storage: 2Gi 257 | -------------------------------------------------------------------------------- /scripts/kubernetes/expose.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | N=$1 4 | 5 | if [ -z $N ]; then 6 | echo "Provide Number of nodes to expose" 7 | exit 8 | fi 9 | 10 | for i in `seq 0 $(($N-1))`; do 11 | kubectl expose pod tm-$i --type NodePort --port 8545,46657 12 | done 13 | -------------------------------------------------------------------------------- /scripts/kubernetes/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | N=$1 # Number of Nodes 4 | V=$2 # Number of Validators 5 | S=$3 # Number of Seeds 6 | 7 | DIR=`dirname ${BASH_SOURCE[0]}` 8 | 9 | usage() { 10 | echo -e '\nUsage: \c' 11 | echo './start.sh N V S' 12 | echo ' N - Number of Nodes, should be more than 3' 13 | echo ' V - Number of Validators, should be more than 3' 14 | echo ' S - Number of Seeds, should be more than 1' 15 | echo 16 | echo 'Example:' 17 | echo -e '\t./start.sh 10 5 2 \c' 18 | echo -e '-- This will start 10 nodes, 5 of them as validators and first 2 will be used as Seeds' 19 | } 20 | 21 | if [ -z $N ]; then 22 | usage 23 | exit 1 24 | fi 25 | 26 | if [ -z $V ] || [ $V -gt $N ]; then 27 | V=$N 28 | fi 29 | 30 | if [ $N -lt 4 ] || [ $V -lt 4 ]; then 31 | echo "Minimum number of nodes and validators should be 4" 32 | usage 33 | exit 2 34 | fi 35 | 36 | if [ -z $S ] || [ $S -lt 1 ] || [ $S -gt $N ]; then 37 | S=$V 38 | fi 39 | 40 | SEEDS="tm-0" 41 | VALIDATORS="tm-0" 42 | 43 | if [ $S -gt 1 ]; then 44 | for i in `seq 1 $(($S-1))`; do 45 | SEEDS="$SEEDS,tm-$i" 46 | done 47 | fi 48 | 49 | for i in `seq 1 $(($V-1))`; do 50 | VALIDATORS="$VALIDATORS,tm-$i" 51 | done 52 | 53 | echo "Nodes: $N, Validators: $V, SEEDS: $S" 54 | SETUP_DIR=$(realpath `basename $DIR`/../../setup) 55 | 56 | cat $DIR/ethermint.template.yaml \ 57 | | sed "s/{SEEDS}/$SEEDS/; s/{VALIDATORS}/$VALIDATORS/; s/{REPLICAS}/$N/;" \ 58 | | sed "s/{S}/$S/; s/{N}/$N/; s/{V}/$V/;" \ 59 | | sed "s#{HOSTPATH}#$SETUP_DIR#;" \ 60 | > $DIR/ethermint.yaml 61 | 62 | kubectl create -f $DIR/ethermint.yaml 63 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION=$1 5 | DIST_DIR=./build/dist 6 | 7 | # Get the version from the environment, or try to figure it out. 8 | if [ -z $VERSION ]; then 9 | VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) 10 | fi 11 | if [ -z "$VERSION" ]; then 12 | echo "Please specify a version." 13 | exit 1 14 | fi 15 | echo "==> Copying ${DIST_DIR} to S3..." 16 | 17 | # copy to s3 18 | aws s3 cp --recursive ${DIST_DIR} s3://tendermint/binaries/ethermint/v${VERSION} --acl public-read 19 | 20 | exit 0 21 | -------------------------------------------------------------------------------- /setup/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 15, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "nonce": "0xdeadbeefdeadbeef", 9 | "timestamp": "0x00", 10 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 11 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 12 | "difficulty": "0x40", 13 | "gasLimit": "0x8000000", 14 | "alloc": { 15 | "0x7eff122b94897ea5b0e2a9abf47b86337fafebdc": { "balance": "10000000000000000000000000000000000" }, 16 | "0xc6713982649D9284ff56c32655a9ECcCDA78422A": { "balance": "10000000000000000000000000000000000" } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /setup/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc: -------------------------------------------------------------------------------- 1 | {"address":"7eff122b94897ea5b0e2a9abf47b86337fafebdc","crypto":{"cipher":"aes-128-ctr","ciphertext":"19de8a919e2f4cbdde2b7352ebd0be8ead2c87db35fc8e4c9acaf74aaaa57dad","cipherparams":{"iv":"ba2bd370d6c9d5845e92fbc6f951c792"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c7cc2380a96adc9eb31d20bd8d8a7827199e8b16889582c0b9089da6a9f58e84"},"mac":"ff2c0caf051ca15d8c43b6f321ec10bd99bd654ddcf12dd1a28f730cc3c13730"},"id":"f86a62b4-0621-4616-99af-c4b7f38fcc48","version":3} -------------------------------------------------------------------------------- /strategies/miner/reward_constant.go: -------------------------------------------------------------------------------- 1 | package minerRewardStrategies 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | ) 6 | 7 | // RewardConstant is a possible strategy 8 | type RewardConstant struct { 9 | } 10 | 11 | // Receiver returns which address should receive the mining reward 12 | func (r *RewardConstant) Receiver() common.Address { 13 | return common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8") 14 | } 15 | -------------------------------------------------------------------------------- /strategies/validators/tx_based.go: -------------------------------------------------------------------------------- 1 | package validatorStrategies 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ethTypes "github.com/ethereum/go-ethereum/core/types" 8 | "github.com/ethereum/go-ethereum/log" 9 | 10 | "github.com/tendermint/abci/types" 11 | ) 12 | 13 | // TxBasedValidatorsStrategy represents a strategy to reward validators with CETH 14 | type TxBasedValidatorsStrategy struct { 15 | currentValidators []*types.Validator 16 | } 17 | 18 | // SetValidators updates the current validators 19 | func (strategy *TxBasedValidatorsStrategy) SetValidators(validators []*types.Validator) { 20 | strategy.currentValidators = validators 21 | } 22 | 23 | // CollectTx collects the rewards for a transaction 24 | func (strategy *TxBasedValidatorsStrategy) CollectTx(tx *ethTypes.Transaction) { 25 | if reflect.DeepEqual(tx.To(), common.HexToAddress("0000000000000000000000000000000000000001")) { 26 | log.Info("Adding validator", "data", tx.Data()) 27 | strategy.currentValidators = append( 28 | strategy.currentValidators, 29 | &types.Validator{ 30 | PubKey: tx.Data(), 31 | Power: tx.Value().Int64(), 32 | }, 33 | ) 34 | } 35 | } 36 | 37 | // GetUpdatedValidators returns the current validators 38 | func (strategy *TxBasedValidatorsStrategy) GetUpdatedValidators() []*types.Validator { 39 | return strategy.currentValidators 40 | } 41 | -------------------------------------------------------------------------------- /tests/benchmarking/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers": [ "http://localhost:8545" ], 3 | "address": "0xb6b29ef90120bec597939e0eda6b8a9164f75deb", 4 | "wallet": {"address":"7eff122b94897ea5b0e2a9abf47b86337fafebdc","crypto":{"cipher":"aes-128-ctr","ciphertext":"19de8a919e2f4cbdde2b7352ebd0be8ead2c87db35fc8e4c9acaf74aaaa57dad","cipherparams":{"iv":"ba2bd370d6c9d5845e92fbc6f951c792"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c7cc2380a96adc9eb31d20bd8d8a7827199e8b16889582c0b9089da6a9f58e84"},"mac":"ff2c0caf051ca15d8c43b6f321ec10bd99bd654ddcf12dd1a28f730cc3c13730"},"id":"f86a62b4-0621-4616-99af-c4b7f38fcc48","version":3}, 5 | "password": "1234", 6 | "n": 100, 7 | "accounts": 5, 8 | "blockTimeout": 20 9 | } 10 | -------------------------------------------------------------------------------- /tests/benchmarking/multipleAccounts.js: -------------------------------------------------------------------------------- 1 | const config = require('config') 2 | const Web3 = require('web3') 3 | const Wallet = require('ethereumjs-wallet') 4 | const utils = require('./utils') 5 | const async = require('async') 6 | const Web3pool = require('./web3pool') 7 | 8 | const web3p = new Web3pool(config.get('providers')) 9 | const web3 = web3p.web3 10 | const wallet = Wallet.fromV3(config.get('wallet'), config.get('password')) 11 | const totalAccounts = config.get('accounts') 12 | 13 | const walletAddress = wallet.getAddressString() 14 | 15 | const gasPrice = web3.eth.gasPrice 16 | const totalTxs = config.get('n') 17 | const balance = web3.eth.getBalance(walletAddress) 18 | const costPerAccount = utils.calculateTransactionsPrice(gasPrice, totalTxs) 19 | const distributingCost = utils.calculateTransactionsPrice(gasPrice, totalAccounts) 20 | const totalCost = distributingCost.plus(costPerAccount.times(totalAccounts)) 21 | 22 | console.log(`Send ${totalTxs} transactions from each account, accounts: ${totalAccounts}`) 23 | console.log(`Cost of each account txs: ${web3.fromWei(costPerAccount, 'ether')}`) 24 | console.log(`Distributing cost: ${web3.fromWei(distributingCost, 'ether')}`) 25 | console.log(`Cost of all: ${web3.fromWei(totalCost, 'ether')}`) 26 | 27 | if (totalCost.comparedTo(balance) > 0) { 28 | throw new Error(`Unsufficient funds: ${web3.fromWei(balance, 'ether')}`) 29 | } 30 | 31 | let privKey = wallet.getPrivateKey() 32 | 33 | console.log('Generating wallets and sending funds') 34 | let wallets = [] 35 | let nonce = web3.eth.getTransactionCount(walletAddress) 36 | 37 | for (let i = 0; i < totalAccounts; i++) { 38 | wallets.push(Wallet.generate()) 39 | 40 | let tx = utils.generateTransaction({ 41 | nonce: nonce + i, 42 | gasPrice: gasPrice, 43 | from: walletAddress, 44 | to: wallets[i].getAddressString(), 45 | value: costPerAccount, 46 | privKey: privKey 47 | }) 48 | 49 | web3.eth.sendRawTransaction(tx) 50 | } 51 | console.log('Distributed Funds.') 52 | 53 | // wait for 200 blocks this case. Initial Fund distribution 54 | utils.waitMultipleProcessedInterval(web3p, 100, 200, (err, endDate) => { 55 | if (err) { 56 | console.error(err) 57 | return 58 | } 59 | // Send Transactions 60 | let dest = config.get('address') 61 | let initialNonces = {} 62 | wallets.forEach((w) => { 63 | let addr = w.getAddressString() 64 | initialNonces[addr] = web3.eth.getTransactionCount(addr) 65 | }) 66 | 67 | let runTransactionsFromAccount = (wallet, cb) => { 68 | let address = wallet.getAddressString() 69 | let privKey = wallet.getPrivateKey() 70 | let initialNonce = initialNonces[address] 71 | let transactions = [] 72 | let totalTxs = config.get('n') 73 | 74 | console.log(`Generating ${totalTxs} transactions for ${address}`) 75 | for (let i = 0; i < totalTxs; i++) { 76 | let tx = utils.generateTransaction({ 77 | nonce: initialNonce + i, 78 | gasPrice: gasPrice, 79 | from: address, 80 | to: dest, 81 | privKey: privKey 82 | }) 83 | 84 | transactions.push(tx) 85 | } 86 | console.log(`Generated, starting sending transactions from ${address}`) 87 | 88 | utils.sendTransactions(web3p, transactions, cb) 89 | } 90 | 91 | const start = new Date() 92 | const blockTimeout = config.get('blockTimeout') 93 | async.parallel( 94 | wallets.map((w) => runTransactionsFromAccount.bind(null, w)), 95 | (err, res) => { 96 | if (err) { 97 | console.log('Error on transactions:', err) 98 | return 99 | } 100 | 101 | utils.waitMultipleProcessedInterval(web3p, 100, blockTimeout, (err, endDate) => { 102 | if (err) { 103 | console.error('Couldn\'t process transactions in blocks') 104 | console.error(err) 105 | return 106 | } 107 | 108 | let sent = totalTxs * totalAccounts 109 | let processed = Object.keys(initialNonces).reduce((sum, addr) => { 110 | return sum + (web3.eth.getTransactionCount(addr) - initialNonces[addr]) 111 | }, 0) 112 | let timePassed = (endDate - start) / 1000 113 | let perSecond = processed / timePassed 114 | 115 | console.log(`Processed ${processed} of ${sent} transactions ` + 116 | `from one account in ${timePassed}s, ${perSecond} tx/s`) 117 | }) 118 | } 119 | ) 120 | }) 121 | -------------------------------------------------------------------------------- /tests/benchmarking/oneAccount.js: -------------------------------------------------------------------------------- 1 | const config = require('config') 2 | const Wallet = require('ethereumjs-wallet') 3 | const Web3pool = require('./web3pool') 4 | const utils = require('./utils') 5 | 6 | const web3p = new Web3pool(config.get('providers')) 7 | const web3 = web3p.web3 8 | const wallet = Wallet.fromV3(config.get('wallet'), config.get('password')) 9 | 10 | const walletAddress = wallet.getAddressString() 11 | const initialNonce = web3.eth.getTransactionCount(walletAddress) 12 | const totalTxs = config.get('n') 13 | const blockTimeout = config.get('blockTimeout') 14 | 15 | const transactions = [] 16 | 17 | console.log('Current block number:', web3.eth.blockNumber) 18 | console.log(`Will send ${totalTxs} transactions and wait for ${blockTimeout} blocks`) 19 | 20 | let privKey = wallet.getPrivateKey() 21 | let dest = config.get('address') 22 | let gasPrice = web3.eth.gasPrice 23 | 24 | let cost = utils.calculateTransactionsPrice(gasPrice, totalTxs) 25 | let balance = web3.eth.getBalance(walletAddress) 26 | 27 | if (cost.comparedTo(balance) > 0) { 28 | let error = `You don't have enough money to make ${totalTxs} transactions, ` + 29 | `it needs ${cost} wei, but you have ${balance}` 30 | throw new Error(error) 31 | } 32 | 33 | console.log(`Generating ${totalTxs} transactions`) 34 | for (let i = 0; i < totalTxs; i++) { 35 | let nonce = i + initialNonce 36 | let tx = utils.generateTransaction({ 37 | from: walletAddress, 38 | to: dest, 39 | privKey: privKey, 40 | nonce: nonce, 41 | gasPrice: gasPrice 42 | }) 43 | 44 | transactions.push(tx) 45 | } 46 | console.log('Generated.') 47 | 48 | // Send transactions 49 | const start = new Date() 50 | utils.sendTransactions(web3p, transactions, (err, ms) => { 51 | if (err) { 52 | console.error('Couldn\'t send Transactions:') 53 | console.error(err) 54 | return 55 | } 56 | 57 | utils.waitProcessedInterval(web3, 100, blockTimeout, (err, endDate) => { 58 | if (err) { 59 | console.error('Couldn\'t process transactions in blocks') 60 | console.error(err) 61 | return 62 | } 63 | 64 | let sent = transactions.length 65 | let processed = web3.eth.getTransactionCount(walletAddress) - initialNonce 66 | let timePassed = (endDate - start) / 1000 67 | let perSecond = processed / timePassed 68 | 69 | console.log(`Processed ${processed} of ${sent} transactions ` + 70 | `from one account in ${timePassed}s, ${perSecond} tx/s`) 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /tests/benchmarking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarking", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "node main.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "async": "^2.5.0", 13 | "bignumber.js": "^4.0.2", 14 | "config": "^1.26.1", 15 | "ethereumjs-tx": "^1.3.1", 16 | "ethereumjs-wallet": "^0.6.0", 17 | "mocha": "^3.4.2", 18 | "web3": "^0.19.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/benchmarking/utils.js: -------------------------------------------------------------------------------- 1 | const BigNumber = require('bignumber.js') 2 | const Tx = require('ethereumjs-tx') 3 | const async = require('async') 4 | const Web3pool = require('./web3pool') 5 | 6 | exports.getWeb3 = (web3o) => { 7 | if (web3o instanceof Web3pool) { 8 | return web3o.web3 9 | } 10 | 11 | return web3o 12 | } 13 | 14 | exports.generateTransaction = (txObject) => { 15 | const txParams = { 16 | nonce: '0x' + txObject.nonce.toString(16), 17 | gasPrice: '0x' + txObject.gasPrice.toString(16), 18 | gas: '0x' + new BigNumber(21024).toString(16), 19 | from: txObject.from, 20 | to: txObject.to, 21 | value: txObject.value ? '0x' + txObject.value.toString(16) : '0x00', 22 | data: '0x' 23 | } 24 | 25 | let tx = new Tx(txParams) 26 | tx.sign(txObject.privKey) 27 | 28 | return '0x' + tx.serialize().toString('hex') 29 | } 30 | 31 | exports.waitProcessedInterval = function (web3, intervalMs, blockTimeout, cb) { 32 | let startingBlock = web3.eth.blockNumber 33 | 34 | console.log('Starting block:', startingBlock) 35 | let interval = setInterval(() => { 36 | let blocksGone = web3.eth.blockNumber - startingBlock 37 | if (blocksGone > blockTimeout) { 38 | clearInterval(interval) 39 | cb(new Error(`Pending full after ${blockTimeout} blocks`)) 40 | return 41 | } 42 | 43 | let status = web3.txpool.status 44 | console.log(`Blocks Passed ${blocksGone}, ` + 45 | `Pending Txs: ${status.pending}, Queued Txs: ${status.queued}`) 46 | 47 | if (status.pending === 0 && status.queued === 0) { 48 | clearInterval(interval) 49 | cb(null, new Date()) 50 | } 51 | }, intervalMs || 100) 52 | } 53 | 54 | exports.waitMultipleProcessedInterval = (web3p, intervalMs, blockTimeout, cb) => { 55 | let waitAll = web3p.web3s.map((web3) => { 56 | return exports.waitProcessedInterval.bind(null, web3, intervalMs, blockTimeout) 57 | }) 58 | 59 | async.parallel(waitAll, (err, ms) => { 60 | if (err) { 61 | return cb(err) 62 | } 63 | 64 | cb(null, new Date()) 65 | }) 66 | } 67 | 68 | exports.sendTransactions = (web3, transactions, cb) => { 69 | let start = new Date() 70 | async.series(transactions.map((tx) => { 71 | let w3 = exports.getWeb3(web3) 72 | return w3.eth.sendRawTransaction.bind(null, tx) 73 | }), (err) => { 74 | if (err) { 75 | return cb(err) 76 | } 77 | 78 | cb(null, new Date() - start) 79 | }) 80 | } 81 | 82 | exports.calculateTransactionsPrice = (gasPrice, txcount) => { 83 | let gas = 21024 // Simple transaction gas requirement 84 | 85 | return new BigNumber(gasPrice).times(gas).times(txcount) 86 | } 87 | -------------------------------------------------------------------------------- /tests/benchmarking/web3pool.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3') 2 | 3 | class Web3pool { 4 | constructor (providers, selector) { 5 | this.providers = Web3pool.resolveProviders(providers) 6 | this.web3s = this.providers.map((p) => new Web3(p)) 7 | this.selector = selector || Web3pool.RR() 8 | 9 | this.web3s.forEach((web3) => { 10 | console.log('extended web3') 11 | extendWeb3(web3) 12 | }) 13 | } 14 | 15 | get web3 () { 16 | return this.selector(this.web3s) 17 | } 18 | 19 | /** 20 | * IPCProvider is not supported 21 | * For IPC every call should be async, there are some sync requests tests are using 22 | */ 23 | static resolveProviders (providers) { 24 | return providers.map((provider) => { 25 | if (/^http/.test(provider)) { 26 | return new Web3.providers.HttpProvider(provider) 27 | } 28 | 29 | return null 30 | }).filter((p) => p != null) 31 | } 32 | 33 | // Default selector 34 | static RR () { 35 | let i = 0 36 | 37 | return (web3s) => { 38 | return web3s[i++ % web3s.length] 39 | } 40 | } 41 | } 42 | 43 | function extendWeb3 (web3) { 44 | web3._extend({ 45 | property: 'txpool', 46 | methods: [], 47 | properties: [ 48 | new web3._extend.Property({ 49 | name: 'status', 50 | getter: 'txpool_status', 51 | outputFormatter: function (status) { 52 | status.pending = web3._extend.utils.toDecimal(status.pending) 53 | status.queued = web3._extend.utils.toDecimal(status.queued) 54 | return status 55 | } 56 | }) 57 | ] 58 | }) 59 | } 60 | 61 | module.exports = Web3pool 62 | -------------------------------------------------------------------------------- /tests/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.8.3 2 | 3 | RUN mkdir /ethermint && \ 4 | chmod 777 /ethermint 5 | 6 | # Setup ethermint repo 7 | ENV REPO $GOPATH/src/github.com/tendermint/ethermint 8 | WORKDIR $REPO 9 | 10 | # Install the vendored dependencies before copying code 11 | # docker caching prevents reinstall on code change! 12 | ADD glide.yaml glide.yaml 13 | ADD glide.lock glide.lock 14 | ADD Makefile Makefile 15 | RUN make get_vendor_deps 16 | 17 | # Now copy in the code 18 | COPY . $REPO 19 | 20 | RUN make install 21 | 22 | RUN mkdir -p /ethermint/setup 23 | COPY setup/genesis.json /ethermint/setup/ 24 | RUN ethermint -datadir /ethermint/data init /ethermint/setup/genesis.json 25 | COPY setup/keystore /ethermint/data/keystore 26 | 27 | # expose the volume for debugging 28 | VOLUME $REPO 29 | 30 | EXPOSE 46658 31 | EXPOSE 8545 32 | EXPOSE 8546 33 | -------------------------------------------------------------------------------- /tests/docker/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | docker build --no-cache -t ethermint_tester -f ./tests/docker/Dockerfile . 4 | -------------------------------------------------------------------------------- /tests/integration/truffle/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | RUN apk add --update git && \ 4 | rm -rf /tmp/* /var/cache/apk/* 5 | 6 | RUN mkdir -p /usr/src/app 7 | WORKDIR /usr/src/app 8 | 9 | COPY package.json /usr/src/app/ 10 | RUN npm install && npm cache clean --force 11 | COPY . /usr/src/app 12 | -------------------------------------------------------------------------------- /tests/integration/truffle/README.md: -------------------------------------------------------------------------------- 1 | Truffle Tests Runner 2 | === 3 | 4 | ## Dependencies 5 | - Node.JS/NPM 6 | - truffle 7 | - ethermint 8 | - geth 9 | 10 | Usage: 11 | `npm i` to install dependencies then 12 | `./compare.sh file` and `node diff.js` 13 | 14 | `file` format: Each file contains repo and the directory in that repo, which should contain truffle tests and configs. 15 | Checkout `file-example`. 16 | 17 | `compare.sh` will setup directory in /tmp/testnet folder, run ethermint and tests against it. Then it will create ethermint folder, run it and test against it. Test repositories are cloned into directory /tmp/truffle-tests and test outputs are saved. `./diff.sh` will return for outputs, empty response means both are same(success). 18 | 19 | ## Test 20 | [Truffle Initial Repo](https://github.com/trufflesuite/truffle-init-default) tests were run against both networks. It contains simple MetaCoin implementation with it's test. 21 | Diff tool checks whether the results are the same for `ethereum` and `ethermint`, if both networks fail certain test then it's expected behaviour. 22 | 23 | ## Gas limit test 24 | 25 | Run `npm test` -------------------------------------------------------------------------------- /tests/integration/truffle/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | docker build --no-cache -t ethermint_js_test -f ./tests/integration/truffle/Dockerfile ./tests/integration/truffle 4 | -------------------------------------------------------------------------------- /tests/integration/truffle/compare.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | shopt -s huponexit 3 | 4 | TMPDIR=/tmp/truffle-tests 5 | TESTNAME=test1 6 | TRUFFLE=$1 7 | 8 | if [ "$TRUFFLE" == "" ] || [ ! -f $TRUFFLE ] 9 | then 10 | echo "pass truffle repos file" 11 | exit 1 12 | fi 13 | 14 | ethermint version 15 | geth version 16 | 17 | # Start Ethereuum 18 | cd deploytools 19 | echo "Starting Ethereum" 20 | ./clean.sh && ./setup-ethereum.sh $TESTNAME 21 | ./run-ethereum.sh $TESTNAME & 22 | sleep 20 23 | 24 | # Run Tests 25 | cd .. 26 | echo "Running Tests" 27 | ./ether-test.sh $TRUFFLE $TMPDIR/$TESTNAME.output.ethereum 28 | pkill -INT -P $(pgrep -P $!) 29 | sleep 30 30 | 31 | ## Start Ethermint 32 | cd deploytools 33 | echo "Starting Ethermint" 34 | ./clean.sh && ./setup-ethermint.sh $TESTNAME 35 | ./run-ethermint.sh $TESTNAME & 36 | sleep 20 37 | 38 | ## Run Tests 39 | cd .. 40 | echo "Running Tests" 41 | ./ether-test.sh $TRUFFLE $TMPDIR/$TESTNAME.output.ethermint 42 | pkill -INT -P $(pgrep -P $!) 43 | 44 | ./deploytools/clean.sh 45 | -------------------------------------------------------------------------------- /tests/integration/truffle/config/default.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | host: "localhost", 5 | port: 8545, 6 | } -------------------------------------------------------------------------------- /tests/integration/truffle/config/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | if (!process.env.WEB3_HOST) { 4 | throw new Error('Please define WEB3_HOST env variable') 5 | } 6 | if (!process.env.WEB3_PORT) { 7 | throw new Error('Please define WEB3_PORT env variable') 8 | } 9 | 10 | module.exports = { 11 | host: process.env.WEB3_HOST, 12 | port: process.env.WEB3_PORT, 13 | } -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | DIR=$(dirname ${BASH_SOURCE[0]}) 5 | source $DIR/vars.sh 6 | 7 | echo "removing root directory: $TMPROOT" 8 | rm -rvf /tmp/$TMPROOT/* 9 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/ethgen.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 18, 4 | "homesteadBlock": 0, 5 | "eip155Block": 0, 6 | "eip158Block": 0 7 | }, 8 | "nonce": "0xdeadbeefdeadbeef", 9 | "timestamp": "0x00", 10 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 11 | "extraData": "0x00", 12 | "gasLimit": "0x80000000", 13 | "difficulty": "0x400", 14 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 15 | "coinbase": "0x3333333333333333333333333333333333333333", 16 | "alloc": { 17 | "0x0161e041aad467a890839d5b08b138c1e6373072": { "balance": "100000000000000000000" }, 18 | "0x77e0d0f7b13e4b322c08f6e5b74ea1ef5844102a": { "balance": "100000000000000000000" }, 19 | "0x85418fff140231f43e7028af2588fa3e5b614c4d": { "balance": "100000000000000000000" }, 20 | "0x87da6a8c6e9eff15d703fc2773e32f6af8dbe301": { "balance": "100000000000000000000" }, 21 | "0xac640610807f2f1a1ad1b88e0af869e9f7ff44bc": { "balance": "100000000000000000000" }, 22 | "0xb51a16ed5000cbff1e9a0c9a497e94e6988b917c": { "balance": "100000000000000000000" }, 23 | "0xb97de4b8c857e4f6bc354f226dc3249aaee49209": { "balance": "100000000000000000000" }, 24 | "0xcf6abdcbd268c680c58e20a146985b3a2d989c13": { "balance": "100000000000000000000" }, 25 | "0xd465119e045a107f7e62a8ed40ee7bf8c9d37109": { "balance": "100000000000000000000" }, 26 | "0xe11c86a37c4403c9a0e510815fe1200fea17d1a3": { "balance": "100000000000000000000" }, 27 | "0xe30c37dc10b70f642bfa52c9d3a05484a817339d": { "balance": "100000000000000000000" }, 28 | "0xf85f61d4466c4808748e288ba1999fb5e834c38e": { "balance": "100000000000000000000" }, 29 | "0xf92adccbff93802b78e24a7a04186c9181c13d76": { "balance": "100000000000000000000" } 30 | } 31 | } 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "app_hash": "", 3 | "chain_id": "test-network", 4 | "genesis_time": "0001-01-01T00:00:00.000Z", 5 | "validators": [{ 6 | "pub_key": { 7 | "type": "ed25519", 8 | "data": "BDD0D78AEB73A5A13B056516CF90828AF8C0EE8C9ECBC58F98729BEE0A0A8868" 9 | }, 10 | "amount": 10, 11 | "name": "test-node" 12 | }] 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/0161e041aad467a890839d5b08b138c1e6373072: -------------------------------------------------------------------------------- 1 | {"address":"0161e041aad467a890839d5b08b138c1e6373072","crypto":{"cipher":"aes-128-ctr","ciphertext":"855d64390f433adee9056c3ec9db899598e53ed3454ecb4ec769877fd5cc5824","cipherparams":{"iv":"5c8273c6d1f5ac0b18caebe31c76445b"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"94c8b8dbb870c2aab91673f430589dbd198e9b5de42e1c755c5aff90a06df504"},"mac":"a98037d7e50ce7eaeb80b6a9eea3abf7d25390b2bd33d558c78c8afe6d4cdf2d"},"id":"96e1d468-3680-4ef5-8628-45dcd112d520","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/77e0d0f7b13e4b322c08f6e5b74ea1ef5844102a: -------------------------------------------------------------------------------- 1 | {"address":"77e0d0f7b13e4b322c08f6e5b74ea1ef5844102a","crypto":{"cipher":"aes-128-ctr","ciphertext":"df860cbc91863966395ec8251170fe3284b2e0414df5ee42b1be82fb50c45f3b","cipherparams":{"iv":"10705b6e9301965f77229ae000d0fb14"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4eb911b13964b6e66fff179a89224a046c96a8370f2150d7a12464551cd588cd"},"mac":"64d9bcc78dfbedfc6b15395bb98d16c75807ab23360cd62e9d30049f7bd10c24"},"id":"2e293d57-52af-4443-989c-6ce9f56d7063","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/85418fff140231f43e7028af2588fa3e5b614c4d: -------------------------------------------------------------------------------- 1 | {"address":"85418fff140231f43e7028af2588fa3e5b614c4d","crypto":{"cipher":"aes-128-ctr","ciphertext":"541ada10d16271ce2a4f994b5e552f5a451de5787170926741bd8bb08e630d52","cipherparams":{"iv":"76ac248f39275083b431e4a202e5dc46"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"39ab6c642c7c99a79acfaa6fcbb9bdbfa261f5fa9ba6b7939eaf7f4a1957ce9f"},"mac":"e1bd96eac552b74f78dbd4b44df53470c419958bad71a174bb17d3831a889d6a"},"id":"af3b47b7-3de4-4722-b0df-773da380b97f","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/87da6a8c6e9eff15d703fc2773e32f6af8dbe301: -------------------------------------------------------------------------------- 1 | {"address":"87da6a8c6e9eff15d703fc2773e32f6af8dbe301","crypto":{"cipher":"aes-128-ctr","ciphertext":"f95f4da94a607b15fa4a00ad634ff892269ea498f43d7a6f5dcac6fc63ab785d","cipherparams":{"iv":"32d5f1486e4d80f08cf18252635e25c4"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4a9f0530b03c8e2e63a25824e895b430dcc054c4b79ec9f582f49f6c1007f534"},"mac":"05bcfd4446dad7ab4314e69902fe78a98e8832b66152f876d00521772928e4de"},"id":"c7d6a462-892c-476f-933b-4f01eb021865","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/ac640610807f2f1a1ad1b88e0af869e9f7ff44bc: -------------------------------------------------------------------------------- 1 | {"address":"ac640610807f2f1a1ad1b88e0af869e9f7ff44bc","crypto":{"cipher":"aes-128-ctr","ciphertext":"9b348de729059b2721eb7c22b09300f8a14c91e4c4d7db52474185896d0c4816","cipherparams":{"iv":"8f64e9601b2177a9fa1a90283b59bbf0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ea83406091195c1b1c994660df8dbfc758e825baf0a9a904b8d91f6d512e24f0"},"mac":"2905febcd7b6624d357d4b6ffdc5f5c1295a539dcf837db0009dd95a0ebcb336"},"id":"36425cea-96eb-4268-95c9-f848d613c9ca","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/b51a16ed5000cbff1e9a0c9a497e94e6988b917c: -------------------------------------------------------------------------------- 1 | {"address":"b51a16ed5000cbff1e9a0c9a497e94e6988b917c","crypto":{"cipher":"aes-128-ctr","ciphertext":"e9e5c5675c34235a44d2ffccba64b392e84bbbe90fdd5ecaf1b2cd6990222408","cipherparams":{"iv":"386e6d0d3d05421438313d19410d74af"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"cd354e9ebbe20786ca7b0a2db11e0f3ff041b9faf2634d8f1eb8daa3e89d9142"},"mac":"18f28d4c7e0541d3c3264855f85f24f3b2708ae20713a6565d46d746b3fc0a8f"},"id":"ad330e98-aa60-424d-aaee-f2ef279b8245","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/b97de4b8c857e4f6bc354f226dc3249aaee49209: -------------------------------------------------------------------------------- 1 | {"address":"b97de4b8c857e4f6bc354f226dc3249aaee49209","crypto":{"cipher":"aes-128-ctr","ciphertext":"37c0b5d56292a74172e92bd50f26cbdfa3d3776c4c5accfd2c53f32a3ad86176","cipherparams":{"iv":"0023b6b9bcba48cf1a552be9381bb8ba"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f346fa2c8ab7f3f529e5b7ed2555def4dfea94aa7d9958c3f9371bec6f9c3d6c"},"mac":"81c9cdf3d5d112bea1c39387eed437b42fdb51f6cc785a3414fdac43da901346"},"id":"ff1a3321-a39e-41df-b566-2d76d7f60055","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/cf6abdcbd268c680c58e20a146985b3a2d989c13: -------------------------------------------------------------------------------- 1 | {"address":"cf6abdcbd268c680c58e20a146985b3a2d989c13","crypto":{"cipher":"aes-128-ctr","ciphertext":"024434000a2081bf71161fa7aade81a0af2d3bb13d899fe6719fb2cf3d78bde6","cipherparams":{"iv":"c542d35091ecbca94157ee7d0c4ee692"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"3feb6deffec3091fdee1a00d757efcbb00d92794100698aa10e7cbd4ced42ae4"},"mac":"8eabcf6dbd32041d958266f42c206804a11eb5bcf2e2f29dcce485b6e5b898bc"},"id":"f02793b1-ef0e-431e-8fec-55212c79a227","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/d465119e045a107f7e62a8ed40ee7bf8c9d37109: -------------------------------------------------------------------------------- 1 | {"address":"d465119e045a107f7e62a8ed40ee7bf8c9d37109","crypto":{"cipher":"aes-128-ctr","ciphertext":"2fcd2ba610226fa79179cefed2406f3152fc60078870b226af44efa12e2e1b45","cipherparams":{"iv":"5638527baaa4d421ff71036b47fa0726"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"df803572cc41889883cd71903328faa8a3cbcf24bd0ecef5f1d33f0724862e96"},"mac":"0acceabda93da47547d34470a12f475e7a1a71780d802fd635e0a7e201e07dd3"},"id":"e328d9b3-12dc-421d-88b0-2f93691787c7","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/e11c86a37c4403c9a0e510815fe1200fea17d1a3: -------------------------------------------------------------------------------- 1 | {"address":"e11c86a37c4403c9a0e510815fe1200fea17d1a3","crypto":{"cipher":"aes-128-ctr","ciphertext":"1a77ce72859eafe21f5e0307c085501287640e8e603e49a4ba57e56f670b1db7","cipherparams":{"iv":"d325bf6f99fb41586a5bde2f8dcd53de"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e98c04252efec43142249d6595c1d51ea80dd0e206446a8c889b80f7fb11d7f9"},"mac":"054ab8ec3cfa8aa7383705018f65744b456825bf6c7903991fcaff5557ab18e5"},"id":"35aff133-4f32-4afb-ab18-bea8b9ea8e49","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/e30c37dc10b70f642bfa52c9d3a05484a817339d: -------------------------------------------------------------------------------- 1 | {"address":"e30c37dc10b70f642bfa52c9d3a05484a817339d","crypto":{"cipher":"aes-128-ctr","ciphertext":"fa7550fd004e1ade3c05e3be03290796c2ca2b9647f98e1c1a077eccb450b48d","cipherparams":{"iv":"2eca8b3374b1a98d7dcbcdb7e3f519c7"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"72657185116175bfb2d8d06bf8e22a0da4aaa446b1d492cd78f2f8dd8135532f"},"mac":"c30adb73f4a6b22ec14aaa4ca3ae3c51b862abf6fed82ec58aca30190c1b62a5"},"id":"d4aedf2c-b149-4373-a3a3-9e0e550a6d0b","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/f85f61d4466c4808748e288ba1999fb5e834c38e: -------------------------------------------------------------------------------- 1 | {"address":"f85f61d4466c4808748e288ba1999fb5e834c38e","crypto":{"cipher":"aes-128-ctr","ciphertext":"9eb1da1a9330f611b33073e0b8a934645f530e51b2af3c6e9d1cb89371299eeb","cipherparams":{"iv":"60b59d04052aa283fb54d9e4b238afbc"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"0af37976aa74120e5e27b02f354465ad137bb95fba419694cf1173a80bab9369"},"mac":"ec53224b869d9988a55d0d78e0f6270f7908e534b6d9bff6654505716b572cbf"},"id":"aebb0450-3eba-4661-bc6e-341f410523f7","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/keystore/f92adccbff93802b78e24a7a04186c9181c13d76: -------------------------------------------------------------------------------- 1 | {"address":"f92adccbff93802b78e24a7a04186c9181c13d76","crypto":{"cipher":"aes-128-ctr","ciphertext":"50c094534f2d84d3316e93770fe36b895e5d293d91264a6808eee690e8e507dc","cipherparams":{"iv":"169130971ad0b15a26230f864aa83351"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"be347718600a2e12a9c2e707f81159b81fde30d29832330ab0c014eafcd99dac"},"mac":"bac81c3d43e15864666ec3f7dfb1046afa90e7581d8e49b327f5300cccd04539"},"id":"cb4c896b-d8e2-468e-bc5d-9ae212dd673d","version":3} -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/passwords: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/priv_validator.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0B2E512026D309C2115FBD7F8560E061A1AFF538", 3 | "last_height": 0, 4 | "last_round": 0, 5 | "last_step": 0, 6 | "priv_key": { 7 | "type": "ed25519", 8 | "data": "31A09B13FCAC999BA03DA644CBF5211D15332110B6F20039240E61FDDDE4E37FBDD0D78AEB73A5A13B056516CF90828AF8C0EE8C9ECBC58F98729BEE0A0A8868" 9 | }, 10 | "pub_key": { 11 | "type": "ed25519", 12 | "data": "BDD0D78AEB73A5A13B056516CF90828AF8C0EE8C9ECBC58F98729BEE0A0A8868" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/run-ethereum.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./run.sh ethereum $1 $2 4 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/run-ethermint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./run.sh ethermint $1 $2 4 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/run-tendermint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./vars.sh 4 | 5 | NETWORK=$1 6 | NAME=$2 7 | DATADIR="/tmp/${TMPROOT}/$NETWORK-$NAME" 8 | 9 | tendermint node --home $DATADIR/tendermint 10 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | shopt -s huponexit 3 | 4 | function join_by { local IFS="$1"; shift; echo "$*"; } 5 | 6 | DIR=$(dirname ${BASH_SOURCE[0]}) 7 | source $DIR/vars.sh 8 | 9 | NETWORK=$1 10 | NAME=$2 11 | PORT=$3 12 | KEYSTORE=./keystore 13 | DATADIR="/tmp/${TMPROOT}/$NETWORK-$NAME" 14 | 15 | if [ "$DATADIR" == "" ]; then 16 | echo "Provide Datadir" 17 | exit 1 18 | fi 19 | 20 | if [ "$NETWORK" == "" ]; then 21 | echo "Provide Network" 22 | exit 1 23 | fi 24 | 25 | if [ "$PORT" == "" ]; then 26 | PORT="8545" 27 | fi 28 | 29 | KEYS=$(join_by , $(ls $KEYSTORE)) 30 | PASS=./passwords 31 | FLAGS=(--datadir $DATADIR/$NETWORK \ 32 | --rpc --rpcapi eth,net,web3,personal,miner,admin,txpool \ 33 | --rpcaddr 127.0.0.1 --rpcport $PORT \ 34 | --ws --wsapi eth,net,web3,personal,miner,admin \ 35 | --wsaddr 127.0.0.1 --wsport 8546 \ 36 | --unlock $KEYS --password $PASS \ 37 | ) 38 | 39 | if [ "$NETWORK" == "ethereum" ]; then 40 | geth version 41 | geth --mine --fakepow --nodiscover --maxpeers 0 ${FLAGS[@]} & 42 | wait 43 | else 44 | echo tendermint node --home $DATADIR/tendermint 45 | tendermint node --home $DATADIR/tendermint & 46 | echo ethermint ${FLAGS[@]} 47 | ethermint ${FLAGS[@]} & 48 | wait 49 | fi 50 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/setup-ethereum.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./setup.sh ethereum $1 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/setup-ethermint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./setup.sh ethermint $1 4 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=$(dirname ${BASH_SOURCE[0]}) 4 | source $DIR/vars.sh 5 | 6 | NETWORK=$1 7 | NAME=$2 8 | TMPFILES="/tmp/$TMPROOT/$NETWORK-$NAME" 9 | NETWORK_DATADIR="$TMPFILES/$NETWORK" 10 | TENDERMINT_DATADIR="$TMPFILES/tendermint" 11 | 12 | TENDERMINT=$(which tendermint) 13 | ETHERMINT=$(which ethermint) 14 | EXEC=$(which geth) 15 | 16 | KEYS=$(ls $KEYSTORE) 17 | 18 | 19 | mkdir -p $TMPFILES $NETWORK_DATADIR 20 | cp -rv keystore/ $NETWORK_DATADIR/keystore 21 | 22 | if [ "$NETWORK" == "ethermint" ]; then 23 | mkdir $TENDERMINT_DATADIR 24 | cp -v ./priv_validator.json $TENDERMINT_DATADIR 25 | cp -v ./genesis.json $TENDERMINT_DATADIR 26 | cd $TENDERMINT_DATADIR 27 | $TENDERMINT init --home $TENDERMINT_DATADIR 28 | cd - 29 | EXEC=$ETHERMINT 30 | fi 31 | 32 | $EXEC --datadir $NETWORK_DATADIR init ./ethgen.json 33 | -------------------------------------------------------------------------------- /tests/integration/truffle/deploytools/vars.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export KEYSTORE=./keystore 4 | export TMPROOT="/testnet" 5 | 6 | 7 | -------------------------------------------------------------------------------- /tests/integration/truffle/diff.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const tmpdir = '/tmp/truffle-tests' 5 | const name = 'test1' 6 | 7 | let ethereum = fs.readFileSync(path.join(tmpdir, name + '.output.ethereum')) 8 | let ethermint = fs.readFileSync(path.join(tmpdir, name + '.output.ethermint')) 9 | 10 | ethereum = generateTestObject(ethereum.toString()) 11 | ethermint = generateTestObject(ethermint.toString()) 12 | 13 | diff(ethereum, ethermint) 14 | 15 | function generateTestObject (src) { 16 | let rawdata = '[' + src.toString().substring(0, src.length - 2) + ']' 17 | let data = JSON.parse(rawdata) 18 | 19 | return data 20 | } 21 | 22 | function diff (ethereum, ethermint) { 23 | if (ethereum.length !== ethermint.length) { 24 | console.error('Tests count didn\'t match') 25 | console.error('Ethereum tests: %d, Ethermint tests: %d', ethereum.length, ethermint.length) 26 | return 27 | } 28 | 29 | let errors = false 30 | ethereum.forEach((test, i) => { 31 | let keys = Object.keys(test) 32 | 33 | keys.forEach((key) => { 34 | if (ethereum[i][key] !== ethermint[i][key]) { 35 | errors = true 36 | console.error('For test case %s\n ethereum(%s) != ethermint(%s)', key, ethereum[i][key], ethermint[i][key]) 37 | } 38 | }) 39 | }) 40 | 41 | if (!errors) { 42 | console.log('Success: Output for ethereum and ethermint was the same') 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/integration/truffle/ether-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | FILE=$1 4 | FILEPATH=$2 5 | TMPFILES=/tmp/truffle-tests 6 | SCRIPTDIR=$(dirname ${BASH_SOURCE[0]}) 7 | 8 | pushd $SCRIPTDIR > /dev/null 9 | SCRIPTDIR=`pwd -P` 10 | popd > /dev/null 11 | 12 | if [ ! -f $FILE ]; then 13 | echo "File $FILE does not exist" 14 | exit 1 15 | fi 16 | 17 | REPOS=($(cat $FILE)) 18 | 19 | mkdir -pv $TMPFILES 20 | 21 | cd $TMPFILES 22 | echo -e '\c' > $FILEPATH 23 | for ((i=0; i < $((${#REPOS[@]})); i=$i+2)); do 24 | repo=${REPOS[$i]} 25 | folder=${REPOS[$((i+1))]} 26 | name=$(basename $repo) 27 | 28 | ( 29 | if [ ! -d $name ]; then 30 | git clone $repo || { 31 | echo "Failed to clone repo $repo" 32 | echo "Skipping.." 33 | exit 2 34 | } 35 | fi 36 | 37 | cd $name 38 | cd $folder 39 | 40 | echo "NPM Install" 41 | npm i 42 | 43 | echo "Create migrations folder" 44 | mkdir -p migrations 45 | 46 | echo "Run Truffle Tests" 47 | TESTS_FILE=$FILEPATH truffle test --mocha.reporter=$SCRIPTDIR/test-reporter.js 48 | echo ',' >> $FILEPATH 49 | ) 50 | done 51 | -------------------------------------------------------------------------------- /tests/integration/truffle/file-example: -------------------------------------------------------------------------------- 1 | https://github.com/ConsenSys/Tokens Token_Contracts 2 | https://github.com/trufflesuite/truffle-init-default . 3 | -------------------------------------------------------------------------------- /tests/integration/truffle/gasLimit/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let chai = require('chai'); 4 | let assert = chai.assert; 5 | let fs = require('fs'); 6 | let config = require('config'); 7 | let solc = require('solc'); 8 | let Web3 = require('web3'); 9 | let web3 = new Web3(new Web3.providers.HttpProvider("http://" + config.host + ":" + config.port)); 10 | 11 | const account = web3.eth.accounts[0]; 12 | 13 | console.log('Block number: ' + web3.eth.blockNumber) 14 | console.log('Account: ' + account) 15 | console.log('Account balance: ' + web3.eth.getBalance(account)) 16 | 17 | //unlock account 18 | web3.personal.unlockAccount(account, "1234"); 19 | let contractSource = fs.readFileSync(__dirname + '/test.sol', 'utf-8'); 20 | 21 | console.log("Solc version is " + solc.version()); 22 | 23 | describe('gasLimit', function () { 24 | it('should return gas too low error', function (done) { 25 | // mocha timeout 26 | this.timeout(120 * 1000); 27 | 28 | const contractCompiled = solc.compile(contractSource); 29 | 30 | const bytecode = contractCompiled.contracts[':Test'].bytecode; 31 | const abi = JSON.parse(contractCompiled.contracts[':Test'].interface); 32 | 33 | // const estimateGas = web3.eth.estimateGas({data: '0x' + bytecode,}); 34 | // console.log('Gas needed: ' + estimateGas); 35 | 36 | const testContract = web3.eth.contract(abi); 37 | testContract.new({ 38 | from: account, 39 | data: '0x' + bytecode, 40 | gas: '100' //set low gas 41 | }, function (error) { 42 | console.log(error); 43 | assert.equal(error, "Error: intrinsic gas too low"); 44 | done(); 45 | }); 46 | }); 47 | 48 | it('should deploy contract', function (done) { 49 | this.timeout(60 * 1000); 50 | 51 | const contractCompiled = solc.compile(contractSource); 52 | 53 | const bytecode = contractCompiled.contracts[':Test'].bytecode; 54 | const abi = JSON.parse(contractCompiled.contracts[':Test'].interface); 55 | 56 | const estimateGas = web3.eth.estimateGas({data: '0x' + bytecode,}); 57 | console.log('Gas needed: ' + estimateGas); 58 | 59 | let callbackCount = 0; 60 | 61 | const testContract = web3.eth.contract(abi); 62 | testContract.new({ 63 | from: account, 64 | data: '0x' + bytecode, 65 | gas: '5379400' //set enough gas 66 | }, function (error, contract) { 67 | callbackCount++; 68 | 69 | assert.equal(error, null); 70 | 71 | if (error) { 72 | done(); 73 | } 74 | 75 | assert.isNotNull(contract.transactionHash); 76 | 77 | if (callbackCount === 2) { 78 | assert.isNotNull(contract.address); 79 | done(); 80 | } 81 | }); 82 | }); 83 | }); 84 | 85 | describe('huge smart contract', function () { 86 | it('should deploy contract', function (done) { 87 | //set mocha timeout 88 | this.timeout(120 * 1000); 89 | 90 | //generate smart contract with many functions 91 | let code = codeGenerator(150); 92 | 93 | const contractCompiled = solc.compile(code); 94 | 95 | const bytecode = contractCompiled.contracts[':Test'].bytecode; 96 | const abi = JSON.parse(contractCompiled.contracts[':Test'].interface); 97 | 98 | const estimateGas = web3.eth.estimateGas({data: '0x' + bytecode,}); 99 | // console.log('Gas needed: ' + estimateGas); 100 | 101 | let callbackCount = 0; 102 | 103 | const testContract = web3.eth.contract(abi); 104 | testContract.new({ 105 | from: account, 106 | data: '0x' + bytecode, 107 | gas: estimateGas//set enough gas 108 | }, function (error, contract) { 109 | callbackCount++; 110 | 111 | assert.equal(error, null); 112 | 113 | if (error) { 114 | done(); 115 | } 116 | 117 | assert.isNotNull(contract.transactionHash); 118 | 119 | if (callbackCount === 2) { 120 | assert.isNotNull(contract.address); 121 | done(); 122 | } 123 | }); 124 | }); 125 | }); 126 | 127 | function codeGenerator(functionsCount) { 128 | let code = ` 129 | pragma solidity ^0.4.0; 130 | contract Test 131 | { 132 | event TestEvent(uint a); 133 | function Test() {} 134 | `; 135 | for (let i = 0; i < functionsCount; i++) { 136 | code += "\nfunction f" + i + `(uint i) 137 | { 138 | TestEvent(i); 139 | }`; 140 | } 141 | code += "\n}"; 142 | 143 | return code; 144 | } 145 | -------------------------------------------------------------------------------- /tests/integration/truffle/gasLimit/test.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | contract Test 3 | { 4 | event TestEvent(uint a); 5 | 6 | function Test() { 7 | } 8 | 9 | function testF1(uint i) 10 | { 11 | TestEvent(i); 12 | } 13 | 14 | function testF2(uint i) 15 | { 16 | TestEvent(i); 17 | } 18 | 19 | function testF3(uint i) 20 | { 21 | TestEvent(i); 22 | } 23 | 24 | function testF4(uint i) 25 | { 26 | TestEvent(i); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/integration/truffle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "truffle-runner", 3 | "version": "1.0.0", 4 | "description": "Truffle Tests Runner ===", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha gasLimit" 8 | }, 9 | "author": "", 10 | "license": "GPL-3.0", 11 | "dependencies": { 12 | "chai": "^4.0.2", 13 | "config": "^1.26.1", 14 | "mocha": "^3.4.2", 15 | "truffle": "^3.2.4", 16 | "web3": "^0.20.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/integration/truffle/test-reporter.js: -------------------------------------------------------------------------------- 1 | const mocha = require('mocha') 2 | const fs = require('fs') 3 | 4 | module.exports = JsonFileReporter 5 | 6 | function JsonFileReporter (runner) { 7 | mocha.reporters.Base.call(this, runner) 8 | 9 | const fileToAppend = process.env.TESTS_FILE 10 | let obj = {} 11 | 12 | runner.on('pass', (test) => { 13 | obj[formatName(test)] = '' 14 | process.stdout.write('.') 15 | }) 16 | 17 | runner.on('fail', (test, err) => { 18 | obj[formatName(test)] = err.message 19 | process.stdout.write('-') 20 | }) 21 | 22 | runner.on('end', () => { 23 | fs.appendFileSync(fileToAppend, JSON.stringify(obj)) 24 | }) 25 | } 26 | 27 | function formatName (test) { 28 | return (test.file ? test.file + ':' : '') + test.fullTitle() 29 | } 30 | -------------------------------------------------------------------------------- /tests/p2p/ip.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -eu 3 | 4 | ID=$1 5 | echo "172.58.0.$((100+$ID))" 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/p2p/seeds.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -eu 3 | 4 | N=$1 5 | 6 | SOURCE="${BASH_SOURCE[0]}" 7 | while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done 8 | P2PDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 9 | 10 | 11 | seeds="$($P2PDIR/ip.sh 2):46656" 12 | 13 | for i in $(seq 2 $N); do 14 | index=$(($i*2)) 15 | TENDERMINT_IP=$($P2PDIR/ip.sh $index) 16 | 17 | seeds="$seeds,$TENDERMINT_IP:46656" 18 | done 19 | 20 | echo "$seeds" 21 | -------------------------------------------------------------------------------- /tests/p2p/stop_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | 5 | N=$1 6 | 7 | for i in $(seq 1 "$N"); do 8 | docker stop "tendermint_$i" 9 | docker stop "ethermint_$i" 10 | 11 | docker rm -fv "tendermint_$i" 12 | docker rm -fv "ethermint_$i" 13 | done 14 | 15 | #delete network 16 | docker network rm ethermint_net 17 | 18 | -------------------------------------------------------------------------------- /tests/scripts/test_coverage.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | PKGS=$(go list ./... | grep -v /vendor/) 4 | 5 | set -e 6 | echo "mode: atomic" > coverage.txt 7 | for pkg in ${PKGS[@]}; do 8 | go test -v -timeout 30m -coverprofile=profile.out -covermode=atomic "$pkg" 9 | if [ -f profile.out ]; then 10 | tail -n +2 profile.out >> coverage.txt; 11 | rm profile.out 12 | fi 13 | done 14 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_1/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genesis_time": "0001-01-01T00:00:00Z", 3 | "chain_id": "test-chain-nzOPWR", 4 | "validators": [ 5 | { 6 | "pub_key": { 7 | "type": "ed25519", 8 | "data": "A0F03C76EF7E22B312097BD01D5BADB62ACA19217AA1E899251B9F4D2349671C" 9 | }, 10 | "amount": 10, 11 | "name": "tendermint_1" 12 | }, 13 | { 14 | "pub_key": { 15 | "type": "ed25519", 16 | "data": "754093E7DC0749D1850D4841127FA1AC6892025366F0F0036B18FB5808BB4B4C" 17 | }, 18 | "amount": 10, 19 | "name": "tendermint_2" 20 | }, 21 | { 22 | "pub_key": { 23 | "type": "ed25519", 24 | "data": "A09C63993DFFF1EBE464D29D37EC0E12D9493A9D596E88B702058C016E54D535" 25 | }, 26 | "amount": 10, 27 | "name": "tendermint_3" 28 | }, 29 | { 30 | "pub_key": { 31 | "type": "ed25519", 32 | "data": "D921894213932A32B47BEF69896B073D52EE7AF0358C3B82BE692E757100F842" 33 | }, 34 | "amount": 10, 35 | "name": "tendermint_4" 36 | } 37 | ], 38 | "app_hash": "" 39 | } 40 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_1/priv_validator.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "E25C0410A1B12202DDDC1EE0AF7873FD48F5DD37", 3 | "pub_key": { 4 | "type": "ed25519", 5 | "data": "A0F03C76EF7E22B312097BD01D5BADB62ACA19217AA1E899251B9F4D2349671C" 6 | }, 7 | "last_height": 0, 8 | "last_round": 0, 9 | "last_step": 0, 10 | "last_signature": null, 11 | "priv_key": { 12 | "type": "ed25519", 13 | "data": "F16E7568552EF3AF1FD5DE23C280E0BFFDF6FC550E23B736074D0AD21BB92CCCA0F03C76EF7E22B312097BD01D5BADB62ACA19217AA1E899251B9F4D2349671C" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_2/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genesis_time": "0001-01-01T00:00:00Z", 3 | "chain_id": "test-chain-nzOPWR", 4 | "validators": [ 5 | { 6 | "pub_key": { 7 | "type": "ed25519", 8 | "data": "A0F03C76EF7E22B312097BD01D5BADB62ACA19217AA1E899251B9F4D2349671C" 9 | }, 10 | "amount": 10, 11 | "name": "tendermint_1" 12 | }, 13 | { 14 | "pub_key": { 15 | "type": "ed25519", 16 | "data": "754093E7DC0749D1850D4841127FA1AC6892025366F0F0036B18FB5808BB4B4C" 17 | }, 18 | "amount": 10, 19 | "name": "tendermint_2" 20 | }, 21 | { 22 | "pub_key": { 23 | "type": "ed25519", 24 | "data": "A09C63993DFFF1EBE464D29D37EC0E12D9493A9D596E88B702058C016E54D535" 25 | }, 26 | "amount": 10, 27 | "name": "tendermint_3" 28 | }, 29 | { 30 | "pub_key": { 31 | "type": "ed25519", 32 | "data": "D921894213932A32B47BEF69896B073D52EE7AF0358C3B82BE692E757100F842" 33 | }, 34 | "amount": 10, 35 | "name": "tendermint_4" 36 | } 37 | ], 38 | "app_hash": "" 39 | } 40 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_2/priv_validator.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "92D0557EEDE48E2A964B244AFFDEE989FF5A57BF", 3 | "pub_key": { 4 | "type": "ed25519", 5 | "data": "754093E7DC0749D1850D4841127FA1AC6892025366F0F0036B18FB5808BB4B4C" 6 | }, 7 | "last_height": 0, 8 | "last_round": 0, 9 | "last_step": 0, 10 | "last_signature": null, 11 | "priv_key": { 12 | "type": "ed25519", 13 | "data": "E4C24521FBBE45AC2FF1D7E039876B2D03303542EE4EF5359A2CC0D96A5501BF754093E7DC0749D1850D4841127FA1AC6892025366F0F0036B18FB5808BB4B4C" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_3/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genesis_time": "0001-01-01T00:00:00Z", 3 | "chain_id": "test-chain-nzOPWR", 4 | "validators": [ 5 | { 6 | "pub_key": { 7 | "type": "ed25519", 8 | "data": "A0F03C76EF7E22B312097BD01D5BADB62ACA19217AA1E899251B9F4D2349671C" 9 | }, 10 | "amount": 10, 11 | "name": "tendermint_1" 12 | }, 13 | { 14 | "pub_key": { 15 | "type": "ed25519", 16 | "data": "754093E7DC0749D1850D4841127FA1AC6892025366F0F0036B18FB5808BB4B4C" 17 | }, 18 | "amount": 10, 19 | "name": "tendermint_2" 20 | }, 21 | { 22 | "pub_key": { 23 | "type": "ed25519", 24 | "data": "A09C63993DFFF1EBE464D29D37EC0E12D9493A9D596E88B702058C016E54D535" 25 | }, 26 | "amount": 10, 27 | "name": "tendermint_3" 28 | }, 29 | { 30 | "pub_key": { 31 | "type": "ed25519", 32 | "data": "D921894213932A32B47BEF69896B073D52EE7AF0358C3B82BE692E757100F842" 33 | }, 34 | "amount": 10, 35 | "name": "tendermint_4" 36 | } 37 | ], 38 | "app_hash": "" 39 | } 40 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_3/priv_validator.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "5FA24ABEC834F6348858FF57AE374F5AB8D0767F", 3 | "pub_key": { 4 | "type": "ed25519", 5 | "data": "A09C63993DFFF1EBE464D29D37EC0E12D9493A9D596E88B702058C016E54D535" 6 | }, 7 | "last_height": 0, 8 | "last_round": 0, 9 | "last_step": 0, 10 | "last_signature": null, 11 | "priv_key": { 12 | "type": "ed25519", 13 | "data": "740D7FB1DDD7FD1134A1BD23CC3D052627C0B9AEB1257E92864775F0126D181FA09C63993DFFF1EBE464D29D37EC0E12D9493A9D596E88B702058C016E54D535" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_4/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genesis_time": "0001-01-01T00:00:00Z", 3 | "chain_id": "test-chain-nzOPWR", 4 | "validators": [ 5 | { 6 | "pub_key": { 7 | "type": "ed25519", 8 | "data": "A0F03C76EF7E22B312097BD01D5BADB62ACA19217AA1E899251B9F4D2349671C" 9 | }, 10 | "amount": 10, 11 | "name": "tendermint_1" 12 | }, 13 | { 14 | "pub_key": { 15 | "type": "ed25519", 16 | "data": "754093E7DC0749D1850D4841127FA1AC6892025366F0F0036B18FB5808BB4B4C" 17 | }, 18 | "amount": 10, 19 | "name": "tendermint_2" 20 | }, 21 | { 22 | "pub_key": { 23 | "type": "ed25519", 24 | "data": "A09C63993DFFF1EBE464D29D37EC0E12D9493A9D596E88B702058C016E54D535" 25 | }, 26 | "amount": 10, 27 | "name": "tendermint_3" 28 | }, 29 | { 30 | "pub_key": { 31 | "type": "ed25519", 32 | "data": "D921894213932A32B47BEF69896B073D52EE7AF0358C3B82BE692E757100F842" 33 | }, 34 | "amount": 10, 35 | "name": "tendermint_4" 36 | } 37 | ], 38 | "app_hash": "" 39 | } 40 | -------------------------------------------------------------------------------- /tests/tendermint_data/tendermint_4/priv_validator.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "41B7949FA3CA372CD89A2A2A1F77BC81C7CD9F74", 3 | "pub_key": { 4 | "type": "ed25519", 5 | "data": "D921894213932A32B47BEF69896B073D52EE7AF0358C3B82BE692E757100F842" 6 | }, 7 | "last_height": 0, 8 | "last_round": 0, 9 | "last_step": 0, 10 | "last_signature": null, 11 | "priv_key": { 12 | "type": "ed25519", 13 | "data": "B07C13FE43E8B5317B41088F22BD41A73FB36DED6470E96434BDC89EA246E968D921894213932A32B47BEF69896B073D52EE7AF0358C3B82BE692E757100F842" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # -x for debug 4 | set -eux 5 | 6 | # count of tendermint/ethermint node 7 | N=4 8 | 9 | # Docker version and info 10 | docker version 11 | docker info 12 | 13 | # Get the directory of where this script is. 14 | SOURCE="${BASH_SOURCE[0]}" 15 | while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done 16 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 17 | 18 | # Directory where we keep tendermint genesis and private key 19 | DATA_DIR="$DIR/tendermint_data" 20 | 21 | # Build docker image for ethermint from current source code 22 | echo 23 | echo "* [$(date +"%T")] building ethermint docker image" 24 | bash "$DIR/docker/build.sh" 25 | 26 | # Build docker image for web3 js tests 27 | echo 28 | echo "* [$(date +"%T")] building nodejs docker image" 29 | bash "$DIR/integration/truffle/build.sh" 30 | 31 | # stop existing container and remove network 32 | set +e 33 | bash "$DIR/p2p/stop_tests.sh" $N 34 | set -e 35 | 36 | echo 37 | echo "* [$(date +"%T")] create docker network" 38 | docker network create --driver bridge --subnet 172.58.0.0/16 ethermint_net 39 | 40 | # Generate seeds parameter to connect all tendermint nodes in one cluster 41 | SEEDS=$(bash $DIR/p2p/seeds.sh $N) 42 | if [[ "$SEEDS" != "" ]]; then 43 | SEEDS="--p2p.seeds $SEEDS" 44 | fi 45 | 46 | # Start N nodes of tendermint and N node of ethermint. 47 | for i in $(seq 1 "$N"); do 48 | echo 49 | echo "* [$(date +"%T")] run tendermint $i container" 50 | 51 | # We have N as input parameter and we need to generate N*2 IP addresses 52 | # for tendermint and ethermint. 53 | # So, here we calculate offset for IP 54 | index=$(($i*2)) 55 | nextIndex=$(($i*2+1)) 56 | 57 | TENDERMINT_IP=$($DIR/p2p/ip.sh $index) 58 | ETHERMINT_IP=$($DIR/p2p/ip.sh $nextIndex) 59 | 60 | # Start tendermint container. Pass ethermint IP and seeds 61 | docker run -d \ 62 | --net=ethermint_net \ 63 | --ip "$TENDERMINT_IP" \ 64 | --name tendermint_$i \ 65 | -v "$DATA_DIR/tendermint_$i:/tendermint" \ 66 | tendermint/tendermint node --proxy_app tcp://$ETHERMINT_IP:46658 $SEEDS 67 | 68 | # Start ethermint container. Pass tendermint IP 69 | echo 70 | echo "* [$(date +"%T")] run ethermint $i container" 71 | docker run -d \ 72 | --net=ethermint_net \ 73 | --ip $ETHERMINT_IP \ 74 | --name ethermint_$i \ 75 | ethermint_tester ethermint --datadir=/ethermint/data --rpc --rpcaddr=0.0.0.0 --ws --wsaddr=0.0.0.0 --rpcapi eth,net,web3,personal,admin --tendermint_addr tcp://$TENDERMINT_IP:46657 76 | 77 | done 78 | 79 | 80 | # Wait for tendermint & ethermint start 81 | sleep 60 82 | 83 | # Run container with web3 js tests 84 | # Pass IP address of last ethermint node 85 | echo 86 | echo "* [$(date +"%T")] run tests" 87 | docker run --net=ethermint_net \ 88 | --rm -it \ 89 | -e NODE_ENV=test \ 90 | -e WEB3_HOST=$ETHERMINT_IP \ 91 | -e WEB3_PORT=8545 \ 92 | ethermint_js_test npm test 93 | 94 | # Stop and remove containers. Remove network 95 | echo 96 | echo "* [$(date +"%T")] stop containers" 97 | bash "$DIR/p2p/stop_tests.sh" $N 98 | 99 | echo 100 | echo "* [$(date +"%T")] done" 101 | -------------------------------------------------------------------------------- /types/mockClient.go: -------------------------------------------------------------------------------- 1 | // TODO: Replace this with the mockClient in 2 | // github.com/tendermint/tendermint/rpc/client/mock/abci.go 3 | 4 | package types 5 | 6 | import ( 7 | data "github.com/tendermint/go-wire/data" 8 | ctypes "github.com/tendermint/tendermint/rpc/core/types" 9 | ttypes "github.com/tendermint/tendermint/types" 10 | "github.com/tendermint/tmlibs/events" 11 | "github.com/tendermint/tmlibs/log" 12 | ) 13 | 14 | // MockClient is a mock implementation of a tendermint rpc client 15 | type MockClient struct { 16 | SentBroadcastTx chan struct{} // fires when we call broadcast_tx_sync 17 | syncing bool 18 | } 19 | 20 | // NewMockClient returns a pointer to a new non-syncing mock tendermint rpc client 21 | func NewMockClient(syncing bool) *MockClient { 22 | return &MockClient{ 23 | make(chan struct{}), 24 | syncing, 25 | } 26 | } 27 | 28 | // --------------------- 29 | // ABCIClient implementation 30 | 31 | // ABCIInfo ... 32 | func (mc *MockClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { 33 | return &ctypes.ResultABCIInfo{}, nil 34 | } 35 | 36 | // ABCIQuery ... 37 | func (mc *MockClient) ABCIQuery(path string, data data.Bytes, 38 | prove bool) (*ctypes.ResultABCIQuery, error) { 39 | 40 | return &ctypes.ResultABCIQuery{}, nil 41 | } 42 | 43 | // BroadcastTxCommit ... 44 | func (mc *MockClient) BroadcastTxCommit(tx ttypes.Tx) (*ctypes.ResultBroadcastTxCommit, error) { 45 | return &ctypes.ResultBroadcastTxCommit{}, nil 46 | } 47 | 48 | // BroadcastTxAsync ... 49 | func (mc *MockClient) BroadcastTxAsync(tx ttypes.Tx) (*ctypes.ResultBroadcastTx, error) { 50 | close(mc.SentBroadcastTx) 51 | 52 | return &ctypes.ResultBroadcastTx{}, nil 53 | } 54 | 55 | // BroadcastTxSync ... 56 | func (mc *MockClient) BroadcastTxSync(tx ttypes.Tx) (*ctypes.ResultBroadcastTx, error) { 57 | mc.SentBroadcastTx <- struct{}{} 58 | 59 | return &ctypes.ResultBroadcastTx{}, nil 60 | } 61 | 62 | // ---------------------- 63 | // SignClient implementation 64 | 65 | // Block ... 66 | func (mc *MockClient) Block(height int) (*ctypes.ResultBlock, error) { 67 | return &ctypes.ResultBlock{}, nil 68 | } 69 | 70 | // Commit ... 71 | func (mc *MockClient) Commit(height int) (*ctypes.ResultCommit, error) { 72 | return &ctypes.ResultCommit{}, nil 73 | } 74 | 75 | // Validators ... 76 | func (mc *MockClient) Validators() (*ctypes.ResultValidators, error) { 77 | return &ctypes.ResultValidators{}, nil 78 | } 79 | 80 | // Tx ... 81 | func (mc *MockClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { 82 | return &ctypes.ResultTx{}, nil 83 | } 84 | 85 | // ------------------- 86 | // HistoryClient implementation 87 | 88 | // Genesis ... 89 | func (mc *MockClient) Genesis() (*ctypes.ResultGenesis, error) { 90 | return &ctypes.ResultGenesis{}, nil 91 | } 92 | 93 | // BlockchainInfo ... 94 | func (mc *MockClient) BlockchainInfo(minHeight, 95 | maxHeight int) (*ctypes.ResultBlockchainInfo, error) { 96 | 97 | return &ctypes.ResultBlockchainInfo{}, nil 98 | } 99 | 100 | // ----------------------- 101 | // StatusClient implementation 102 | 103 | // Status ... 104 | func (mc *MockClient) Status() (*ctypes.ResultStatus, error) { 105 | return &ctypes.ResultStatus{Syncing: mc.syncing}, nil 106 | } 107 | 108 | // ----------------------- 109 | // Service implementation 110 | 111 | // Start ... 112 | func (mc *MockClient) Start() (bool, error) { 113 | return true, nil 114 | } 115 | 116 | // OnStart ... 117 | func (mc *MockClient) OnStart() error { 118 | return nil 119 | } 120 | 121 | // Stop ... 122 | func (mc *MockClient) Stop() bool { 123 | return true 124 | } 125 | 126 | // OnStop ... 127 | func (mc *MockClient) OnStop() { 128 | close(mc.SentBroadcastTx) 129 | } 130 | 131 | // Reset ... 132 | func (mc *MockClient) Reset() (bool, error) { 133 | return true, nil 134 | } 135 | 136 | // OnReset ... 137 | func (mc *MockClient) OnReset() error { 138 | return nil 139 | } 140 | 141 | // IsRunning ... 142 | func (mc *MockClient) IsRunning() bool { 143 | return true 144 | } 145 | 146 | // String ... 147 | func (mc *MockClient) String() string { 148 | return "MockClient" 149 | } 150 | 151 | // SetLogger ... 152 | func (mc *MockClient) SetLogger(log.Logger) { 153 | 154 | } 155 | 156 | // ----------------------- 157 | // types.EventSwitch implementation 158 | 159 | // AddListenerForEvent ... 160 | func (mc *MockClient) AddListenerForEvent(listenerID, event string, cb events.EventCallback) { 161 | // nop 162 | } 163 | 164 | // FireEvent ... 165 | func (mc *MockClient) FireEvent(event string, data events.EventData) { 166 | // nop 167 | } 168 | 169 | // RemoveListenerForEvent ... 170 | func (mc *MockClient) RemoveListenerForEvent(event string, listenerID string) { 171 | // nop 172 | } 173 | 174 | // RemoveListener ... 175 | func (mc *MockClient) RemoveListener(listenerID string) { 176 | // nop 177 | } 178 | -------------------------------------------------------------------------------- /types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | ethTypes "github.com/ethereum/go-ethereum/core/types" 6 | 7 | "github.com/tendermint/abci/types" 8 | ) 9 | 10 | // MinerRewardStrategy is a mining strategy 11 | type MinerRewardStrategy interface { 12 | Receiver() common.Address 13 | } 14 | 15 | // ValidatorsStrategy is a validator strategy 16 | type ValidatorsStrategy interface { 17 | SetValidators(validators []*types.Validator) 18 | CollectTx(tx *ethTypes.Transaction) 19 | GetUpdatedValidators() []*types.Validator 20 | } 21 | 22 | // Strategy encompasses all available strategies 23 | type Strategy struct { 24 | MinerRewardStrategy 25 | ValidatorsStrategy 26 | } 27 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // Major version component of the current release 4 | const Major = 0 5 | 6 | // Minor version component of the current release 7 | const Minor = 5 8 | 9 | // Fix version component of the current release 10 | const Fix = 3 11 | 12 | var ( 13 | // Version is the full version string 14 | Version = "0.5.3" 15 | 16 | // GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse --short HEAD)" 17 | GitCommit string 18 | ) 19 | 20 | func init() { 21 | if GitCommit != "" { 22 | Version += "-" + GitCommit 23 | } 24 | } 25 | --------------------------------------------------------------------------------