├── .circleci └── config.yml ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── Makefile ├── README.md ├── app ├── ante.go ├── ante_test.go ├── ethermint.go ├── genesis.go └── test_utils.go ├── cmd ├── emintcli │ └── main.go └── emintd │ └── main.go ├── core ├── chain.go └── chain_test.go ├── crypto ├── codec.go ├── secp256k1.go └── secp256k1_test.go ├── docs ├── intro │ └── README.md └── spec │ ├── evm │ └── README.md │ └── transactions │ └── README.md ├── gometalinter.json ├── importer ├── blockchain └── importer_test.go ├── server └── rpc │ ├── apis.go │ ├── apis_test.go │ ├── config.go │ ├── eth_api.go │ ├── rpc.go │ └── rpc_test.go ├── types ├── account.go ├── codec.go ├── context.go └── errors.go ├── version └── version.go └── x └── evm ├── handler.go └── types ├── codec.go ├── dump.go ├── journal.go ├── msg.go ├── msg_test.go ├── state_object.go ├── statedb.go └── utils.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/golang:1.10 6 | 7 | working_directory: /go/src/github.com/cosmos/ethermint 8 | 9 | steps: 10 | - checkout 11 | 12 | - run: 13 | name: "Install tools and dependancies" 14 | command: make tools deps 15 | 16 | - run: 17 | name: "Run tests" 18 | command: make test-lint test-unit test-import 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS: https://help.github.com/articles/about-codeowners/ 2 | 3 | # Primary repo maintainers 4 | * @alexanderbez @AlexeyAkhunov @jackzampolin 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: create a bug report 4 | --- 5 | 6 | __System info:__ [Include Ethermint commit, operating system name, and other relevant details] 7 | 8 | __Steps to reproduce:__ 9 | 10 | 1. [First Step] 11 | 2. [Second Step] 12 | 3. [and so on...] 13 | 14 | __Expected behavior:__ [What you expected to happen] 15 | 16 | __Actual behavior:__ [What actually happened] 17 | 18 | __Additional info:__ [Include gist of relevant config, logs, etc.] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Opening a feature request kicks off a discussion 4 | 5 | --- 6 | 7 | __Proposal:__ [Description of the feature] 8 | 9 | __Current behavior:__ [What currently happens] 10 | 11 | __Desired behavior:__ [What you would like to happen] 12 | 13 | __Use case:__ [Why is this important (helps with prioritizing requests)] 14 | 15 | Requests may be closed if we're not actively planning to work on them. 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | - Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ethermint/blob/master/CONTRIBUTING.md#pr-targeting)) 8 | 9 | - [ ] Linked to github-issue with discussion and accepted design OR link to spec that describes this work. 10 | - [ ] Wrote tests 11 | - [ ] Updated relevant documentation (`docs/`) 12 | - [ ] Added entries in `PENDING.md` with issue # 13 | - [ ] rereviewed `Files changed` in the github PR explorer 14 | 15 | ______ 16 | 17 | For Admin Use: 18 | - Added appropriate labels to PR (ex. wip, ready-for-review, docs) 19 | - Reviewers Assigned 20 | - Squashed all commits, uses message "Merge pull request #XYZ: [title]" 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Vendor deps 14 | .glide/ 15 | vendor/ 16 | build/ 17 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jack@tendermint.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering making contributions to Ethermint! Start by taking a look at this [coding repo](https://github.com/tendermint/coding) for overall information on repository workflow and standards. 4 | 5 | Please follow standard github best practices: fork the repo, branch from the tip of develop, make some commits, and submit a pull request to develop. See the [open issues](https://github.com/cosmos/ethermint/issues) for things we need help with! 6 | 7 | Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. Additionally please ensure that your code is lint compliant by running `make lint` 8 | 9 | Looking for a good place to start contributing? How about checking out some [good first issues](https://github.com/cosmos/ethermint/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) 10 | 11 | ## Forking 12 | 13 | Please note that Go requires code to live under absolute paths, which complicates forking. 14 | While your fork should live at `https://github.com//ethermint`, 15 | the code should not exist at `$GOPATH/src/github.com//ethermint`. 16 | Instead, you should use `git remote` to add the fork as a new remote for the original repo, 17 | `$GOPATH/src/github.com/cosmos/ethermint `, and do all the work there. 18 | 19 | For instance, to create a fork and work on a branch of it, One would: 20 | 21 | * Create the fork on github, using the fork button. 22 | * Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/ethermint`) 23 | * `git remote rename origin upstream` 24 | * `git remote add origin git@github.com:/ethermint.git` 25 | 26 | Now `origin` refers to my fork and `upstream` refers to the Ethermint version. 27 | So I can `git push -u origin ` to update my fork, and make pull requests to Ethermint from there. 28 | Of course, replace `` with your git handle. 29 | 30 | To pull in updates from the origin repo, run 31 | 32 | * `git fetch upstream` 33 | * `git rebase upstream/develop` (or whatever branch you want) 34 | 35 | Do not make Pull Requests to `master`, they will not be considered. 36 | 37 | ## Dependencies 38 | 39 | We use [`dep`](https://github.com/golang/dep) to manage dependencies. 40 | 41 | That said, the master branch of every Cosmos repository should just build 42 | with `go get`, which means they should be kept up-to-date with their 43 | dependencies so we can get away with telling people they can just `go get` our 44 | software. 45 | 46 | Since some dependencies are not under our control, a third party may break our 47 | build, in which case we can fall back on `dep ensure` (or `make 48 | deps`). Even for dependencies under our control, `dep` helps us to 49 | keep multiple repos in sync as they evolve. Anything with an executable, such 50 | as apps, tools, and the core, should use `dep`. 51 | 52 | Run `dep status` to get a list of vendor dependencies that may not be 53 | up-to-date. 54 | 55 | ## Testing 56 | 57 | All repos should be hooked up to [CircleCI](https://circleci.com/). 58 | 59 | If they have `.go` files in the root directory, they will be automatically 60 | tested by circle using `go test -v -race ./...`. If not, they will need a 61 | `circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and 62 | includes its continuous integration status using a badge in the `README.md`. 63 | 64 | ## Branching Model and Release 65 | 66 | User-facing repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/. 67 | That is, these repos should be well versioned, and any merge to master requires a version bump and tagged release. 68 | 69 | Libraries need not follow the model strictly, but would be wise to. 70 | 71 | Ethermint utilizes [semantic versioning](https://semver.org/). 72 | 73 | ### Development Procedure: 74 | - the latest state of development is on `develop` 75 | - `develop` must never fail `make test` 76 | - `develop` should not fail `make test-lint` 77 | - no --force onto `develop` (except when reverting a broken commit, which should seldom happen) 78 | - create a development branch either on github.com/cosmos/ethermint, or your fork (using `git remote add origin`) 79 | - [squash your commits](https://github.com/todotxt/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit) into an individual commit 80 | - before submitting a pull request, begin `git rebase` on top of `develop` 81 | 82 | ### Pull Merge Procedure: 83 | - ensure pull branch is rebased on develop 84 | - [squash your commits](https://github.com/todotxt/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit) into an individual commit 85 | - run `make test` and `make test-cli` to ensure that all tests pass 86 | - merge pull request 87 | 88 | ### Release Procedure: 89 | - start on `develop` 90 | - prepare changelog/release issue 91 | - bump versions 92 | - push to release-vX.X.X to run CI 93 | - merge to master 94 | - merge master back to develop 95 | 96 | ### Hotfix Procedure: 97 | - start on `master` 98 | - checkout a new branch named hotfix-vX.X.X 99 | - make the required changes 100 | - these changes should be small and an absolute necessity 101 | - add a note to CHANGELOG.md 102 | - bump versions 103 | - push to hotfix-vX.X.X to run the extended integration tests on the CI 104 | - merge hotfix-vX.X.X to master 105 | - merge hotfix-vX.X.X to develop 106 | - delete the hotfix-vX.X.X branch 107 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS build-env 2 | 3 | # Set up dependencies 4 | ENV PACKAGES make git curl build-base 5 | 6 | # Set working directory for the build 7 | WORKDIR /go/src/github.com/cosmos/ethermint 8 | 9 | # Install dependencies 10 | RUN apk add --update $PACKAGES 11 | 12 | # Add source files 13 | COPY . . 14 | 15 | # Make the binary 16 | RUN make tools deps build 17 | 18 | # Final image 19 | FROM alpine 20 | 21 | # Install ca-certificates 22 | RUN apk add --update ca-certificates 23 | WORKDIR /root 24 | 25 | # Copy over binaries from the build-env 26 | COPY --from=build-env /go/src/github.com/cosmos/ethermint/build/emintd /usr/bin/emintd 27 | 28 | # Run emintd by default 29 | CMD ["emintd"] 30 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | digest = "1:8038f3159385d2017d12ce7d8ccf25e59554a265d76d8c31f8c80acb589da6c6" 7 | name = "github.com/aristanetworks/goarista" 8 | packages = ["monotime"] 9 | pruneopts = "T" 10 | revision = "5bb443fba8e05f4a819301a63af91fe3cbadcc17" 11 | 12 | [[projects]] 13 | branch = "master" 14 | digest = "1:ad4589ec239820ee99eb01c1ad47ebc5f8e02c4f5103a9b210adff9696d89f36" 15 | name = "github.com/beorn7/perks" 16 | packages = ["quantile"] 17 | pruneopts = "T" 18 | revision = "3a771d992973f24aa725d07868b467d1ddfceafb" 19 | 20 | [[projects]] 21 | branch = "master" 22 | digest = "1:0bd9f11575e82b723837f50e170d010ec29a50aa8ca02a962c439146f03aea55" 23 | name = "github.com/btcsuite/btcd" 24 | packages = ["btcec"] 25 | pruneopts = "T" 26 | revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" 27 | 28 | [[projects]] 29 | digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533" 30 | name = "github.com/btcsuite/btcutil" 31 | packages = ["bech32"] 32 | pruneopts = "T" 33 | revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" 34 | 35 | [[projects]] 36 | branch = "develop" 37 | digest = "1:83eccb94de6a2aadff1bddb7020e3850131645ff45e9ce9093d1604e407130e7" 38 | name = "github.com/cosmos/cosmos-sdk" 39 | packages = [ 40 | "baseapp", 41 | "codec", 42 | "store", 43 | "types", 44 | "version", 45 | "x/auth", 46 | "x/bank", 47 | "x/gov", 48 | "x/gov/tags", 49 | "x/mock", 50 | "x/params", 51 | "x/params/subspace", 52 | "x/slashing", 53 | "x/stake", 54 | "x/stake/keeper", 55 | "x/stake/querier", 56 | "x/stake/tags", 57 | "x/stake/types", 58 | ] 59 | pruneopts = "T" 60 | revision = "ec9c4ea543b5d0f558cf6ad9f1386d26cfe87f28" 61 | 62 | [[projects]] 63 | digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a" 64 | name = "github.com/davecgh/go-spew" 65 | packages = ["spew"] 66 | pruneopts = "T" 67 | revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" 68 | version = "v1.1.1" 69 | 70 | [[projects]] 71 | digest = "1:e47d51dab652d26c3fba6f8cba403f922d02757a82abdc77e90df7948daf296e" 72 | name = "github.com/deckarep/golang-set" 73 | packages = ["."] 74 | pruneopts = "T" 75 | revision = "cbaa98ba5575e67703b32b4b19f73c91f3c4159e" 76 | version = "v1.7.1" 77 | 78 | [[projects]] 79 | branch = "master" 80 | digest = "1:67d0b50be0549e610017cb91e0b0b745ec0cad7c613bc8e18ff2d1c1fc8825a7" 81 | name = "github.com/edsrzf/mmap-go" 82 | packages = ["."] 83 | pruneopts = "T" 84 | revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e" 85 | 86 | [[projects]] 87 | branch = "ethermint-statedb" 88 | digest = "1:00d8053ec8370727a6ecb12cdbfc0b3ce08bce2f8ac2aa34d57402ff09243517" 89 | name = "github.com/ethereum/go-ethereum" 90 | packages = [ 91 | ".", 92 | "accounts", 93 | "accounts/abi", 94 | "accounts/keystore", 95 | "accounts/usbwallet", 96 | "accounts/usbwallet/internal/trezor", 97 | "common", 98 | "common/bitutil", 99 | "common/hexutil", 100 | "common/math", 101 | "common/mclock", 102 | "common/prque", 103 | "consensus", 104 | "consensus/ethash", 105 | "consensus/misc", 106 | "core", 107 | "core/rawdb", 108 | "core/state", 109 | "core/types", 110 | "core/vm", 111 | "crypto", 112 | "crypto/bn256", 113 | "crypto/bn256/cloudflare", 114 | "crypto/bn256/google", 115 | "crypto/ecies", 116 | "crypto/secp256k1", 117 | "crypto/sha3", 118 | "eth/downloader", 119 | "ethdb", 120 | "event", 121 | "internal/ethapi", 122 | "log", 123 | "metrics", 124 | "p2p", 125 | "p2p/discover", 126 | "p2p/discv5", 127 | "p2p/nat", 128 | "p2p/netutil", 129 | "params", 130 | "rlp", 131 | "rpc", 132 | "signer/core", 133 | "trie", 134 | ] 135 | pruneopts = "T" 136 | revision = "0a57b29f0c8e6dc27901fae1c91d3758723b81eb" 137 | source = "github.com/alexanderbez/go-ethereum" 138 | 139 | [[projects]] 140 | digest = "1:7fc160b460a6fc506b37fcca68332464c3f2cd57b6e3f111f26c5bbfd2d5518e" 141 | name = "github.com/fsnotify/fsnotify" 142 | packages = ["."] 143 | pruneopts = "T" 144 | revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" 145 | version = "v1.4.7" 146 | 147 | [[projects]] 148 | digest = "1:0b9c3ad6c948d57a379da9c4e1cdd989b1c73ddc5ec8673f52a9539ce60a109b" 149 | name = "github.com/go-kit/kit" 150 | packages = [ 151 | "log", 152 | "log/level", 153 | "log/term", 154 | "metrics", 155 | "metrics/discard", 156 | "metrics/internal/lv", 157 | "metrics/prometheus", 158 | ] 159 | pruneopts = "T" 160 | revision = "4dc7be5d2d12881735283bcab7352178e190fc71" 161 | version = "v0.6.0" 162 | 163 | [[projects]] 164 | digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659" 165 | name = "github.com/go-logfmt/logfmt" 166 | packages = ["."] 167 | pruneopts = "T" 168 | revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" 169 | version = "v0.3.0" 170 | 171 | [[projects]] 172 | digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" 173 | name = "github.com/go-stack/stack" 174 | packages = ["."] 175 | pruneopts = "T" 176 | revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" 177 | version = "v1.8.0" 178 | 179 | [[projects]] 180 | digest = "1:da39f4a22829ca95e63566208e0ea42d6f055f41dff1b14fdab88d88f62df653" 181 | name = "github.com/gogo/protobuf" 182 | packages = [ 183 | "gogoproto", 184 | "jsonpb", 185 | "proto", 186 | "protoc-gen-gogo/descriptor", 187 | "sortkeys", 188 | "types", 189 | ] 190 | pruneopts = "T" 191 | revision = "636bf0302bc95575d69441b25a2603156ffdddf1" 192 | version = "v1.1.1" 193 | 194 | [[projects]] 195 | digest = "1:832e17df5ff8bbe0e0693d2fb46c5e53f96c662ee804049ce3ab6557df74e3ab" 196 | name = "github.com/golang/protobuf" 197 | packages = [ 198 | "proto", 199 | "protoc-gen-go/descriptor", 200 | "ptypes", 201 | "ptypes/any", 202 | "ptypes/duration", 203 | "ptypes/timestamp", 204 | ] 205 | pruneopts = "T" 206 | revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" 207 | version = "v1.1.0" 208 | 209 | [[projects]] 210 | branch = "master" 211 | digest = "1:6027b20c168728321bd99ad01f35118eded457b01c03e647a84833ab331f2f5b" 212 | name = "github.com/golang/snappy" 213 | packages = ["."] 214 | pruneopts = "T" 215 | revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" 216 | 217 | [[projects]] 218 | digest = "1:3a26588bc48b96825977c1b3df964f8fd842cd6860cc26370588d3563433cf11" 219 | name = "github.com/google/uuid" 220 | packages = ["."] 221 | pruneopts = "T" 222 | revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" 223 | version = "v1.0.0" 224 | 225 | [[projects]] 226 | digest = "1:0ead695774eaa7bf1a284d246febe82054767941de80ab2328a194b088f07026" 227 | name = "github.com/gorilla/websocket" 228 | packages = ["."] 229 | pruneopts = "T" 230 | revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" 231 | version = "v1.2.0" 232 | 233 | [[projects]] 234 | digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5" 235 | name = "github.com/hashicorp/golang-lru" 236 | packages = [ 237 | ".", 238 | "simplelru", 239 | ] 240 | pruneopts = "T" 241 | revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" 242 | version = "v0.5.0" 243 | 244 | [[projects]] 245 | digest = "1:071bcbf82c289fba4d3f63c876bf4f0ba7eda625cd60795e0a03ccbf949e517a" 246 | name = "github.com/hashicorp/hcl" 247 | packages = [ 248 | ".", 249 | "hcl/ast", 250 | "hcl/parser", 251 | "hcl/scanner", 252 | "hcl/strconv", 253 | "hcl/token", 254 | "json/parser", 255 | "json/scanner", 256 | "json/token", 257 | ] 258 | pruneopts = "T" 259 | revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" 260 | version = "v1.0.0" 261 | 262 | [[projects]] 263 | digest = "1:a33cc2e4fb12c58430d2aae5834ff6e84cb609da97692e1fe2aa0cd5ebc92623" 264 | name = "github.com/huin/goupnp" 265 | packages = [ 266 | ".", 267 | "dcps/internetgateway1", 268 | "dcps/internetgateway2", 269 | "httpu", 270 | "scpd", 271 | "soap", 272 | "ssdp", 273 | ] 274 | pruneopts = "T" 275 | revision = "656e61dfadd241c7cbdd22a023fa81ecb6860ea8" 276 | version = "v1.0.0" 277 | 278 | [[projects]] 279 | digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" 280 | name = "github.com/inconshreveable/mousetrap" 281 | packages = ["."] 282 | pruneopts = "T" 283 | revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" 284 | version = "v1.0" 285 | 286 | [[projects]] 287 | digest = "1:32b82e71cf24f8b78323e0d7903c4b90278486283965aa2a19b1ea13763b8f34" 288 | name = "github.com/jackpal/go-nat-pmp" 289 | packages = ["."] 290 | pruneopts = "T" 291 | revision = "c9cfead9f2a36ddf3daa40ba269aa7f4bbba6b62" 292 | version = "v1.0.1" 293 | 294 | [[projects]] 295 | branch = "master" 296 | digest = "1:dc6b1a6801b3055e9bd3da4cd1e568606eb48118cc6f28e947783aa5d998ad74" 297 | name = "github.com/jmhodges/levigo" 298 | packages = ["."] 299 | pruneopts = "T" 300 | revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" 301 | 302 | [[projects]] 303 | branch = "master" 304 | digest = "1:01f1325bf6f105bb633029a2d8b63f1a2357181e60af8dadabf14ad2e84398c5" 305 | name = "github.com/karalabe/hid" 306 | packages = ["."] 307 | pruneopts = "T" 308 | revision = "2b4488a37358b7283de4f9622553e85ebbe73125" 309 | 310 | [[projects]] 311 | branch = "master" 312 | digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" 313 | name = "github.com/kr/logfmt" 314 | packages = ["."] 315 | pruneopts = "T" 316 | revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" 317 | 318 | [[projects]] 319 | digest = "1:53e8c5c79716437e601696140e8b1801aae4204f4ec54a504333702a49572c4f" 320 | name = "github.com/magiconair/properties" 321 | packages = ["."] 322 | pruneopts = "T" 323 | revision = "c2353362d570a7bfa228149c62842019201cfb71" 324 | version = "v1.8.0" 325 | 326 | [[projects]] 327 | digest = "1:a8e3d14801bed585908d130ebfc3b925ba642208e6f30d879437ddfc7bb9b413" 328 | name = "github.com/matttproud/golang_protobuf_extensions" 329 | packages = ["pbutil"] 330 | pruneopts = "T" 331 | revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" 332 | version = "v1.0.1" 333 | 334 | [[projects]] 335 | digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" 336 | name = "github.com/mitchellh/mapstructure" 337 | packages = ["."] 338 | pruneopts = "T" 339 | revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" 340 | version = "v1.1.2" 341 | 342 | [[projects]] 343 | digest = "1:e5d0bd87abc2781d14e274807a470acd180f0499f8bf5bb18606e9ec22ad9de9" 344 | name = "github.com/pborman/uuid" 345 | packages = ["."] 346 | pruneopts = "T" 347 | revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" 348 | version = "v1.2" 349 | 350 | [[projects]] 351 | digest = "1:ccf9949c9c53e85dcb7e2905fc620571422567040925381e6baa62f0b7b850fe" 352 | name = "github.com/pelletier/go-toml" 353 | packages = ["."] 354 | pruneopts = "T" 355 | revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" 356 | version = "v1.2.0" 357 | 358 | [[projects]] 359 | digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" 360 | name = "github.com/pkg/errors" 361 | packages = ["."] 362 | pruneopts = "T" 363 | revision = "645ef00459ed84a119197bfb8d8205042c6df63d" 364 | version = "v0.8.0" 365 | 366 | [[projects]] 367 | digest = "1:22aa691fe0213cb5c07d103f9effebcb7ad04bee45a0ce5fe5369d0ca2ec3a1f" 368 | name = "github.com/pmezard/go-difflib" 369 | packages = ["difflib"] 370 | pruneopts = "T" 371 | revision = "792786c7400a136282c1664665ae0a8db921c6c2" 372 | version = "v1.0.0" 373 | 374 | [[projects]] 375 | digest = "1:98aa8bc119587e8bddd558bf2921a645ea6c0ff3195760142113d4dc7cab509f" 376 | name = "github.com/prometheus/client_golang" 377 | packages = [ 378 | "prometheus", 379 | "prometheus/internal", 380 | "prometheus/promhttp", 381 | ] 382 | pruneopts = "T" 383 | revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3" 384 | version = "v0.9.1" 385 | 386 | [[projects]] 387 | branch = "master" 388 | digest = "1:185cf55b1f44a1bf243558901c3f06efa5c64ba62cfdcbb1bf7bbe8c3fb68561" 389 | name = "github.com/prometheus/client_model" 390 | packages = ["go"] 391 | pruneopts = "T" 392 | revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" 393 | 394 | [[projects]] 395 | branch = "master" 396 | digest = "1:95442856f5c1df4ff0be91b5a320ee717dd539d4091a3574aeb96bfbd407aa41" 397 | name = "github.com/prometheus/common" 398 | packages = [ 399 | "expfmt", 400 | "internal/bitbucket.org/ww/goautoneg", 401 | "model", 402 | ] 403 | pruneopts = "T" 404 | revision = "0b1957f9d949dfa3084171a6ec5642b38055276a" 405 | 406 | [[projects]] 407 | branch = "master" 408 | digest = "1:57bf59ce0c73cef5cc4796a5d64f1ec5b81f6335f242d4a80a62b0a6edc4b77f" 409 | name = "github.com/prometheus/procfs" 410 | packages = [ 411 | ".", 412 | "internal/util", 413 | "nfs", 414 | "xfs", 415 | ] 416 | pruneopts = "T" 417 | revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" 418 | 419 | [[projects]] 420 | digest = "1:523d2c2500965d035691347a6d30befd53fde95fad16e0b94bef5d3d2cca8ff7" 421 | name = "github.com/rcrowley/go-metrics" 422 | packages = ["."] 423 | pruneopts = "T" 424 | revision = "e2704e165165ec55d062f5919b4b29494e9fa790" 425 | 426 | [[projects]] 427 | digest = "1:9787d2d3220cbfd444596afd03ab0abcf391df169b789fbe3eae27fa2e426cf6" 428 | name = "github.com/rjeczalik/notify" 429 | packages = ["."] 430 | pruneopts = "T" 431 | revision = "69d839f37b13a8cb7a78366f7633a4071cb43be7" 432 | version = "v0.9.2" 433 | 434 | [[projects]] 435 | digest = "1:a8a03bca5a81878daa4958136f3372af00437c61129ca088a430b0b786b9378a" 436 | name = "github.com/rs/cors" 437 | packages = ["."] 438 | pruneopts = "T" 439 | revision = "9a47f48565a795472d43519dd49aac781f3034fb" 440 | version = "v1.6.0" 441 | 442 | [[projects]] 443 | digest = "1:b7bf9fd95d38ebe6726a63b7d0320611f7c920c64e2c8313eba0cec51926bf55" 444 | name = "github.com/spf13/afero" 445 | packages = [ 446 | ".", 447 | "mem", 448 | ] 449 | pruneopts = "T" 450 | revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" 451 | version = "v1.1.2" 452 | 453 | [[projects]] 454 | digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" 455 | name = "github.com/spf13/cast" 456 | packages = ["."] 457 | pruneopts = "T" 458 | revision = "8c9545af88b134710ab1cd196795e7f2388358d7" 459 | version = "v1.3.0" 460 | 461 | [[projects]] 462 | digest = "1:52565bd966162d1f4579757f66ce6a7ca9054e7f6b662f0c7c96e4dd228fd017" 463 | name = "github.com/spf13/cobra" 464 | packages = ["."] 465 | pruneopts = "T" 466 | revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" 467 | version = "v0.0.1" 468 | 469 | [[projects]] 470 | digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" 471 | name = "github.com/spf13/jwalterweatherman" 472 | packages = ["."] 473 | pruneopts = "T" 474 | revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" 475 | version = "v1.0.0" 476 | 477 | [[projects]] 478 | digest = "1:0f775ea7a72e30d5574267692aaa9ff265aafd15214a7ae7db26bc77f2ca04dc" 479 | name = "github.com/spf13/pflag" 480 | packages = ["."] 481 | pruneopts = "T" 482 | revision = "298182f68c66c05229eb03ac171abe6e309ee79a" 483 | version = "v1.0.3" 484 | 485 | [[projects]] 486 | digest = "1:a8a1cbf83d6ba47a3421e51b5dd1999e1f64f6175c64295d6b42bdea55312a79" 487 | name = "github.com/spf13/viper" 488 | packages = ["."] 489 | pruneopts = "T" 490 | revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" 491 | version = "v1.0.0" 492 | 493 | [[projects]] 494 | digest = "1:8f39978e4fb2a11d43cc954f2ab458cb38995d4c1557b6d3a7c8cafe0ec2277c" 495 | name = "github.com/stretchr/testify" 496 | packages = [ 497 | "assert", 498 | "require", 499 | "suite", 500 | ] 501 | pruneopts = "T" 502 | revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" 503 | version = "v1.2.1" 504 | 505 | [[projects]] 506 | branch = "master" 507 | digest = "1:fa0605d74039818b662892c950cfd9938ab81ebb5f8e2479ceb4734cdae21df3" 508 | name = "github.com/syndtr/goleveldb" 509 | packages = [ 510 | "leveldb", 511 | "leveldb/cache", 512 | "leveldb/comparer", 513 | "leveldb/errors", 514 | "leveldb/filter", 515 | "leveldb/iterator", 516 | "leveldb/journal", 517 | "leveldb/memdb", 518 | "leveldb/opt", 519 | "leveldb/storage", 520 | "leveldb/table", 521 | "leveldb/util", 522 | ] 523 | pruneopts = "T" 524 | revision = "f9080354173f192dfc8821931eacf9cfd6819253" 525 | 526 | [[projects]] 527 | digest = "1:71ffd1fca92b4972ecd588cf13d9929d4f444659788e9128d055a9126498d41d" 528 | name = "github.com/tendermint/btcd" 529 | packages = ["btcec"] 530 | pruneopts = "T" 531 | revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" 532 | 533 | [[projects]] 534 | digest = "1:02462ad4cc9b135c4ebfb9edccb53e8c705bbebdbf8664799ea641440188387e" 535 | name = "github.com/tendermint/go-amino" 536 | packages = ["."] 537 | pruneopts = "T" 538 | revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885" 539 | version = "v0.14.1" 540 | 541 | [[projects]] 542 | digest = "1:8a1dc8fc625c867614b48327112718c51c0ca83453c8a043f2f23721e19b353f" 543 | name = "github.com/tendermint/iavl" 544 | packages = ["."] 545 | pruneopts = "T" 546 | revision = "de0740903a67b624d887f9055d4c60175dcfa758" 547 | version = "v0.12.0" 548 | 549 | [[projects]] 550 | digest = "1:844c7ba6332e1e6f073d7ac69768fd19f761bdf5964b559bd4b47103a0629144" 551 | name = "github.com/tendermint/tendermint" 552 | packages = [ 553 | "abci/client", 554 | "abci/example/code", 555 | "abci/example/kvstore", 556 | "abci/server", 557 | "abci/types", 558 | "blockchain", 559 | "cmd/tendermint/commands", 560 | "config", 561 | "consensus", 562 | "consensus/types", 563 | "crypto", 564 | "crypto/ed25519", 565 | "crypto/encoding/amino", 566 | "crypto/merkle", 567 | "crypto/multisig", 568 | "crypto/multisig/bitarray", 569 | "crypto/secp256k1", 570 | "crypto/tmhash", 571 | "evidence", 572 | "libs/autofile", 573 | "libs/bech32", 574 | "libs/cli", 575 | "libs/cli/flags", 576 | "libs/clist", 577 | "libs/common", 578 | "libs/db", 579 | "libs/events", 580 | "libs/fail", 581 | "libs/flowrate", 582 | "libs/log", 583 | "libs/pubsub", 584 | "libs/pubsub/query", 585 | "lite", 586 | "lite/client", 587 | "lite/errors", 588 | "lite/proxy", 589 | "mempool", 590 | "node", 591 | "p2p", 592 | "p2p/conn", 593 | "p2p/pex", 594 | "p2p/upnp", 595 | "privval", 596 | "proxy", 597 | "rpc/client", 598 | "rpc/core", 599 | "rpc/core/types", 600 | "rpc/grpc", 601 | "rpc/lib/client", 602 | "rpc/lib/server", 603 | "rpc/lib/types", 604 | "state", 605 | "state/txindex", 606 | "state/txindex/kv", 607 | "state/txindex/null", 608 | "types", 609 | "types/time", 610 | "version", 611 | ] 612 | pruneopts = "T" 613 | revision = "v0.27.0" 614 | 615 | [[projects]] 616 | digest = "1:d738326441b0b732070d727891855573dcb579e74d82fcf9a9459d3257f2eb0c" 617 | name = "golang.org/x/crypto" 618 | packages = [ 619 | "chacha20poly1305", 620 | "curve25519", 621 | "ed25519", 622 | "ed25519/internal/edwards25519", 623 | "hkdf", 624 | "internal/chacha20", 625 | "internal/subtle", 626 | "nacl/box", 627 | "nacl/secretbox", 628 | "pbkdf2", 629 | "poly1305", 630 | "ripemd160", 631 | "salsa20/salsa", 632 | "scrypt", 633 | "ssh/terminal", 634 | ] 635 | pruneopts = "T" 636 | revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" 637 | source = "https://github.com/tendermint/crypto" 638 | 639 | [[projects]] 640 | digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7" 641 | name = "golang.org/x/net" 642 | packages = [ 643 | "context", 644 | "html", 645 | "html/atom", 646 | "html/charset", 647 | "http/httpguts", 648 | "http2", 649 | "http2/hpack", 650 | "idna", 651 | "internal/timeseries", 652 | "netutil", 653 | "trace", 654 | "websocket", 655 | ] 656 | pruneopts = "T" 657 | revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" 658 | 659 | [[projects]] 660 | digest = "1:96672c90ede3a9cd379151c13c436c93efe845c4a3fdd2ce2a94e9c96f233a2c" 661 | name = "golang.org/x/sys" 662 | packages = [ 663 | "cpu", 664 | "unix", 665 | "windows", 666 | ] 667 | pruneopts = "T" 668 | revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" 669 | 670 | [[projects]] 671 | digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a" 672 | name = "golang.org/x/text" 673 | packages = [ 674 | "collate", 675 | "collate/build", 676 | "encoding", 677 | "encoding/charmap", 678 | "encoding/htmlindex", 679 | "encoding/internal", 680 | "encoding/internal/identifier", 681 | "encoding/japanese", 682 | "encoding/korean", 683 | "encoding/simplifiedchinese", 684 | "encoding/traditionalchinese", 685 | "encoding/unicode", 686 | "internal/colltab", 687 | "internal/gen", 688 | "internal/tag", 689 | "internal/triegen", 690 | "internal/ucd", 691 | "internal/utf8internal", 692 | "language", 693 | "runes", 694 | "secure/bidirule", 695 | "transform", 696 | "unicode/bidi", 697 | "unicode/cldr", 698 | "unicode/norm", 699 | "unicode/rangetable", 700 | ] 701 | pruneopts = "T" 702 | revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" 703 | version = "v0.3.0" 704 | 705 | [[projects]] 706 | branch = "master" 707 | digest = "1:2460b53d2a66eb9897a17c59ce16c82eeec9affaa31a3ce5814d254abc80fbbd" 708 | name = "google.golang.org/genproto" 709 | packages = ["googleapis/rpc/status"] 710 | pruneopts = "T" 711 | revision = "5fc9ac5403620be16bcdb0c8e7644b1178472c3b" 712 | 713 | [[projects]] 714 | digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770" 715 | name = "google.golang.org/grpc" 716 | packages = [ 717 | ".", 718 | "balancer", 719 | "balancer/base", 720 | "balancer/roundrobin", 721 | "codes", 722 | "connectivity", 723 | "credentials", 724 | "encoding", 725 | "encoding/proto", 726 | "grpclog", 727 | "internal", 728 | "internal/backoff", 729 | "internal/channelz", 730 | "internal/grpcrand", 731 | "keepalive", 732 | "metadata", 733 | "naming", 734 | "peer", 735 | "resolver", 736 | "resolver/dns", 737 | "resolver/passthrough", 738 | "stats", 739 | "status", 740 | "tap", 741 | "transport", 742 | ] 743 | pruneopts = "T" 744 | revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" 745 | version = "v1.13.0" 746 | 747 | [[projects]] 748 | branch = "v2" 749 | digest = "1:3d3f9391ab615be8655ae0d686a1564f3fec413979bb1aaf018bac1ec1bb1cc7" 750 | name = "gopkg.in/natefinch/npipe.v2" 751 | packages = ["."] 752 | pruneopts = "T" 753 | revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6" 754 | 755 | [[projects]] 756 | digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" 757 | name = "gopkg.in/yaml.v2" 758 | packages = ["."] 759 | pruneopts = "T" 760 | revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" 761 | version = "v2.2.1" 762 | 763 | [solve-meta] 764 | analyzer-name = "dep" 765 | analyzer-version = 1 766 | input-imports = [ 767 | "github.com/cosmos/cosmos-sdk/baseapp", 768 | "github.com/cosmos/cosmos-sdk/codec", 769 | "github.com/cosmos/cosmos-sdk/store", 770 | "github.com/cosmos/cosmos-sdk/types", 771 | "github.com/cosmos/cosmos-sdk/x/auth", 772 | "github.com/cosmos/cosmos-sdk/x/bank", 773 | "github.com/cosmos/cosmos-sdk/x/gov", 774 | "github.com/cosmos/cosmos-sdk/x/params", 775 | "github.com/cosmos/cosmos-sdk/x/slashing", 776 | "github.com/cosmos/cosmos-sdk/x/stake", 777 | "github.com/ethereum/go-ethereum/common", 778 | "github.com/ethereum/go-ethereum/common/hexutil", 779 | "github.com/ethereum/go-ethereum/consensus", 780 | "github.com/ethereum/go-ethereum/consensus/ethash", 781 | "github.com/ethereum/go-ethereum/consensus/misc", 782 | "github.com/ethereum/go-ethereum/core", 783 | "github.com/ethereum/go-ethereum/core/state", 784 | "github.com/ethereum/go-ethereum/core/types", 785 | "github.com/ethereum/go-ethereum/core/vm", 786 | "github.com/ethereum/go-ethereum/crypto", 787 | "github.com/ethereum/go-ethereum/crypto/secp256k1", 788 | "github.com/ethereum/go-ethereum/crypto/sha3", 789 | "github.com/ethereum/go-ethereum/params", 790 | "github.com/ethereum/go-ethereum/rlp", 791 | "github.com/ethereum/go-ethereum/rpc", 792 | "github.com/ethereum/go-ethereum/signer/core", 793 | "github.com/pkg/errors", 794 | "github.com/stretchr/testify/require", 795 | "github.com/stretchr/testify/suite", 796 | "github.com/tendermint/tendermint/abci/types", 797 | "github.com/tendermint/tendermint/crypto", 798 | "github.com/tendermint/tendermint/libs/common", 799 | "github.com/tendermint/tendermint/libs/db", 800 | "github.com/tendermint/tendermint/libs/log", 801 | ] 802 | solver-name = "gps-cdcl" 803 | solver-version = 1 804 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | [[constraint]] 2 | name = "github.com/ethereum/go-ethereum" 3 | # TODO: Remove this forked source and branch once Turbo-Geth is ready as a 4 | # client. 5 | source = "github.com/alexanderbez/go-ethereum" 6 | branch = "ethermint-statedb" 7 | 8 | [[constraint]] 9 | name = "github.com/cosmos/cosmos-sdk" 10 | revision = "ec9c4ea543b5d0f558cf6ad9f1386d26cfe87f28" 11 | # version = "v0.28.0" 12 | 13 | [[constraint]] 14 | name = "github.com/hashicorp/golang-lru" 15 | revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3" 16 | 17 | [[constraint]] 18 | name = "github.com/spf13/cobra" 19 | version = "~0.0.1" 20 | 21 | [[constraint]] 22 | name = "github.com/stretchr/testify" 23 | version = "=1.2.1" 24 | 25 | [[constraint]] 26 | name = "github.com/pkg/errors" 27 | version = "=0.8.0" 28 | 29 | [[constraint]] 30 | name = "golang.org/x/net" 31 | revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" 32 | 33 | ####################### 34 | # dependecy overrides # 35 | ####################### 36 | 37 | [[override]] 38 | name = "gopkg.in/fatih/set.v0" 39 | version = "=0.1.0" 40 | 41 | [[override]] 42 | name = "github.com/tendermint/go-amino" 43 | version = "v0.14.1" 44 | 45 | [[override]] 46 | name = "golang.org/x/crypto" 47 | source = "https://github.com/tendermint/crypto" 48 | revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" 49 | 50 | [[override]] 51 | name = "github.com/tendermint/iavl" 52 | version = "v0.12.0" 53 | 54 | [[override]] 55 | name = "github.com/tendermint/tendermint" 56 | revision = "v0.27.0" 57 | 58 | [[override]] 59 | name = "golang.org/x/sys" 60 | revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" 61 | 62 | [prune] 63 | go-tests = true 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Tendermint. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | PACKAGES=$(shell go list ./... | grep -Ev 'vendor|importer') 16 | COMMIT_HASH := $(shell git rev-parse --short HEAD) 17 | BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/ethermint/version.GitCommit=${COMMIT_HASH}" 18 | DOCKER_TAG = unstable 19 | DOCKER_IMAGE = cosmos/ethermint 20 | ETHERMINT_DAEMON_BINARY = emintd 21 | ETHERMINT_CLI_BINARY = emintcli 22 | 23 | all: tools deps install 24 | 25 | ####################### 26 | ### Build / Install ### 27 | ####################### 28 | 29 | build: 30 | ifeq ($(OS),Windows_NT) 31 | go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY).exe ./cmd/emintd 32 | go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY).exe ./cmd/emintcli 33 | else 34 | go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY) ./cmd/emintd/ 35 | go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY) ./cmd/emintcli/ 36 | endif 37 | 38 | install: 39 | go install $(BUILD_FLAGS) ./cmd/emintd 40 | go install $(BUILD_FLAGS) ./cmd/emintcli 41 | 42 | clean: 43 | @rm -rf ./build ./vendor 44 | 45 | update-tools: 46 | @echo "--> Updating vendor dependencies" 47 | go get -u -v $(DEP) $(GOLINT) $(GOMETALINTER) $(UNCONVERT) $(INEFFASSIGN) $(MISSPELL) $(ERRCHECK) $(UNPARAM) 48 | 49 | 50 | ############################ 51 | ### Tools / Dependencies ### 52 | ############################ 53 | 54 | ########################################################## 55 | ### TODO: Move tool depedencies to a separate makefile ### 56 | ########################################################## 57 | 58 | DEP = github.com/golang/dep/cmd/dep 59 | GOLINT = github.com/tendermint/lint/golint 60 | GOMETALINTER = gopkg.in/alecthomas/gometalinter.v2 61 | UNCONVERT = github.com/mdempsky/unconvert 62 | INEFFASSIGN = github.com/gordonklaus/ineffassign 63 | MISSPELL = github.com/client9/misspell/cmd/misspell 64 | ERRCHECK = github.com/kisielk/errcheck 65 | UNPARAM = mvdan.cc/unparam 66 | 67 | DEP_CHECK := $(shell command -v dep 2> /dev/null) 68 | GOLINT_CHECK := $(shell command -v golint 2> /dev/null) 69 | GOMETALINTER_CHECK := $(shell command -v gometalinter.v2 2> /dev/null) 70 | UNCONVERT_CHECK := $(shell command -v unconvert 2> /dev/null) 71 | INEFFASSIGN_CHECK := $(shell command -v ineffassign 2> /dev/null) 72 | MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null) 73 | ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null) 74 | UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null) 75 | 76 | tools: 77 | ifdef DEP_CHECK 78 | @echo "Dep is already installed. Run 'make update-tools' to update." 79 | else 80 | @echo "--> Installing dep" 81 | go get -v $(DEP) 82 | endif 83 | ifdef GOLINT_CHECK 84 | @echo "Golint is already installed. Run 'make update-tools' to update." 85 | else 86 | @echo "--> Installing golint" 87 | go get -v $(GOLINT) 88 | endif 89 | ifdef GOMETALINTER_CHECK 90 | @echo "Gometalinter.v2 is already installed. Run 'make update-tools' to update." 91 | else 92 | @echo "--> Installing gometalinter.v2" 93 | go get -v $(GOMETALINTER) 94 | endif 95 | ifdef UNCONVERT_CHECK 96 | @echo "Unconvert is already installed. Run 'make update-tools' to update." 97 | else 98 | @echo "--> Installing unconvert" 99 | go get -v $(UNCONVERT) 100 | endif 101 | ifdef INEFFASSIGN_CHECK 102 | @echo "Ineffassign is already installed. Run 'make update-tools' to update." 103 | else 104 | @echo "--> Installing ineffassign" 105 | go get -v $(INEFFASSIGN) 106 | endif 107 | ifdef MISSPELL_CHECK 108 | @echo "misspell is already installed. Run 'make update-tools' to update." 109 | else 110 | @echo "--> Installing misspell" 111 | go get -v $(MISSPELL) 112 | endif 113 | ifdef ERRCHECK_CHECK 114 | @echo "errcheck is already installed. Run 'make update-tools' to update." 115 | else 116 | @echo "--> Installing errcheck" 117 | go get -v $(ERRCHECK) 118 | endif 119 | ifdef UNPARAM_CHECK 120 | @echo "unparam is already installed. Run 'make update-tools' to update." 121 | else 122 | @echo "--> Installing unparam" 123 | go get -v $(UNPARAM) 124 | endif 125 | 126 | deps: 127 | @rm -rf vendor/ 128 | @echo "--> Running dep ensure" 129 | @dep ensure -v 130 | 131 | ####################### 132 | ### Testing / Misc. ### 133 | ####################### 134 | 135 | test: test-unit 136 | 137 | test-unit: 138 | @go test -v --vet=off $(PACKAGES) 139 | 140 | test-race: 141 | @go test -v --vet=off -race $(PACKAGES) 142 | 143 | test-cli: 144 | @echo "NO CLI TESTS" 145 | 146 | test-lint: 147 | @echo "--> Running gometalinter..." 148 | @gometalinter.v2 --config=gometalinter.json --exclude=vendor ./... 149 | 150 | test-import: 151 | @go test ./importer -v --vet=off --run=TestImportBlocks --datadir tmp \ 152 | --blockchain blockchain --timeout=5m 153 | 154 | godocs: 155 | @echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint" 156 | godoc -http=:6060 157 | 158 | docker: 159 | docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . 160 | docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest 161 | docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} 162 | 163 | format: 164 | @echo "--> Formatting go files" 165 | @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -w -s 166 | @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs misspell -w 167 | 168 | .PHONY: build install update-tools tools deps godocs clean format test-lint \ 169 | test-cli test-race test-unit test test-import 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > This repo is archived as of October 7, 2020 but made available for historical record. 2 | 3 | ### Active development of Ethermint is located at [github.com/cosmos/ethermint](https://github.com/cosmos/ethermint) 4 | 5 | 6 | [![CircleCI](https://circleci.com/gh/cosmos/ethermint.svg?style=svg)](https://circleci.com/gh/cosmos/ethermint) 7 | [![](https://godoc.org/github.com/cosmos/ethermint?status.svg)](http://godoc.org/github.com/cosmos/ethermint) [![Go Report Card](https://goreportcard.com/badge/github.com/cosmos/ethermint)](https://goreportcard.com/report/github.com/cosmos/ethermint) 8 | 9 | # Ethermint 10 | 11 | 12 | \***\*WARNING:\*\*** Ethermint is under VERY ACTIVE DEVELOPMENT and should be treated as pre-alpha software. This means it is not meant to be run in production, its APIs are subject to change without warning and should not be relied upon, and it should not be used to hold any value. We will remove this warning when we have a release that is stable, secure, and properly tested. 13 | 14 | ## What is it? 15 | 16 | `ethermint` will be an implementation of the EVM that runs on top of [`tendermint`](https://github.com/tendermint/tendermint) consensus, a Proof of Stake system. This project has as its primary goals: 17 | 18 | - [Hard Spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) enablement: This is the ability to take a token from the Ethereum mainnet and "spoon" (shift) the balances over to another network. This feature is intended to make it easy for applications that require more transactions than the Ethereum main chain can provide to move their code over to a compatible chain with much more capacity. 19 | - Web3 Compatibility: In order enable applications to be moved over to an ethermint chain existing tooling (i.e. web3 compatible clients) need to be able to interact with `ethermint`. 20 | 21 | ### Implementation 22 | 23 | #### Completed 24 | 25 | - Have a working implementation that can parse and validate the existing ETH Chain and persist it in a Tendermint store 26 | - Implement Ethereum transactions in the CosmosSDK 27 | 28 | #### Current Work 29 | 30 | - Implement web3 compatible API layer 31 | - Implement the EVM as a CosmosSDK module 32 | - Allow the Ethermint EVM to interact with other [Cosmos SDK modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/app3.md) 33 | 34 | #### Next Steps 35 | 36 | - Hard spoon enablement: The ability to export state from `geth` and import token balances into Ethermint 37 | - Ethermint is a functioning Cosmos SDK application and can be deployed as its own zone 38 | - Full web3 compatibility will enable existing Ethereum applications to use Ethermint 39 | 40 | ### Building Ethermint 41 | 42 | To build, execute the following commands: 43 | 44 | ```bash 45 | # To build the binary and put the resulting binary in ./build 46 | $ make tools deps build 47 | 48 | # To build the project and install it in $GOBIN 49 | $ make tools deps install 50 | ``` 51 | 52 | ### Tests 53 | 54 | Integration tests are invoked via: 55 | 56 | ```bash 57 | $ make test 58 | ``` 59 | 60 | To run CLI tests, execute: 61 | 62 | ```bash 63 | $ make test-cli 64 | ``` 65 | 66 | #### Ethereum Mainnet Import 67 | 68 | There is an included Ethereum mainnet exported blockchain file in `importer/blockchain` 69 | that includes blocks up to height `97638`. To execute and test a full import of 70 | these blocks using the EVM module, execute: 71 | 72 | ```bash 73 | $ make test-import 74 | ``` 75 | 76 | You may also provide a custom blockchain export file to test importing more blocks 77 | via the `--blockchain` flag. See `TestImportBlocks` for further documentation. 78 | 79 | ### Community 80 | 81 | The following chat channels and forums are a great spot to ask questions about Ethermint: 82 | 83 | - [Cosmos Riot Chat Channel](https://riot.im/app/#/group/+cosmos:matrix.org) 84 | - Cosmos Forum [![Discourse status](https://img.shields.io/discourse/https/forum.cosmos.network/status.svg)](https://forum.cosmos.network) 85 | -------------------------------------------------------------------------------- /app/ante.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/x/auth" 9 | 10 | "github.com/cosmos/ethermint/crypto" 11 | "github.com/cosmos/ethermint/types" 12 | evmtypes "github.com/cosmos/ethermint/x/evm/types" 13 | 14 | ethcmn "github.com/ethereum/go-ethereum/common" 15 | ethcore "github.com/ethereum/go-ethereum/core" 16 | 17 | tmcrypto "github.com/tendermint/tendermint/crypto" 18 | ) 19 | 20 | const ( 21 | memoCostPerByte sdk.Gas = 3 22 | secp256k1VerifyCost = 21000 23 | ) 24 | 25 | // NewAnteHandler returns an ante handler responsible for attempting to route an 26 | // Ethereum or SDK transaction to an internal ante handler for performing 27 | // transaction-level processing (e.g. fee payment, signature verification) before 28 | // being passed onto it's respective handler. 29 | // 30 | // NOTE: The EVM will already consume (intrinsic) gas for signature verification 31 | // and covering input size as well as handling nonce incrementing. 32 | func NewAnteHandler(ak auth.AccountKeeper, fck auth.FeeCollectionKeeper) sdk.AnteHandler { 33 | return func( 34 | ctx sdk.Context, tx sdk.Tx, sim bool, 35 | ) (newCtx sdk.Context, res sdk.Result, abort bool) { 36 | 37 | switch castTx := tx.(type) { 38 | case auth.StdTx: 39 | return sdkAnteHandler(ctx, ak, fck, castTx, sim) 40 | 41 | case *evmtypes.EthereumTxMsg: 42 | return ethAnteHandler(ctx, castTx, ak) 43 | 44 | default: 45 | return ctx, sdk.ErrInternal(fmt.Sprintf("transaction type invalid: %T", tx)).Result(), true 46 | } 47 | } 48 | } 49 | 50 | // ---------------------------------------------------------------------------- 51 | // SDK Ante Handler 52 | 53 | func sdkAnteHandler( 54 | ctx sdk.Context, ak auth.AccountKeeper, fck auth.FeeCollectionKeeper, stdTx auth.StdTx, sim bool, 55 | ) (newCtx sdk.Context, res sdk.Result, abort bool) { 56 | 57 | // Ensure that the provided fees meet a minimum threshold for the validator, 58 | // if this is a CheckTx. This is only for local mempool purposes, and thus 59 | // is only ran on check tx. 60 | if ctx.IsCheckTx() && !sim { 61 | res := auth.EnsureSufficientMempoolFees(ctx, stdTx) 62 | if !res.IsOK() { 63 | return newCtx, res, true 64 | } 65 | } 66 | 67 | newCtx = auth.SetGasMeter(sim, ctx, stdTx) 68 | 69 | // AnteHandlers must have their own defer/recover in order for the BaseApp 70 | // to know how much gas was used! This is because the GasMeter is created in 71 | // the AnteHandler, but if it panics the context won't be set properly in 72 | // runTx's recover call. 73 | defer func() { 74 | if r := recover(); r != nil { 75 | switch rType := r.(type) { 76 | case sdk.ErrorOutOfGas: 77 | log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor) 78 | res = sdk.ErrOutOfGas(log).Result() 79 | res.GasWanted = stdTx.Fee.Gas 80 | res.GasUsed = newCtx.GasMeter().GasConsumed() 81 | abort = true 82 | default: 83 | panic(r) 84 | } 85 | } 86 | }() 87 | 88 | if err := stdTx.ValidateBasic(); err != nil { 89 | return newCtx, err.Result(), true 90 | } 91 | 92 | newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo") 93 | 94 | signerAccs, res := auth.GetSignerAccs(newCtx, ak, stdTx.GetSigners()) 95 | if !res.IsOK() { 96 | return newCtx, res, true 97 | } 98 | 99 | // the first signer pays the transaction fees 100 | if !stdTx.Fee.Amount.IsZero() { 101 | signerAccs[0], res = auth.DeductFees(signerAccs[0], stdTx.Fee) 102 | if !res.IsOK() { 103 | return newCtx, res, true 104 | } 105 | 106 | fck.AddCollectedFees(newCtx, stdTx.Fee.Amount) 107 | } 108 | 109 | isGenesis := ctx.BlockHeight() == 0 110 | signBytesList := auth.GetSignBytesList(newCtx.ChainID(), stdTx, signerAccs, isGenesis) 111 | stdSigs := stdTx.GetSignatures() 112 | 113 | for i := 0; i < len(stdSigs); i++ { 114 | // check signature, return account with incremented nonce 115 | signerAccs[i], res = processSig(newCtx, signerAccs[i], stdSigs[i], signBytesList[i], sim) 116 | if !res.IsOK() { 117 | return newCtx, res, true 118 | } 119 | 120 | ak.SetAccount(newCtx, signerAccs[i]) 121 | } 122 | 123 | return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false 124 | } 125 | 126 | // processSig verifies the signature and increments the nonce. If the account 127 | // doesn't have a pubkey, set it. 128 | func processSig( 129 | ctx sdk.Context, acc auth.Account, sig auth.StdSignature, signBytes []byte, sim bool, 130 | ) (updatedAcc auth.Account, res sdk.Result) { 131 | 132 | pubKey, res := auth.ProcessPubKey(acc, sig, sim) 133 | if !res.IsOK() { 134 | return nil, res 135 | } 136 | 137 | err := acc.SetPubKey(pubKey) 138 | if err != nil { 139 | return nil, sdk.ErrInternal("failed to set PubKey on signer account").Result() 140 | } 141 | 142 | consumeSigGas(ctx.GasMeter(), pubKey) 143 | if !sim && !pubKey.VerifyBytes(signBytes, sig.Signature) { 144 | return nil, sdk.ErrUnauthorized("signature verification failed").Result() 145 | } 146 | 147 | err = acc.SetSequence(acc.GetSequence() + 1) 148 | if err != nil { 149 | return nil, sdk.ErrInternal("failed to set account nonce").Result() 150 | } 151 | 152 | return acc, res 153 | } 154 | 155 | func consumeSigGas(meter sdk.GasMeter, pubkey tmcrypto.PubKey) { 156 | switch pubkey.(type) { 157 | case crypto.PubKeySecp256k1: 158 | meter.ConsumeGas(secp256k1VerifyCost, "ante verify: secp256k1") 159 | default: 160 | panic("Unrecognized signature type") 161 | } 162 | } 163 | 164 | // ---------------------------------------------------------------------------- 165 | // Ethereum Ante Handler 166 | 167 | // ethAnteHandler defines an internal ante handler for an Ethereum transaction 168 | // ethTxMsg. During CheckTx, the transaction is passed through a series of 169 | // pre-message execution validation checks such as signature and account 170 | // verification in addition to minimum fees being checked. Otherwise, during 171 | // DeliverTx, the transaction is simply passed to the EVM which will also 172 | // perform the same series of checks. The distinction is made in CheckTx to 173 | // prevent spam and DoS attacks. 174 | func ethAnteHandler( 175 | ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg, ak auth.AccountKeeper, 176 | ) (newCtx sdk.Context, res sdk.Result, abort bool) { 177 | 178 | if ctx.IsCheckTx() { 179 | // Only perform pre-message (Ethereum transaction) execution validation 180 | // during CheckTx. Otherwise, during DeliverTx the EVM will handle them. 181 | if res := validateEthTxCheckTx(ctx, ak, ethTxMsg); !res.IsOK() { 182 | return newCtx, res, true 183 | } 184 | } 185 | 186 | return ctx, sdk.Result{}, false 187 | } 188 | 189 | func validateEthTxCheckTx( 190 | ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, 191 | ) sdk.Result { 192 | 193 | // parse the chainID from a string to a base-10 integer 194 | chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) 195 | if !ok { 196 | return types.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())).Result() 197 | } 198 | 199 | // Validate sufficient fees have been provided that meet a minimum threshold 200 | // defined by the proposer (for mempool purposes during CheckTx). 201 | if res := ensureSufficientMempoolFees(ctx, ethTxMsg); !res.IsOK() { 202 | return res 203 | } 204 | 205 | // validate enough intrinsic gas 206 | if res := validateIntrinsicGas(ethTxMsg); !res.IsOK() { 207 | return res 208 | } 209 | 210 | // validate sender/signature 211 | signer, err := ethTxMsg.VerifySig(chainID) 212 | if err != nil { 213 | return sdk.ErrUnauthorized("signature verification failed").Result() 214 | } 215 | 216 | // validate account (nonce and balance checks) 217 | if res := validateAccount(ctx, ak, ethTxMsg, signer); !res.IsOK() { 218 | return res 219 | } 220 | 221 | return sdk.Result{} 222 | } 223 | 224 | // validateIntrinsicGas validates that the Ethereum tx message has enough to 225 | // cover intrinsic gas. Intrinsic gas for a transaction is the amount of gas 226 | // that the transaction uses before the transaction is executed. The gas is a 227 | // constant value of 21000 plus any cost inccured by additional bytes of data 228 | // supplied with the transaction. 229 | func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) sdk.Result { 230 | gas, err := ethcore.IntrinsicGas(ethTxMsg.Data.Payload, ethTxMsg.To() == nil, false) 231 | if err != nil { 232 | return sdk.ErrInternal(fmt.Sprintf("failed to compute intrinsic gas cost: %s", err)).Result() 233 | } 234 | 235 | if ethTxMsg.Data.GasLimit < gas { 236 | return sdk.ErrInternal( 237 | fmt.Sprintf("intrinsic gas too low; %d < %d", ethTxMsg.Data.GasLimit, gas), 238 | ).Result() 239 | } 240 | 241 | return sdk.Result{} 242 | } 243 | 244 | // validateAccount validates the account nonce and that the account has enough 245 | // funds to cover the tx cost. 246 | func validateAccount( 247 | ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, signer ethcmn.Address, 248 | ) sdk.Result { 249 | 250 | acc := ak.GetAccount(ctx, sdk.AccAddress(signer.Bytes())) 251 | 252 | // on InitChain make sure account number == 0 253 | if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 { 254 | return sdk.ErrInternal( 255 | fmt.Sprintf( 256 | "invalid account number for height zero; got %d, expected 0", acc.GetAccountNumber(), 257 | )).Result() 258 | } 259 | 260 | // Validate the transaction nonce is valid (equivalent to the sender account’s 261 | // current nonce). 262 | seq := acc.GetSequence() 263 | if ethTxMsg.Data.AccountNonce != seq { 264 | return sdk.ErrInvalidSequence( 265 | fmt.Sprintf("nonce too low; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq)).Result() 266 | } 267 | 268 | // validate sender has enough funds 269 | balance := acc.GetCoins().AmountOf(types.DenomDefault) 270 | if balance.BigInt().Cmp(ethTxMsg.Cost()) < 0 { 271 | return sdk.ErrInsufficientFunds( 272 | fmt.Sprintf("insufficient funds: %s < %s", balance, ethTxMsg.Cost()), 273 | ).Result() 274 | } 275 | 276 | return sdk.Result{} 277 | } 278 | 279 | // ensureSufficientMempoolFees verifies that enough fees have been provided by the 280 | // Ethereum transaction that meet the minimum threshold set by the block 281 | // proposer. 282 | // 283 | // NOTE: This should only be ran during a CheckTx mode. 284 | func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) sdk.Result { 285 | // fee = GP * GL 286 | fee := sdk.Coins{sdk.NewInt64Coin(types.DenomDefault, ethTxMsg.Fee().Int64())} 287 | 288 | // it is assumed that the minimum fees will only include the single valid denom 289 | if !ctx.MinimumFees().IsZero() && !fee.IsAllGTE(ctx.MinimumFees()) { 290 | // reject the transaction that does not meet the minimum fee 291 | return sdk.ErrInsufficientFee( 292 | fmt.Sprintf("insufficient fee, got: %q required: %q", fee, ctx.MinimumFees()), 293 | ).Result() 294 | } 295 | 296 | return sdk.Result{} 297 | } 298 | -------------------------------------------------------------------------------- /app/ante_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | 8 | ethcmn "github.com/ethereum/go-ethereum/common" 9 | "github.com/stretchr/testify/require" 10 | tmcrypto "github.com/tendermint/tendermint/crypto" 11 | 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | "github.com/cosmos/cosmos-sdk/x/auth" 14 | "github.com/cosmos/ethermint/types" 15 | evmtypes "github.com/cosmos/ethermint/x/evm/types" 16 | ) 17 | 18 | func requireValidTx( 19 | t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, sim bool, 20 | ) { 21 | 22 | _, result, abort := anteHandler(ctx, tx, sim) 23 | require.Equal(t, sdk.CodeOK, result.Code, result.Log) 24 | require.False(t, abort) 25 | require.True(t, result.IsOK()) 26 | } 27 | 28 | func requireInvalidTx( 29 | t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, 30 | tx sdk.Tx, sim bool, code sdk.CodeType, 31 | ) { 32 | 33 | newCtx, result, abort := anteHandler(ctx, tx, sim) 34 | require.True(t, abort) 35 | require.Equal(t, code, result.Code, fmt.Sprintf("invalid result: %v", result)) 36 | 37 | if code == sdk.CodeOutOfGas { 38 | stdTx, ok := tx.(auth.StdTx) 39 | require.True(t, ok, "tx must be in form auth.StdTx") 40 | 41 | // require GasWanted is set correctly 42 | require.Equal(t, stdTx.Fee.Gas, result.GasWanted, "'GasWanted' wanted not set correctly") 43 | require.True(t, result.GasUsed > result.GasWanted, "'GasUsed' not greater than GasWanted") 44 | 45 | // require that context is set correctly 46 | require.Equal(t, result.GasUsed, newCtx.GasMeter().GasConsumed(), "Context not updated correctly") 47 | } 48 | } 49 | 50 | func TestValidEthTx(t *testing.T) { 51 | input := newTestSetup() 52 | input.ctx = input.ctx.WithBlockHeight(1) 53 | 54 | addr1, priv1 := newTestAddrKey() 55 | addr2, _ := newTestAddrKey() 56 | 57 | acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 58 | acc1.SetCoins(newTestCoins()) 59 | input.accKeeper.SetAccount(input.ctx, acc1) 60 | 61 | acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2) 62 | acc2.SetCoins(newTestCoins()) 63 | input.accKeeper.SetAccount(input.ctx, acc2) 64 | 65 | // require a valid Ethereum tx to pass 66 | to := ethcmn.BytesToAddress(addr2.Bytes()) 67 | amt := big.NewInt(32) 68 | gas := big.NewInt(20) 69 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) 70 | 71 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 72 | requireValidTx(t, input.anteHandler, input.ctx, tx, false) 73 | } 74 | 75 | func TestValidTx(t *testing.T) { 76 | input := newTestSetup() 77 | input.ctx = input.ctx.WithBlockHeight(1) 78 | 79 | addr1, priv1 := newTestAddrKey() 80 | addr2, priv2 := newTestAddrKey() 81 | 82 | acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 83 | acc1.SetCoins(newTestCoins()) 84 | input.accKeeper.SetAccount(input.ctx, acc1) 85 | 86 | acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2) 87 | acc2.SetCoins(newTestCoins()) 88 | input.accKeeper.SetAccount(input.ctx, acc2) 89 | 90 | // require a valid SDK tx to pass 91 | fee := newTestStdFee() 92 | msg1 := newTestMsg(addr1, addr2) 93 | msgs := []sdk.Msg{msg1} 94 | 95 | privKeys := []tmcrypto.PrivKey{priv1, priv2} 96 | accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} 97 | accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()} 98 | 99 | tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) 100 | requireValidTx(t, input.anteHandler, input.ctx, tx, false) 101 | 102 | // require accounts to update 103 | acc1 = input.accKeeper.GetAccount(input.ctx, addr1) 104 | acc2 = input.accKeeper.GetAccount(input.ctx, addr2) 105 | require.Equal(t, accSeqs[0]+1, acc1.GetSequence()) 106 | require.Equal(t, accSeqs[1]+1, acc2.GetSequence()) 107 | } 108 | 109 | func TestSDKInvalidSigs(t *testing.T) { 110 | input := newTestSetup() 111 | input.ctx = input.ctx.WithBlockHeight(1) 112 | 113 | addr1, priv1 := newTestAddrKey() 114 | addr2, priv2 := newTestAddrKey() 115 | addr3, priv3 := newTestAddrKey() 116 | 117 | acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 118 | acc1.SetCoins(newTestCoins()) 119 | input.accKeeper.SetAccount(input.ctx, acc1) 120 | 121 | acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2) 122 | acc2.SetCoins(newTestCoins()) 123 | input.accKeeper.SetAccount(input.ctx, acc2) 124 | 125 | fee := newTestStdFee() 126 | msg1 := newTestMsg(addr1, addr2) 127 | 128 | // require validation failure with no signers 129 | msgs := []sdk.Msg{msg1} 130 | 131 | privKeys := []tmcrypto.PrivKey{} 132 | accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} 133 | accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()} 134 | 135 | tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) 136 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) 137 | 138 | // require validation failure with invalid number of signers 139 | msgs = []sdk.Msg{msg1} 140 | 141 | privKeys = []tmcrypto.PrivKey{priv1} 142 | accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} 143 | accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence()} 144 | 145 | tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) 146 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) 147 | 148 | // require validation failure with an invalid signer 149 | msg2 := newTestMsg(addr1, addr3) 150 | msgs = []sdk.Msg{msg1, msg2} 151 | 152 | privKeys = []tmcrypto.PrivKey{priv1, priv2, priv3} 153 | accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber(), 0} 154 | accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence(), 0} 155 | 156 | tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) 157 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnknownAddress) 158 | } 159 | 160 | func TestSDKInvalidAcc(t *testing.T) { 161 | input := newTestSetup() 162 | input.ctx = input.ctx.WithBlockHeight(1) 163 | 164 | addr1, priv1 := newTestAddrKey() 165 | 166 | acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 167 | acc1.SetCoins(newTestCoins()) 168 | input.accKeeper.SetAccount(input.ctx, acc1) 169 | 170 | fee := newTestStdFee() 171 | msg1 := newTestMsg(addr1) 172 | msgs := []sdk.Msg{msg1} 173 | privKeys := []tmcrypto.PrivKey{priv1} 174 | 175 | // require validation failure with invalid account number 176 | accNums := []uint64{1} 177 | accSeqs := []uint64{acc1.GetSequence()} 178 | 179 | tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) 180 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) 181 | 182 | // require validation failure with invalid sequence (nonce) 183 | accNums = []uint64{acc1.GetAccountNumber()} 184 | accSeqs = []uint64{1} 185 | 186 | tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) 187 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) 188 | } 189 | 190 | func TestEthInvalidSig(t *testing.T) { 191 | input := newTestSetup() 192 | input.ctx = input.ctx.WithBlockHeight(1) 193 | 194 | _, priv1 := newTestAddrKey() 195 | addr2, _ := newTestAddrKey() 196 | to := ethcmn.BytesToAddress(addr2.Bytes()) 197 | amt := big.NewInt(32) 198 | gas := big.NewInt(20) 199 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) 200 | 201 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 202 | ctx := input.ctx.WithChainID("4") 203 | requireInvalidTx(t, input.anteHandler, ctx, tx, false, sdk.CodeUnauthorized) 204 | } 205 | 206 | func TestEthInvalidNonce(t *testing.T) { 207 | input := newTestSetup() 208 | input.ctx = input.ctx.WithBlockHeight(1) 209 | 210 | addr1, priv1 := newTestAddrKey() 211 | addr2, _ := newTestAddrKey() 212 | 213 | acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 214 | acc.SetCoins(newTestCoins()) 215 | acc.SetSequence(10) 216 | input.accKeeper.SetAccount(input.ctx, acc) 217 | 218 | // require a valid Ethereum tx to pass 219 | to := ethcmn.BytesToAddress(addr2.Bytes()) 220 | amt := big.NewInt(32) 221 | gas := big.NewInt(20) 222 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) 223 | 224 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 225 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInvalidSequence) 226 | } 227 | 228 | func TestEthInsufficientBalance(t *testing.T) { 229 | input := newTestSetup() 230 | input.ctx = input.ctx.WithBlockHeight(1) 231 | 232 | addr1, priv1 := newTestAddrKey() 233 | addr2, _ := newTestAddrKey() 234 | 235 | acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 236 | input.accKeeper.SetAccount(input.ctx, acc) 237 | 238 | // require a valid Ethereum tx to pass 239 | to := ethcmn.BytesToAddress(addr2.Bytes()) 240 | amt := big.NewInt(32) 241 | gas := big.NewInt(20) 242 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) 243 | 244 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 245 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInsufficientFunds) 246 | } 247 | 248 | func TestEthInvalidIntrinsicGas(t *testing.T) { 249 | input := newTestSetup() 250 | input.ctx = input.ctx.WithBlockHeight(1) 251 | 252 | addr1, priv1 := newTestAddrKey() 253 | addr2, _ := newTestAddrKey() 254 | 255 | acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 256 | acc.SetCoins(newTestCoins()) 257 | input.accKeeper.SetAccount(input.ctx, acc) 258 | 259 | // require a valid Ethereum tx to pass 260 | to := ethcmn.BytesToAddress(addr2.Bytes()) 261 | amt := big.NewInt(32) 262 | gas := big.NewInt(20) 263 | gasLimit := uint64(1000) 264 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, gasLimit, gas, []byte("test")) 265 | 266 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 267 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInternal) 268 | } 269 | 270 | func TestEthInvalidMempoolFees(t *testing.T) { 271 | input := newTestSetup() 272 | input.ctx = input.ctx.WithBlockHeight(1) 273 | input.ctx = input.ctx.WithMinimumFees(sdk.Coins{sdk.NewInt64Coin(types.DenomDefault, 500000)}) 274 | 275 | addr1, priv1 := newTestAddrKey() 276 | addr2, _ := newTestAddrKey() 277 | 278 | acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 279 | acc.SetCoins(newTestCoins()) 280 | input.accKeeper.SetAccount(input.ctx, acc) 281 | 282 | // require a valid Ethereum tx to pass 283 | to := ethcmn.BytesToAddress(addr2.Bytes()) 284 | amt := big.NewInt(32) 285 | gas := big.NewInt(20) 286 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) 287 | 288 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 289 | requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInsufficientFee) 290 | } 291 | 292 | func TestEthInvalidChainID(t *testing.T) { 293 | input := newTestSetup() 294 | input.ctx = input.ctx.WithBlockHeight(1) 295 | 296 | addr1, priv1 := newTestAddrKey() 297 | addr2, _ := newTestAddrKey() 298 | 299 | acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) 300 | acc.SetCoins(newTestCoins()) 301 | input.accKeeper.SetAccount(input.ctx, acc) 302 | 303 | // require a valid Ethereum tx to pass 304 | to := ethcmn.BytesToAddress(addr2.Bytes()) 305 | amt := big.NewInt(32) 306 | gas := big.NewInt(20) 307 | ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) 308 | 309 | tx := newTestEthTx(input.ctx, ethMsg, priv1) 310 | ctx := input.ctx.WithChainID("bad-chain-id") 311 | requireInvalidTx(t, input.anteHandler, ctx, tx, false, types.CodeInvalidChainID) 312 | } 313 | -------------------------------------------------------------------------------- /app/ethermint.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | bam "github.com/cosmos/cosmos-sdk/baseapp" 5 | "github.com/cosmos/cosmos-sdk/codec" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/x/auth" 8 | "github.com/cosmos/cosmos-sdk/x/bank" 9 | "github.com/cosmos/cosmos-sdk/x/gov" 10 | "github.com/cosmos/cosmos-sdk/x/params" 11 | "github.com/cosmos/cosmos-sdk/x/slashing" 12 | "github.com/cosmos/cosmos-sdk/x/stake" 13 | 14 | "github.com/cosmos/ethermint/crypto" 15 | evmtypes "github.com/cosmos/ethermint/x/evm/types" 16 | 17 | "github.com/pkg/errors" 18 | 19 | abci "github.com/tendermint/tendermint/abci/types" 20 | tmcmn "github.com/tendermint/tendermint/libs/common" 21 | dbm "github.com/tendermint/tendermint/libs/db" 22 | tmlog "github.com/tendermint/tendermint/libs/log" 23 | ) 24 | 25 | const appName = "Ethermint" 26 | 27 | // application multi-store keys 28 | var ( 29 | storeKeyAccount = sdk.NewKVStoreKey("acc") 30 | storeKeyStorage = sdk.NewKVStoreKey("contract_storage") 31 | storeKeyMain = sdk.NewKVStoreKey("main") 32 | storeKeyStake = sdk.NewKVStoreKey("stake") 33 | storeKeySlashing = sdk.NewKVStoreKey("slashing") 34 | storeKeyGov = sdk.NewKVStoreKey("gov") 35 | storeKeyFeeColl = sdk.NewKVStoreKey("fee") 36 | storeKeyParams = sdk.NewKVStoreKey("params") 37 | storeKeyTransParams = sdk.NewTransientStoreKey("transient_params") 38 | ) 39 | 40 | type ( 41 | // EthermintApp implements an extended ABCI application. It is an application 42 | // that may process transactions through Ethereum's EVM running atop of 43 | // Tendermint consensus. 44 | EthermintApp struct { 45 | *bam.BaseApp 46 | 47 | cdc *codec.Codec 48 | 49 | accountKey *sdk.KVStoreKey 50 | storageKey *sdk.KVStoreKey 51 | mainKey *sdk.KVStoreKey 52 | stakeKey *sdk.KVStoreKey 53 | slashingKey *sdk.KVStoreKey 54 | govKey *sdk.KVStoreKey 55 | feeCollKey *sdk.KVStoreKey 56 | paramsKey *sdk.KVStoreKey 57 | tParamsKey *sdk.TransientStoreKey 58 | 59 | accountKeeper auth.AccountKeeper 60 | feeCollKeeper auth.FeeCollectionKeeper 61 | coinKeeper bank.Keeper 62 | stakeKeeper stake.Keeper 63 | slashingKeeper slashing.Keeper 64 | govKeeper gov.Keeper 65 | paramsKeeper params.Keeper 66 | } 67 | ) 68 | 69 | // NewEthermintApp returns a reference to a new initialized Ethermint 70 | // application. 71 | // 72 | // TODO: Ethermint needs to support being bootstrapped as an application running 73 | // in a sovereign zone and as an application running with a shared security model. 74 | // For now, it will support only running as a sovereign application. 75 | func NewEthermintApp(logger tmlog.Logger, db dbm.DB, baseAppOpts ...func(*bam.BaseApp)) *EthermintApp { 76 | cdc := CreateCodec() 77 | 78 | baseApp := bam.NewBaseApp(appName, logger, db, evmtypes.TxDecoder(cdc), baseAppOpts...) 79 | app := &EthermintApp{ 80 | BaseApp: baseApp, 81 | cdc: cdc, 82 | accountKey: storeKeyAccount, 83 | storageKey: storeKeyStorage, 84 | mainKey: storeKeyMain, 85 | stakeKey: storeKeyStake, 86 | slashingKey: storeKeySlashing, 87 | govKey: storeKeyGov, 88 | feeCollKey: storeKeyFeeColl, 89 | paramsKey: storeKeyParams, 90 | tParamsKey: storeKeyTransParams, 91 | } 92 | 93 | app.paramsKeeper = params.NewKeeper(app.cdc, app.paramsKey, app.tParamsKey) 94 | app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.accountKey, auth.ProtoBaseAccount) 95 | app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.feeCollKey) 96 | 97 | // register message handlers 98 | app.Router(). 99 | // TODO: add remaining routes 100 | AddRoute("stake", stake.NewHandler(app.stakeKeeper)). 101 | AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). 102 | AddRoute("gov", gov.NewHandler(app.govKeeper)) 103 | 104 | // initialize the underlying ABCI BaseApp 105 | app.SetInitChainer(app.initChainer) 106 | app.SetBeginBlocker(app.BeginBlocker) 107 | app.SetEndBlocker(app.EndBlocker) 108 | app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.feeCollKeeper)) 109 | 110 | app.MountStores( 111 | app.mainKey, app.accountKey, app.stakeKey, app.slashingKey, 112 | app.govKey, app.feeCollKey, app.paramsKey, app.storageKey, 113 | ) 114 | app.MountStore(app.tParamsKey, sdk.StoreTypeTransient) 115 | 116 | if err := app.LoadLatestVersion(app.accountKey); err != nil { 117 | tmcmn.Exit(err.Error()) 118 | } 119 | 120 | app.BaseApp.Seal() 121 | return app 122 | } 123 | 124 | // BeginBlocker signals the beginning of a block. It performs application 125 | // updates on the start of every block. 126 | func (app *EthermintApp) BeginBlocker( 127 | _ sdk.Context, _ abci.RequestBeginBlock, 128 | ) abci.ResponseBeginBlock { 129 | 130 | return abci.ResponseBeginBlock{} 131 | } 132 | 133 | // EndBlocker signals the end of a block. It performs application updates on 134 | // the end of every block. 135 | func (app *EthermintApp) EndBlocker( 136 | _ sdk.Context, _ abci.RequestEndBlock, 137 | ) abci.ResponseEndBlock { 138 | 139 | return abci.ResponseEndBlock{} 140 | } 141 | 142 | // initChainer initializes the application blockchain with validators and other 143 | // state data from TendermintCore. 144 | func (app *EthermintApp) initChainer( 145 | _ sdk.Context, req abci.RequestInitChain, 146 | ) abci.ResponseInitChain { 147 | 148 | var genesisState GenesisState 149 | stateJSON := req.AppStateBytes 150 | 151 | err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) 152 | if err != nil { 153 | panic(errors.Wrap(err, "failed to parse application genesis state")) 154 | } 155 | 156 | // TODO: load the genesis accounts 157 | 158 | return abci.ResponseInitChain{} 159 | } 160 | 161 | // CreateCodec creates a new amino wire codec and registers all the necessary 162 | // concrete types and interfaces needed for the application. 163 | func CreateCodec() *codec.Codec { 164 | cdc := codec.New() 165 | 166 | // TODO: Add remaining codec registrations: 167 | // bank, staking, distribution, slashing, and gov 168 | 169 | crypto.RegisterCodec(cdc) 170 | evmtypes.RegisterCodec(cdc) 171 | auth.RegisterCodec(cdc) 172 | sdk.RegisterCodec(cdc) 173 | codec.RegisterCrypto(cdc) 174 | 175 | return cdc 176 | } 177 | -------------------------------------------------------------------------------- /app/genesis.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/ethermint/types" 6 | ) 7 | 8 | type ( 9 | // GenesisState defines the application's genesis state. It contains all the 10 | // information required and accounts to initialize the blockchain. 11 | GenesisState struct { 12 | Accounts []GenesisAccount `json:"accounts"` 13 | } 14 | 15 | // GenesisAccount defines an account to be initialized in the genesis state. 16 | GenesisAccount struct { 17 | Address sdk.AccAddress `json:"address"` 18 | Coins sdk.Coins `json:"coins"` 19 | Code []byte `json:"code,omitempty"` 20 | Storage types.Storage `json:"storage,omitempty"` 21 | } 22 | ) 23 | -------------------------------------------------------------------------------- /app/test_utils.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "time" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | "github.com/cosmos/cosmos-sdk/store" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | "github.com/cosmos/cosmos-sdk/x/auth" 12 | 13 | "github.com/cosmos/ethermint/crypto" 14 | "github.com/cosmos/ethermint/types" 15 | evmtypes "github.com/cosmos/ethermint/x/evm/types" 16 | 17 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 18 | 19 | abci "github.com/tendermint/tendermint/abci/types" 20 | tmcrypto "github.com/tendermint/tendermint/crypto" 21 | dbm "github.com/tendermint/tendermint/libs/db" 22 | "github.com/tendermint/tendermint/libs/log" 23 | ) 24 | 25 | type testSetup struct { 26 | ctx sdk.Context 27 | cdc *codec.Codec 28 | accKeeper auth.AccountKeeper 29 | feeKeeper auth.FeeCollectionKeeper 30 | anteHandler sdk.AnteHandler 31 | } 32 | 33 | func newTestSetup() testSetup { 34 | db := dbm.NewMemDB() 35 | authCapKey := sdk.NewKVStoreKey("authCapKey") 36 | feeCapKey := sdk.NewKVStoreKey("feeCapKey") 37 | keyParams := sdk.NewKVStoreKey("params") 38 | tkeyParams := sdk.NewTransientStoreKey("transient_params") 39 | 40 | ms := store.NewCommitMultiStore(db) 41 | ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db) 42 | ms.MountStoreWithDB(feeCapKey, sdk.StoreTypeIAVL, db) 43 | ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) 44 | ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeIAVL, db) 45 | ms.LoadLatestVersion() 46 | 47 | cdc := CreateCodec() 48 | cdc.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil) 49 | 50 | accKeeper := auth.NewAccountKeeper(cdc, authCapKey, auth.ProtoBaseAccount) 51 | feeKeeper := auth.NewFeeCollectionKeeper(cdc, feeCapKey) 52 | anteHandler := NewAnteHandler(accKeeper, feeKeeper) 53 | 54 | ctx := sdk.NewContext( 55 | ms, 56 | abci.Header{ChainID: "3", Time: time.Now().UTC()}, 57 | true, 58 | log.NewNopLogger(), 59 | ) 60 | 61 | return testSetup{ 62 | ctx: ctx, 63 | cdc: cdc, 64 | accKeeper: accKeeper, 65 | feeKeeper: feeKeeper, 66 | anteHandler: anteHandler, 67 | } 68 | } 69 | 70 | func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg { 71 | return sdk.NewTestMsg(addrs...) 72 | } 73 | 74 | func newTestCoins() sdk.Coins { 75 | return sdk.Coins{sdk.NewInt64Coin(types.DenomDefault, 500000000)} 76 | } 77 | 78 | func newTestStdFee() auth.StdFee { 79 | return auth.NewStdFee(220000, sdk.NewInt64Coin(types.DenomDefault, 150)) 80 | } 81 | 82 | // GenerateAddress generates an Ethereum address. 83 | func newTestAddrKey() (sdk.AccAddress, tmcrypto.PrivKey) { 84 | privkey, _ := crypto.GenerateKey() 85 | addr := ethcrypto.PubkeyToAddress(privkey.PublicKey) 86 | 87 | return sdk.AccAddress(addr.Bytes()), privkey 88 | } 89 | 90 | func newTestSDKTx( 91 | ctx sdk.Context, msgs []sdk.Msg, privs []tmcrypto.PrivKey, 92 | accNums []uint64, seqs []uint64, fee auth.StdFee, 93 | ) sdk.Tx { 94 | 95 | sigs := make([]auth.StdSignature, len(privs)) 96 | for i, priv := range privs { 97 | signBytes := auth.StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") 98 | 99 | sig, err := priv.Sign(signBytes) 100 | if err != nil { 101 | panic(err) 102 | } 103 | 104 | sigs[i] = auth.StdSignature{ 105 | PubKey: priv.PubKey(), 106 | Signature: sig, 107 | } 108 | } 109 | 110 | return auth.NewStdTx(msgs, fee, sigs, "") 111 | } 112 | 113 | func newTestEthTx(ctx sdk.Context, msg *evmtypes.EthereumTxMsg, priv tmcrypto.PrivKey) sdk.Tx { 114 | chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) 115 | if !ok { 116 | panic(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())) 117 | } 118 | 119 | privkey, ok := priv.(crypto.PrivKeySecp256k1) 120 | if !ok { 121 | panic(fmt.Sprintf("invalid private key type: %T", priv)) 122 | } 123 | 124 | msg.Sign(chainID, privkey.ToECDSA()) 125 | return msg 126 | } 127 | -------------------------------------------------------------------------------- /cmd/emintcli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // TODO: Implement CLI commands and logic 5 | // 6 | // Ref: https://github.com/cosmos/ethermint/issues/432 7 | } 8 | -------------------------------------------------------------------------------- /cmd/emintd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // TODO: Implement daemon command and logic 5 | // 6 | // Ref: https://github.com/cosmos/ethermint/issues/433 7 | } 8 | -------------------------------------------------------------------------------- /core/chain.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // TODO: This functionality and implementation may be deprecated 4 | 5 | import ( 6 | "math/big" 7 | 8 | ethcmn "github.com/ethereum/go-ethereum/common" 9 | ethcons "github.com/ethereum/go-ethereum/consensus" 10 | ethstate "github.com/ethereum/go-ethereum/core/state" 11 | ethtypes "github.com/ethereum/go-ethereum/core/types" 12 | ethrpc "github.com/ethereum/go-ethereum/rpc" 13 | ) 14 | 15 | // ChainContext implements Ethereum's core.ChainContext and consensus.Engine 16 | // interfaces. It is needed in order to apply and process Ethereum 17 | // transactions. There should only be a single implementation in Ethermint. For 18 | // the purposes of Ethermint, it should be support retrieving headers and 19 | // consensus parameters from the current blockchain to be used during 20 | // transaction processing. 21 | // 22 | // NOTE: Ethermint will distribute the fees out to validators, so the structure 23 | // and functionality of this is a WIP and subject to change. 24 | type ChainContext struct { 25 | Coinbase ethcmn.Address 26 | headersByNumber map[uint64]*ethtypes.Header 27 | } 28 | 29 | func NewChainContext() *ChainContext { 30 | return &ChainContext{ 31 | headersByNumber: make(map[uint64]*ethtypes.Header), 32 | } 33 | } 34 | 35 | // Engine implements Ethereum's core.ChainContext interface. As a ChainContext 36 | // implements the consensus.Engine interface, it is simply returned. 37 | func (cc *ChainContext) Engine() ethcons.Engine { 38 | return cc 39 | } 40 | 41 | // SetHeader implements Ethereum's core.ChainContext interface. It sets the 42 | // header for the given block number. 43 | func (cc *ChainContext) SetHeader(number uint64, header *ethtypes.Header) { 44 | cc.headersByNumber[number] = header 45 | } 46 | 47 | // GetHeader implements Ethereum's core.ChainContext interface. 48 | // 49 | // TODO: The Cosmos SDK supports retreiving such information in contexts and 50 | // multi-store, so this will be need to be integrated. 51 | func (cc *ChainContext) GetHeader(_ ethcmn.Hash, number uint64) *ethtypes.Header { 52 | if header, ok := cc.headersByNumber[number]; ok { 53 | return header 54 | } 55 | 56 | return nil 57 | } 58 | 59 | // Author implements Ethereum's consensus.Engine interface. It is responsible 60 | // for returned the address of the validtor to receive any fees. This function 61 | // is only invoked if the given author in the ApplyTransaction call is nil. 62 | // 63 | // NOTE: Ethermint will distribute the fees out to validators, so the structure 64 | // and functionality of this is a WIP and subject to change. 65 | func (cc *ChainContext) Author(_ *ethtypes.Header) (ethcmn.Address, error) { 66 | return cc.Coinbase, nil 67 | } 68 | 69 | // APIs implements Ethereum's consensus.Engine interface. It currently performs 70 | // a no-op. 71 | // 72 | // TODO: Do we need to support such RPC APIs? This will tie into a bigger 73 | // discussion on if we want to support web3. 74 | func (cc *ChainContext) APIs(_ ethcons.ChainReader) []ethrpc.API { 75 | return nil 76 | } 77 | 78 | // CalcDifficulty implements Ethereum's consensus.Engine interface. It currently 79 | // performs a no-op. 80 | func (cc *ChainContext) CalcDifficulty(_ ethcons.ChainReader, _ uint64, _ *ethtypes.Header) *big.Int { 81 | return nil 82 | } 83 | 84 | // Finalize implements Ethereum's consensus.Engine interface. It currently 85 | // performs a no-op. 86 | // 87 | // TODO: Figure out if this needs to be hooked up to any part of the ABCI? 88 | func (cc *ChainContext) Finalize( 89 | _ ethcons.ChainReader, _ *ethtypes.Header, _ ethstate.StateDB, 90 | _ []*ethtypes.Transaction, _ []*ethtypes.Header, _ []*ethtypes.Receipt, 91 | ) (*ethtypes.Block, error) { 92 | return nil, nil 93 | } 94 | 95 | // Prepare implements Ethereum's consensus.Engine interface. It currently 96 | // performs a no-op. 97 | // 98 | // TODO: Figure out if this needs to be hooked up to any part of the ABCI? 99 | func (cc *ChainContext) Prepare(_ ethcons.ChainReader, _ *ethtypes.Header) error { 100 | return nil 101 | } 102 | 103 | // Seal implements Ethereum's consensus.Engine interface. It currently 104 | // performs a no-op. 105 | // 106 | // TODO: Figure out if this needs to be hooked up to any part of the ABCI? 107 | func (cc *ChainContext) Seal(_ ethcons.ChainReader, _ *ethtypes.Block, _ chan<- *ethtypes.Block, _ <-chan struct{}) error { 108 | return nil 109 | } 110 | 111 | // SealHash implements Ethereum's consensus.Engine interface. It returns the 112 | // hash of a block prior to it being sealed. 113 | func (cc *ChainContext) SealHash(header *ethtypes.Header) ethcmn.Hash { 114 | return ethcmn.Hash{} 115 | } 116 | 117 | // VerifyHeader implements Ethereum's consensus.Engine interface. It currently 118 | // performs a no-op. 119 | // 120 | // TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK 121 | // handlers? 122 | func (cc *ChainContext) VerifyHeader(_ ethcons.ChainReader, _ *ethtypes.Header, _ bool) error { 123 | return nil 124 | } 125 | 126 | // VerifyHeaders implements Ethereum's consensus.Engine interface. It 127 | // currently performs a no-op. 128 | // 129 | // TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK 130 | // handlers? 131 | func (cc *ChainContext) VerifyHeaders(_ ethcons.ChainReader, _ []*ethtypes.Header, _ []bool) (chan<- struct{}, <-chan error) { 132 | return nil, nil 133 | } 134 | 135 | // VerifySeal implements Ethereum's consensus.Engine interface. It currently 136 | // performs a no-op. 137 | // 138 | // TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK 139 | // handlers? 140 | func (cc *ChainContext) VerifySeal(_ ethcons.ChainReader, _ *ethtypes.Header) error { 141 | return nil 142 | } 143 | 144 | // VerifyUncles implements Ethereum's consensus.Engine interface. It currently 145 | // performs a no-op. 146 | func (cc *ChainContext) VerifyUncles(_ ethcons.ChainReader, _ *ethtypes.Block) error { 147 | return nil 148 | } 149 | 150 | // Close implements Ethereum's consensus.Engine interface. It terminates any 151 | // background threads maintained by the consensus engine. It currently performs 152 | // a no-op. 153 | func (cc *ChainContext) Close() error { 154 | return nil 155 | } 156 | -------------------------------------------------------------------------------- /core/chain_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // NOTE: A bulk of these unit tests will change and evolve as the context and 4 | // implementation of ChainConext evolves. 5 | 6 | import ( 7 | "math/big" 8 | "testing" 9 | 10 | ethcmn "github.com/ethereum/go-ethereum/common" 11 | ethcons "github.com/ethereum/go-ethereum/consensus" 12 | ethcore "github.com/ethereum/go-ethereum/core" 13 | ethtypes "github.com/ethereum/go-ethereum/core/types" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestChainContextInterface(t *testing.T) { 18 | require.Implements(t, (*ethcore.ChainContext)(nil), new(ChainContext)) 19 | require.Implements(t, (*ethcons.Engine)(nil), new(ChainContext)) 20 | } 21 | 22 | func TestNewChainContext(t *testing.T) { 23 | cc := NewChainContext() 24 | require.NotNil(t, cc.headersByNumber) 25 | } 26 | 27 | func TestChainContextEngine(t *testing.T) { 28 | cc := NewChainContext() 29 | require.Equal(t, cc, cc.Engine()) 30 | } 31 | 32 | func TestChainContextSetHeader(t *testing.T) { 33 | cc := NewChainContext() 34 | header := ðtypes.Header{ 35 | Number: big.NewInt(64), 36 | } 37 | 38 | cc.SetHeader(uint64(header.Number.Int64()), header) 39 | require.Equal(t, header, cc.headersByNumber[uint64(header.Number.Int64())]) 40 | } 41 | 42 | func TestChainContextGetHeader(t *testing.T) { 43 | cc := NewChainContext() 44 | header := ðtypes.Header{ 45 | Number: big.NewInt(64), 46 | } 47 | 48 | cc.SetHeader(uint64(header.Number.Int64()), header) 49 | require.Equal(t, header, cc.GetHeader(ethcmn.Hash{}, uint64(header.Number.Int64()))) 50 | require.Nil(t, cc.GetHeader(ethcmn.Hash{}, 0)) 51 | } 52 | 53 | func TestChainContextAuthor(t *testing.T) { 54 | cc := NewChainContext() 55 | 56 | cb, err := cc.Author(nil) 57 | require.Nil(t, err) 58 | require.Equal(t, cc.Coinbase, cb) 59 | } 60 | 61 | func TestChainContextAPIs(t *testing.T) { 62 | cc := NewChainContext() 63 | require.Nil(t, cc.APIs(nil)) 64 | } 65 | 66 | func TestChainContextCalcDifficulty(t *testing.T) { 67 | cc := NewChainContext() 68 | require.Nil(t, cc.CalcDifficulty(nil, 0, nil)) 69 | } 70 | 71 | func TestChainContextFinalize(t *testing.T) { 72 | cc := NewChainContext() 73 | 74 | block, err := cc.Finalize(nil, nil, nil, nil, nil, nil) 75 | require.Nil(t, err) 76 | require.Nil(t, block) 77 | } 78 | 79 | func TestChainContextPrepare(t *testing.T) { 80 | cc := NewChainContext() 81 | 82 | err := cc.Prepare(nil, nil) 83 | require.Nil(t, err) 84 | } 85 | 86 | func TestChainContextSeal(t *testing.T) { 87 | cc := NewChainContext() 88 | 89 | err := cc.Seal(nil, nil, nil, nil) 90 | require.Nil(t, err) 91 | } 92 | 93 | func TestChainContextVerifyHeader(t *testing.T) { 94 | cc := NewChainContext() 95 | 96 | err := cc.VerifyHeader(nil, nil, false) 97 | require.Nil(t, err) 98 | } 99 | 100 | func TestChainContextVerifyHeaders(t *testing.T) { 101 | cc := NewChainContext() 102 | 103 | ch, err := cc.VerifyHeaders(nil, nil, []bool{false}) 104 | require.Nil(t, err) 105 | require.Nil(t, ch) 106 | } 107 | 108 | func TestChainContextVerifySeal(t *testing.T) { 109 | cc := NewChainContext() 110 | 111 | err := cc.VerifySeal(nil, nil) 112 | require.Nil(t, err) 113 | } 114 | 115 | func TestChainContextVerifyUncles(t *testing.T) { 116 | cc := NewChainContext() 117 | 118 | err := cc.VerifyUncles(nil, nil) 119 | require.Nil(t, err) 120 | } 121 | -------------------------------------------------------------------------------- /crypto/codec.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/cosmos/cosmos-sdk/codec" 4 | 5 | var cryptoCodec = codec.New() 6 | 7 | func init() { 8 | RegisterCodec(cryptoCodec) 9 | } 10 | 11 | // RegisterCodec registers all the necessary types with amino for the given 12 | // codec. 13 | func RegisterCodec(cdc *codec.Codec) { 14 | cdc.RegisterConcrete(PubKeySecp256k1{}, "crypto/PubKeySecp256k1", nil) 15 | cdc.RegisterConcrete(PrivKeySecp256k1{}, "crypto/PrivKeySecp256k1", nil) 16 | } 17 | -------------------------------------------------------------------------------- /crypto/secp256k1.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "crypto/ecdsa" 6 | 7 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 8 | ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1" 9 | 10 | tmcrypto "github.com/tendermint/tendermint/crypto" 11 | ) 12 | 13 | // ---------------------------------------------------------------------------- 14 | // secp256k1 Private Key 15 | 16 | var _ tmcrypto.PrivKey = PrivKeySecp256k1{} 17 | 18 | // PrivKeySecp256k1 defines a type alias for an ecdsa.PrivateKey that implements 19 | // Tendermint's PrivateKey interface. 20 | type PrivKeySecp256k1 ecdsa.PrivateKey 21 | 22 | // GenerateKey generates a new random private key. It returns an error upon 23 | // failure. 24 | func GenerateKey() (PrivKeySecp256k1, error) { 25 | priv, err := ethcrypto.GenerateKey() 26 | if err != nil { 27 | return PrivKeySecp256k1{}, err 28 | } 29 | 30 | return PrivKeySecp256k1(*priv), nil 31 | } 32 | 33 | // PubKey returns the ECDSA private key's public key. 34 | func (privkey PrivKeySecp256k1) PubKey() tmcrypto.PubKey { 35 | return PubKeySecp256k1{privkey.PublicKey} 36 | } 37 | 38 | // Bytes returns the raw ECDSA private key bytes. 39 | func (privkey PrivKeySecp256k1) Bytes() []byte { 40 | return ethcrypto.FromECDSA(privkey.ToECDSA()) 41 | } 42 | 43 | // Sign creates a recoverable ECDSA signature on the secp256k1 curve over the 44 | // Keccak256 hash of the provided message. The produced signature is 65 bytes 45 | // where the last byte contains the recovery ID. 46 | func (privkey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) { 47 | return ethcrypto.Sign(ethcrypto.Keccak256Hash(msg).Bytes(), privkey.ToECDSA()) 48 | } 49 | 50 | // Equals returns true if two ECDSA private keys are equal and false otherwise. 51 | func (privkey PrivKeySecp256k1) Equals(other tmcrypto.PrivKey) bool { 52 | if other, ok := other.(PrivKeySecp256k1); ok { 53 | return bytes.Equal(privkey.Bytes(), other.Bytes()) 54 | } 55 | 56 | return false 57 | } 58 | 59 | // ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type. 60 | func (privkey PrivKeySecp256k1) ToECDSA() *ecdsa.PrivateKey { 61 | return (*ecdsa.PrivateKey)(&privkey) 62 | } 63 | 64 | // ---------------------------------------------------------------------------- 65 | // secp256k1 Public Key 66 | 67 | var _ tmcrypto.PubKey = (*PubKeySecp256k1)(nil) 68 | 69 | // PubKeySecp256k1 defines a type alias for an ecdsa.PublicKey that implements 70 | // Tendermint's PubKey interface. 71 | type PubKeySecp256k1 struct { 72 | pubkey ecdsa.PublicKey 73 | } 74 | 75 | // Address returns the address of the ECDSA public key. 76 | func (key PubKeySecp256k1) Address() tmcrypto.Address { 77 | return tmcrypto.Address(ethcrypto.PubkeyToAddress(key.pubkey).Bytes()) 78 | } 79 | 80 | // Bytes returns the raw bytes of the ECDSA public key. 81 | func (key PubKeySecp256k1) Bytes() []byte { 82 | return ethcrypto.FromECDSAPub(&key.pubkey) 83 | } 84 | 85 | // VerifyBytes verifies that the ECDSA public key created a given signature over 86 | // the provided message. It will calculate the Keccak256 hash of the message 87 | // prior to verification. 88 | func (key PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool { 89 | if len(sig) == 65 { 90 | // remove recovery ID if contained in the signature 91 | sig = sig[:len(sig)-1] 92 | } 93 | 94 | // the signature needs to be in [R || S] format when provided to VerifySignature 95 | return ethsecp256k1.VerifySignature(key.Bytes(), ethcrypto.Keccak256Hash(msg).Bytes(), sig) 96 | } 97 | 98 | // Equals returns true if two ECDSA public keys are equal and false otherwise. 99 | func (key PubKeySecp256k1) Equals(other tmcrypto.PubKey) bool { 100 | if other, ok := other.(PubKeySecp256k1); ok { 101 | return bytes.Equal(key.Bytes(), other.Bytes()) 102 | } 103 | 104 | return false 105 | } 106 | -------------------------------------------------------------------------------- /crypto/secp256k1_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 8 | ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1" 9 | "github.com/stretchr/testify/require" 10 | tmcrypto "github.com/tendermint/tendermint/crypto" 11 | ) 12 | 13 | func TestPrivKeySecp256k1PrivKey(t *testing.T) { 14 | // validate type and equality 15 | privKey, err := GenerateKey() 16 | require.NoError(t, err) 17 | require.True(t, privKey.Equals(privKey)) 18 | require.Implements(t, (*tmcrypto.PrivKey)(nil), privKey) 19 | 20 | // validate inequality 21 | privKey2, err := GenerateKey() 22 | require.NoError(t, err) 23 | require.False(t, privKey.Equals(privKey2)) 24 | 25 | // validate Ethereum address equality 26 | addr := privKey.PubKey().Address() 27 | expectedAddr := ethcrypto.PubkeyToAddress(privKey.PublicKey) 28 | require.Equal(t, expectedAddr.Bytes(), addr.Bytes()) 29 | 30 | // validate we can sign some bytes 31 | msg := []byte("hello world") 32 | sigHash := ethcrypto.Keccak256Hash(msg) 33 | expectedSig, _ := ethsecp256k1.Sign(sigHash.Bytes(), privKey.Bytes()) 34 | 35 | sig, err := privKey.Sign(msg) 36 | require.NoError(t, err) 37 | require.Equal(t, expectedSig, sig) 38 | } 39 | 40 | func TestPrivKeySecp256k1PubKey(t *testing.T) { 41 | privKey, err := GenerateKey() 42 | require.NoError(t, err) 43 | 44 | // validate type and equality 45 | pubKey := privKey.PubKey().(PubKeySecp256k1) 46 | require.Implements(t, (*tmcrypto.PubKey)(nil), pubKey) 47 | 48 | // validate inequality 49 | privKey2, err := GenerateKey() 50 | require.NoError(t, err) 51 | require.False(t, pubKey.Equals(privKey2.PubKey())) 52 | 53 | // validate signature 54 | msg := []byte("hello world") 55 | sig, err := privKey.Sign(msg) 56 | require.NoError(t, err) 57 | 58 | fmt.Println("SIG LENGTH:", len(sig)) 59 | res := pubKey.VerifyBytes(msg, sig) 60 | require.True(t, res) 61 | } 62 | -------------------------------------------------------------------------------- /docs/intro/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## What is Ethermint 4 | 5 | Ethermint is a high throughput PoS blockchain that is fully compatible and 6 | interoperable with Ethereum. In other words, it allows for running vanilla Ethereum 7 | on top of [Tendermint](https://github.com/tendermint/tendermint) consensus via 8 | the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/). This allows developers 9 | to have all the desired features of Ethereum, while at the same time benefit 10 | from Tendermint’s PoS implementation. Also, because it is built on top of the 11 | Cosmos SDK, it will be able to exchange value with the rest of the Cosmos Ecosystem. 12 | 13 | Here’s a glance at some of the key features of Ethermint: 14 | 15 | * Web3 compatibility 16 | * High throughput 17 | * Horizontal scalability 18 | * Transaction finality 19 | 20 | Ethermint enables these key features through: 21 | 22 | * Implementing Tendermint's ABCI application interface to manage the base Blockchain 23 | * Leveraging [modules](https://github.com/cosmos/cosmos-sdk/tree/master/x/) and other mechanisms implemented by the Cosmos SDK 24 | * Utilizing [`geth`](https://github.com/ethereum/go-ethereum) as a library to avoid code reuse and improve maintainability 25 | * Exposing a fully compatible Web3 RPC layer for interacting with the system 26 | 27 | The sum of these features allows developers to leverage existing Ethereum ecosystem 28 | tooling and software to seamlessly deploy smart contracts which interact with the rest of the Cosmos 29 | ecosystem! 30 | 31 | ## In-depth Topics 32 | 33 | ### Tendermint Core & the Application Blockchain Interface (ABCI) 34 | 35 | Tendermint consists of two chief technical components: a blockchain consensus 36 | engine and a generic application interface. The consensus engine, called 37 | Tendermint Core, ensures that the same transactions are recorded on every machine 38 | in the same order. The application interface, called the Application Blockchain 39 | Interface (ABCI), enables the transactions to be processed in any programming 40 | language. 41 | 42 | Tendermint has evolved to be a general purpose blockchain consensus engine that 43 | can host arbitrary application states. Since Tendermint can replicate arbitrary 44 | applications, it can be used as a plug-and-play replacement for the consensus 45 | engines of other blockchains. Ethermint is such an example of an ABCI application 46 | replacing Ethereum's PoW via Tendermint's consensus engine. 47 | 48 | Another example of a cryptocurrency application built on Tendermint is the Cosmos 49 | network. Tendermint is able to decompose the blockchain design by offering a very 50 | simple API (ie. the ABCI) between the application process and consensus process. 51 | -------------------------------------------------------------------------------- /docs/spec/evm/README.md: -------------------------------------------------------------------------------- 1 | # EVM 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /docs/spec/transactions/README.md: -------------------------------------------------------------------------------- 1 | # Transactions 2 | 3 | > NOTE: The specification documented below is still highly active in development 4 | and subject to change. 5 | 6 | ## Routing 7 | 8 | Ethermint needs to parse and handle transactions routed for both the EVM and for 9 | the Cosmos hub. We attempt to achieve this by mimicking 10 | [Geth's](https://github.com/ethereum/go-ethereum) `Transaction` structure and 11 | treat it as a unique Cosmos SDK message type. An Ethereum transaction is a single 12 | [`sdk.Msg`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg) contained 13 | in an [`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). 14 | All relevant Ethereum transaction information is contained in this message. This 15 | includes the signature, gas, payload, etc. 16 | 17 | Being that Ethermint implements the Tendermint ABCI application interface, as 18 | transactions are consumed, they are passed through a series of handlers. Once such 19 | handler, the `AnteHandler`, is responsible for performing preliminary message 20 | execution business logic such as fee payment, signature verification, etc. This is 21 | particular to Cosmos SDK routed transactions. Ethereum routed transactions will 22 | bypass this as the EVM handles the same business logic. 23 | 24 | Ethereum routed transactions coming from a web3 source are expected to be RLP 25 | encoded, however all internal interaction between Ethermint and Tendermint will 26 | utilize Amino encoding. 27 | 28 | __Note__: Our goal is to utilize Geth/Turbo-Geth as a library, at least as much 29 | as possible, so it should be expected that these types and the operations you may 30 | perform on them will keep in line with Ethereum (e.g. signature algorithms and 31 | gas/fees). In addition, we aim to have existing tooling and frameworks in the 32 | Ethereum ecosystem have 100% compatibility with creating transactions in Ethermint. 33 | 34 | ## Signatures 35 | 36 | Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) 37 | signatures. A `Transaction` is expected to have a single signature for Ethereum 38 | routed transactions. However, just as in Cosmos, Ethermint will support multiple 39 | signers for non-Ethereum transactions. Signatures over the 40 | `Transaction` type are identical to Ethereum and the signatures will not be duplicated 41 | in the embedding [`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). 42 | 43 | ## Gas & Fees 44 | 45 | TODO 46 | -------------------------------------------------------------------------------- /gometalinter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Linters": { 3 | "vet": "go tool vet -composites=false :PATH:LINE:MESSAGE" 4 | }, 5 | "Enable": [ 6 | "golint", 7 | "vet", 8 | "ineffassign", 9 | "unparam", 10 | "unconvert", 11 | "misspell" 12 | ], 13 | "Deadline": "500s", 14 | "Vendor": true, 15 | "Cyclo": 11 16 | } -------------------------------------------------------------------------------- /importer/blockchain: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cosmos/ethermint-archive/c073115b746bedbe7f9977cb175b3f0d94c5218c/importer/blockchain -------------------------------------------------------------------------------- /importer/importer_test.go: -------------------------------------------------------------------------------- 1 | package importer 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "math/big" 8 | "os" 9 | "os/signal" 10 | "runtime/pprof" 11 | "sort" 12 | "syscall" 13 | "testing" 14 | "time" 15 | 16 | "github.com/cosmos/cosmos-sdk/codec" 17 | "github.com/cosmos/cosmos-sdk/store" 18 | sdk "github.com/cosmos/cosmos-sdk/types" 19 | "github.com/cosmos/cosmos-sdk/x/auth" 20 | 21 | "github.com/cosmos/ethermint/core" 22 | "github.com/cosmos/ethermint/types" 23 | evmtypes "github.com/cosmos/ethermint/x/evm/types" 24 | 25 | ethcmn "github.com/ethereum/go-ethereum/common" 26 | "github.com/ethereum/go-ethereum/consensus/ethash" 27 | ethmisc "github.com/ethereum/go-ethereum/consensus/misc" 28 | ethcore "github.com/ethereum/go-ethereum/core" 29 | ethtypes "github.com/ethereum/go-ethereum/core/types" 30 | ethvm "github.com/ethereum/go-ethereum/core/vm" 31 | ethparams "github.com/ethereum/go-ethereum/params" 32 | ethrlp "github.com/ethereum/go-ethereum/rlp" 33 | 34 | "github.com/stretchr/testify/require" 35 | 36 | abci "github.com/tendermint/tendermint/abci/types" 37 | dbm "github.com/tendermint/tendermint/libs/db" 38 | tmlog "github.com/tendermint/tendermint/libs/log" 39 | ) 40 | 41 | var ( 42 | flagDataDir string 43 | flagBlockchain string 44 | flagCPUProfile string 45 | 46 | miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") 47 | genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0") 48 | 49 | paramsKey = sdk.NewKVStoreKey("params") 50 | tParamsKey = sdk.NewTransientStoreKey("transient_params") 51 | accKey = sdk.NewKVStoreKey("acc") 52 | storageKey = sdk.NewKVStoreKey("storage") 53 | codeKey = sdk.NewKVStoreKey("code") 54 | 55 | logger = tmlog.NewNopLogger() 56 | 57 | rewardBig8 = big.NewInt(8) 58 | rewardBig32 = big.NewInt(32) 59 | ) 60 | 61 | func init() { 62 | flag.StringVar(&flagCPUProfile, "cpu-profile", "", "write CPU profile") 63 | flag.StringVar(&flagDataDir, "datadir", "", "test data directory for state storage") 64 | flag.StringVar(&flagBlockchain, "blockchain", "blockchain", "ethereum block export file (blocks to import)") 65 | flag.Parse() 66 | } 67 | 68 | func newTestCodec() *codec.Codec { 69 | cdc := codec.New() 70 | 71 | evmtypes.RegisterCodec(cdc) 72 | types.RegisterCodec(cdc) 73 | auth.RegisterCodec(cdc) 74 | sdk.RegisterCodec(cdc) 75 | codec.RegisterCrypto(cdc) 76 | 77 | return cdc 78 | } 79 | 80 | func cleanup() { 81 | fmt.Println("cleaning up test execution...") 82 | os.RemoveAll(flagDataDir) 83 | 84 | if flagCPUProfile != "" { 85 | pprof.StopCPUProfile() 86 | } 87 | } 88 | 89 | func trapSignals() { 90 | sigs := make(chan os.Signal, 1) 91 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 92 | 93 | go func() { 94 | <-sigs 95 | cleanup() 96 | os.Exit(1) 97 | }() 98 | } 99 | 100 | func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.AccountKeeper) { 101 | genBlock := ethcore.DefaultGenesisBlock() 102 | ms := cms.CacheMultiStore() 103 | ctx := sdk.NewContext(ms, abci.Header{}, false, logger) 104 | 105 | stateDB, err := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey) 106 | require.NoError(t, err, "failed to create a StateDB instance") 107 | 108 | // sort the addresses and insertion of key/value pairs matters 109 | genAddrs := make([]string, len(genBlock.Alloc)) 110 | i := 0 111 | for addr := range genBlock.Alloc { 112 | genAddrs[i] = addr.String() 113 | i++ 114 | } 115 | 116 | sort.Strings(genAddrs) 117 | 118 | for _, addrStr := range genAddrs { 119 | addr := ethcmn.HexToAddress(addrStr) 120 | acc := genBlock.Alloc[addr] 121 | 122 | stateDB.AddBalance(addr, acc.Balance) 123 | stateDB.SetCode(addr, acc.Code) 124 | stateDB.SetNonce(addr, acc.Nonce) 125 | 126 | for key, value := range acc.Storage { 127 | stateDB.SetState(addr, key, value) 128 | } 129 | } 130 | 131 | // get balance of one of the genesis account having 200 ETH 132 | b := stateDB.GetBalance(genInvestor) 133 | require.Equal(t, "200000000000000000000", b.String()) 134 | 135 | // commit the stateDB with 'false' to delete empty objects 136 | // 137 | // NOTE: Commit does not yet return the intra merkle root (version) 138 | _, err = stateDB.Commit(false) 139 | require.NoError(t, err) 140 | 141 | // persist multi-store cache state 142 | ms.Write() 143 | 144 | // persist multi-store root state 145 | cms.Commit() 146 | 147 | // verify account mapper state 148 | genAcc := ak.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) 149 | require.NotNil(t, genAcc) 150 | require.Equal(t, sdk.NewIntFromBigInt(b), genAcc.GetCoins().AmountOf(types.DenomDefault)) 151 | } 152 | 153 | func TestImportBlocks(t *testing.T) { 154 | if flagDataDir == "" { 155 | flagDataDir = os.TempDir() 156 | } 157 | 158 | if flagCPUProfile != "" { 159 | f, err := os.Create(flagCPUProfile) 160 | require.NoError(t, err, "failed to create CPU profile") 161 | 162 | err = pprof.StartCPUProfile(f) 163 | require.NoError(t, err, "failed to start CPU profile") 164 | } 165 | 166 | db := dbm.NewDB("state", dbm.LevelDBBackend, flagDataDir) 167 | defer cleanup() 168 | trapSignals() 169 | 170 | // create logger, codec and root multi-store 171 | cdc := newTestCodec() 172 | cms := store.NewCommitMultiStore(db) 173 | ak := auth.NewAccountKeeper(cdc, accKey, types.ProtoBaseAccount) 174 | 175 | // mount stores 176 | keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey} 177 | for _, key := range keys { 178 | cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil) 179 | } 180 | 181 | cms.SetPruning(sdk.PruneNothing) 182 | 183 | // load latest version (root) 184 | err := cms.LoadLatestVersion() 185 | require.NoError(t, err) 186 | 187 | // set and test genesis block 188 | createAndTestGenesis(t, cms, ak) 189 | 190 | // open blockchain export file 191 | blockchainInput, err := os.Open(flagBlockchain) 192 | require.Nil(t, err) 193 | 194 | defer blockchainInput.Close() 195 | 196 | // ethereum mainnet config 197 | chainContext := core.NewChainContext() 198 | vmConfig := ethvm.Config{} 199 | chainConfig := ethparams.MainnetChainConfig 200 | 201 | // create RLP stream for exported blocks 202 | stream := ethrlp.NewStream(blockchainInput, 0) 203 | startTime := time.Now() 204 | 205 | var block ethtypes.Block 206 | for { 207 | err = stream.Decode(&block) 208 | if err == io.EOF { 209 | break 210 | } 211 | 212 | require.NoError(t, err, "failed to decode block") 213 | 214 | var ( 215 | usedGas = new(uint64) 216 | gp = new(ethcore.GasPool).AddGas(block.GasLimit()) 217 | ) 218 | 219 | header := block.Header() 220 | chainContext.Coinbase = header.Coinbase 221 | 222 | chainContext.SetHeader(block.NumberU64(), header) 223 | 224 | // Create a cached-wrapped multi-store based on the commit multi-store and 225 | // create a new context based off of that. 226 | ms := cms.CacheMultiStore() 227 | ctx := sdk.NewContext(ms, abci.Header{}, false, logger) 228 | ctx = ctx.WithBlockHeight(int64(block.NumberU64())) 229 | 230 | stateDB := createStateDB(t, ctx, ak) 231 | 232 | if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { 233 | ethmisc.ApplyDAOHardFork(stateDB) 234 | } 235 | 236 | for i, tx := range block.Transactions() { 237 | stateDB.Prepare(tx.Hash(), block.Hash(), i) 238 | 239 | _, _, err = ethcore.ApplyTransaction( 240 | chainConfig, chainContext, nil, gp, stateDB, header, tx, usedGas, vmConfig, 241 | ) 242 | require.NoError(t, err, "failed to apply tx at block %d; tx: %X", block.NumberU64(), tx.Hash()) 243 | } 244 | 245 | // apply mining rewards 246 | accumulateRewards(chainConfig, stateDB, header, block.Uncles()) 247 | 248 | // commit stateDB 249 | _, err := stateDB.Commit(chainConfig.IsEIP158(block.Number())) 250 | require.NoError(t, err, "failed to commit StateDB") 251 | 252 | // simulate BaseApp EndBlocker commitment 253 | ms.Write() 254 | cms.Commit() 255 | 256 | // block debugging output 257 | if block.NumberU64() > 0 && block.NumberU64()%1000 == 0 { 258 | fmt.Printf("processed block: %d (time so far: %v)\n", block.NumberU64(), time.Since(startTime)) 259 | } 260 | } 261 | } 262 | 263 | func createStateDB(t *testing.T, ctx sdk.Context, ak auth.AccountKeeper) *evmtypes.CommitStateDB { 264 | stateDB, err := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey) 265 | require.NoError(t, err, "failed to create a StateDB instance") 266 | 267 | return stateDB 268 | } 269 | 270 | // accumulateRewards credits the coinbase of the given block with the mining 271 | // reward. The total reward consists of the static block reward and rewards for 272 | // included uncles. The coinbase of each uncle block is also rewarded. 273 | func accumulateRewards( 274 | config *ethparams.ChainConfig, stateDB *evmtypes.CommitStateDB, 275 | header *ethtypes.Header, uncles []*ethtypes.Header, 276 | ) { 277 | 278 | // select the correct block reward based on chain progression 279 | blockReward := ethash.FrontierBlockReward 280 | if config.IsByzantium(header.Number) { 281 | blockReward = ethash.ByzantiumBlockReward 282 | } 283 | 284 | // accumulate the rewards for the miner and any included uncles 285 | reward := new(big.Int).Set(blockReward) 286 | r := new(big.Int) 287 | 288 | for _, uncle := range uncles { 289 | r.Add(uncle.Number, rewardBig8) 290 | r.Sub(r, header.Number) 291 | r.Mul(r, blockReward) 292 | r.Div(r, rewardBig8) 293 | stateDB.AddBalance(uncle.Coinbase, r) 294 | r.Div(blockReward, rewardBig32) 295 | reward.Add(reward, r) 296 | } 297 | 298 | stateDB.AddBalance(header.Coinbase, reward) 299 | } 300 | -------------------------------------------------------------------------------- /server/rpc/apis.go: -------------------------------------------------------------------------------- 1 | // Package rpc contains RPC handler methods and utilities to start 2 | // Ethermint's Web3-compatibly JSON-RPC server. 3 | package rpc 4 | 5 | import ( 6 | "github.com/cosmos/ethermint/version" 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/ethereum/go-ethereum/crypto" 9 | "github.com/ethereum/go-ethereum/rpc" 10 | ) 11 | 12 | // GetRPCAPIs returns the master list of public APIs for use with 13 | // StartHTTPEndpoint. 14 | func GetRPCAPIs() []rpc.API { 15 | return []rpc.API{ 16 | { 17 | Namespace: "web3", 18 | Version: "1.0", 19 | Service: NewPublicWeb3API(), 20 | }, 21 | { 22 | Namespace: "eth", 23 | Version: "1.0", 24 | Service: NewPublicEthAPI(), 25 | }, 26 | } 27 | } 28 | 29 | // PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec. 30 | type PublicWeb3API struct { 31 | } 32 | 33 | // NewPublicWeb3API creates an instance of the Web3 API. 34 | func NewPublicWeb3API() *PublicWeb3API { 35 | return &PublicWeb3API{} 36 | } 37 | 38 | // ClientVersion returns the client version in the Web3 user agent format. 39 | func (a *PublicWeb3API) ClientVersion() string { 40 | return version.ClientVersion() 41 | } 42 | 43 | // Sha3 returns the keccak-256 hash of the passed-in input. 44 | func (a *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes { 45 | return crypto.Keccak256(input) 46 | } 47 | -------------------------------------------------------------------------------- /server/rpc/apis_test.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/cosmos/ethermint/version" 9 | "github.com/ethereum/go-ethereum/rpc" 10 | "github.com/stretchr/testify/require" 11 | "github.com/stretchr/testify/suite" 12 | ) 13 | 14 | type apisTestSuite struct { 15 | suite.Suite 16 | Stop context.CancelFunc 17 | Port int 18 | } 19 | 20 | func (s *apisTestSuite) SetupSuite() { 21 | stop, port, err := startAPIServer() 22 | require.Nil(s.T(), err, "unexpected error") 23 | s.Stop = stop 24 | s.Port = port 25 | } 26 | 27 | func (s *apisTestSuite) TearDownSuite() { 28 | s.Stop() 29 | } 30 | 31 | func (s *apisTestSuite) TestPublicWeb3APIClientVersion() { 32 | res, err := rpcCall(s.Port, "web3_clientVersion", []string{}) 33 | require.Nil(s.T(), err, "unexpected error") 34 | require.Equal(s.T(), version.ClientVersion(), res) 35 | } 36 | 37 | func (s *apisTestSuite) TestPublicWeb3APISha3() { 38 | res, err := rpcCall(s.Port, "web3_sha3", []string{"0x67656c6c6f20776f726c64"}) 39 | require.Nil(s.T(), err, "unexpected error") 40 | require.Equal(s.T(), "0x1b84adea42d5b7d192fd8a61a85b25abe0757e9a65cab1da470258914053823f", res) 41 | } 42 | 43 | func (s *apisTestSuite) TestMiningAPIs() { 44 | res, err := rpcCall(s.Port, "eth_mining", nil) 45 | require.Nil(s.T(), err, "unexpected error") 46 | require.Equal(s.T(), false, res) 47 | 48 | res, err = rpcCall(s.Port, "eth_hashrate", nil) 49 | require.Nil(s.T(), err, "unexpected error") 50 | require.Equal(s.T(), "0x0", res) 51 | } 52 | 53 | func TestAPIsTestSuite(t *testing.T) { 54 | suite.Run(t, new(apisTestSuite)) 55 | } 56 | 57 | func startAPIServer() (context.CancelFunc, int, error) { 58 | config := &Config{ 59 | RPCAddr: "127.0.0.1", 60 | RPCPort: randomPort(), 61 | } 62 | timeouts := rpc.HTTPTimeouts{ 63 | ReadTimeout: 5 * time.Second, 64 | WriteTimeout: 5 * time.Second, 65 | IdleTimeout: 5 * time.Second, 66 | } 67 | 68 | ctx, cancel := context.WithCancel(context.Background()) 69 | 70 | _, err := StartHTTPEndpoint(ctx, config, GetRPCAPIs(), timeouts) 71 | if err != nil { 72 | return cancel, 0, err 73 | } 74 | 75 | return cancel, config.RPCPort, nil 76 | } 77 | -------------------------------------------------------------------------------- /server/rpc/config.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // Config contains configuration fields that determine the 4 | // behavior of the RPC HTTP server. 5 | type Config struct { 6 | // EnableRPC defines whether or not to enable the RPC server 7 | EnableRPC bool 8 | // RPCAddr defines the IP address to listen on 9 | RPCAddr string 10 | // RPCPort defines the port to listen on 11 | RPCPort int 12 | // RPCCORSDomains defines list of domains to enable CORS headers for (used by browsers) 13 | RPCCORSDomains []string 14 | // RPCVhosts defines list of domains to listen on (useful if Tendermint is addressable via DNS) 15 | RPCVHosts []string 16 | } 17 | -------------------------------------------------------------------------------- /server/rpc/eth_api.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/cosmos/ethermint/version" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "github.com/ethereum/go-ethereum/rpc" 8 | "github.com/ethereum/go-ethereum/signer/core" 9 | "math/big" 10 | ) 11 | 12 | // PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. 13 | type PublicEthAPI struct{} 14 | 15 | // NewPublicEthAPI creates an instance of the public ETH Web3 API. 16 | func NewPublicEthAPI() *PublicEthAPI { 17 | return &PublicEthAPI{} 18 | } 19 | 20 | // ProtocolVersion returns the supported Ethereum protocol version. 21 | func (e *PublicEthAPI) ProtocolVersion() string { 22 | return version.ProtocolVersion 23 | } 24 | 25 | // Syncing returns whether or not the current node is syncing with other peers. Returns false if not, or a struct 26 | // outlining the state of the sync if it is. 27 | func (e *PublicEthAPI) Syncing() interface{} { 28 | return false 29 | } 30 | 31 | // Coinbase returns this node's coinbase address. Not used in Ethermint. 32 | func (e *PublicEthAPI) Coinbase() (addr common.Address) { 33 | return 34 | } 35 | 36 | // Mining returns whether or not this node is currently mining. Always false. 37 | func (e *PublicEthAPI) Mining() bool { 38 | return false 39 | } 40 | 41 | // Hashrate returns the current node's hashrate. Always 0. 42 | func (e *PublicEthAPI) Hashrate() hexutil.Uint64 { 43 | return 0 44 | } 45 | 46 | // GasPrice returns the current gas price based on Ethermint's gas price oracle. 47 | func (e *PublicEthAPI) GasPrice() *hexutil.Big { 48 | out := big.NewInt(0) 49 | return (*hexutil.Big)(out) 50 | } 51 | 52 | // Accounts returns the list of accounts available to this node. 53 | func (e *PublicEthAPI) Accounts() []common.Address { 54 | return nil 55 | } 56 | 57 | // BlockNumber returns the current block number. 58 | func (e *PublicEthAPI) BlockNumber() *big.Int { 59 | return big.NewInt(0) 60 | } 61 | 62 | // GetBalance returns the provided account's balance up to the provided block number. 63 | func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumber) *hexutil.Big { 64 | out := big.NewInt(0) 65 | return (*hexutil.Big)(out) 66 | } 67 | 68 | // GetStorageAt returns the contract storage at the given address, block number, and key. 69 | func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum rpc.BlockNumber) hexutil.Bytes { 70 | return nil 71 | } 72 | 73 | // GetTransactionCount returns the number of transactions at the given address up to the given block number. 74 | func (e *PublicEthAPI) GetTransactionCount(address common.Address, blockNum rpc.BlockNumber) hexutil.Uint64 { 75 | return 0 76 | } 77 | 78 | // GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash. 79 | func (e *PublicEthAPI) GetBlockTransactionCountByHash(hash common.Hash) hexutil.Uint { 80 | return 0 81 | } 82 | 83 | // GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. 84 | func (e *PublicEthAPI) GetBlockTransactionCountByNumber(blockNum rpc.BlockNumber) hexutil.Uint { 85 | return 0 86 | } 87 | 88 | // GetUncleCountByBlockHash returns the number of uncles in the block idenfied by hash. Always zero. 89 | func (e *PublicEthAPI) GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint { 90 | return 0 91 | } 92 | 93 | // GetUncleCountByBlockNumber returns the number of uncles in the block idenfied by number. Always zero. 94 | func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum rpc.BlockNumber) hexutil.Uint { 95 | return 0 96 | } 97 | 98 | // GetCode returns the contract code at the given address and block number. 99 | func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumber) hexutil.Bytes { 100 | return nil 101 | } 102 | 103 | // Sign signs the provided data using the private key of address via Geth's signature standard. 104 | func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) hexutil.Bytes { 105 | return nil 106 | } 107 | 108 | // SendTransaction sends an Ethereum transaction. 109 | func (e *PublicEthAPI) SendTransaction(args core.SendTxArgs) common.Hash { 110 | var h common.Hash 111 | return h 112 | } 113 | 114 | // SendRawTransaction send a raw Ethereum transaction. 115 | func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) common.Hash { 116 | var h common.Hash 117 | return h 118 | } 119 | 120 | // CallArgs represents arguments to a smart contract call as provided by RPC clients. 121 | type CallArgs struct { 122 | From common.Address `json:"from"` 123 | To common.Address `json:"to"` 124 | Gas hexutil.Uint64 `json:"gas"` 125 | GasPrice hexutil.Big `json:"gasPrice"` 126 | Value hexutil.Big `json:"value"` 127 | Data hexutil.Bytes `json:"data"` 128 | } 129 | 130 | // Call performs a raw contract call. 131 | func (e *PublicEthAPI) Call(args CallArgs, blockNum rpc.BlockNumber) hexutil.Bytes { 132 | return nil 133 | } 134 | 135 | // EstimateGas estimates gas usage for the given smart contract call. 136 | func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNum rpc.BlockNumber) hexutil.Uint64 { 137 | return 0 138 | } 139 | 140 | // GetBlockByHash returns the block identified by hash. 141 | func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) map[string]interface{} { 142 | return nil 143 | } 144 | 145 | // GetBlockByNumber returns the block identified by number. 146 | func (e *PublicEthAPI) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) map[string]interface{} { 147 | return nil 148 | } 149 | 150 | // Transaction represents a transaction returned to RPC clients. 151 | type Transaction struct { 152 | BlockHash common.Hash `json:"blockHash"` 153 | BlockNumber *hexutil.Big `json:"blockNumber"` 154 | From common.Address `json:"from"` 155 | Gas hexutil.Uint64 `json:"gas"` 156 | GasPrice *hexutil.Big `json:"gasPrice"` 157 | Hash common.Hash `json:"hash"` 158 | Input hexutil.Bytes `json:"input"` 159 | Nonce hexutil.Uint64 `json:"nonce"` 160 | To *common.Address `json:"to"` 161 | TransactionIndex hexutil.Uint `json:"transactionIndex"` 162 | Value *hexutil.Big `json:"value"` 163 | V *hexutil.Big `json:"v"` 164 | R *hexutil.Big `json:"r"` 165 | S *hexutil.Big `json:"s"` 166 | } 167 | 168 | // GetTransactionByHash returns the transaction identified by hash. 169 | func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) *Transaction { 170 | return nil 171 | } 172 | 173 | // GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. 174 | func (e *PublicEthAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) *Transaction { 175 | return nil 176 | } 177 | 178 | // GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. 179 | func (e *PublicEthAPI) GetTransactionByBlockNumberAndIndex(blockNumber rpc.BlockNumber, idx hexutil.Uint) *Transaction { 180 | return nil 181 | } 182 | 183 | // GetTransactionReceipt returns the transaction receipt identified by hash. 184 | func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) map[string]interface{} { 185 | return nil 186 | } 187 | 188 | // GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil. 189 | func (e *PublicEthAPI) GetUncleByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) map[string]interface{} { 190 | return nil 191 | } 192 | 193 | // GetUncleByBlockNumberAndIndex returns the uncle identified by number and index. Always returns nil. 194 | func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexutil.Uint) map[string]interface{} { 195 | return nil 196 | } 197 | -------------------------------------------------------------------------------- /server/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/ethereum/go-ethereum/rpc" 8 | ) 9 | 10 | // StartHTTPEndpoint starts the Tendermint Web3-compatible RPC layer. Consumes 11 | // a Context for cancellation, a config struct, and a list of rpc.API interfaces 12 | // that will be automatically wired into a JSON-RPC webserver. 13 | func StartHTTPEndpoint(ctx context.Context, config *Config, apis []rpc.API, timeouts rpc.HTTPTimeouts) (*rpc.Server, error) { 14 | uniqModules := make(map[string]string) 15 | for _, api := range apis { 16 | uniqModules[api.Namespace] = api.Namespace 17 | } 18 | 19 | modules := make([]string, len(uniqModules)) 20 | i := 0 21 | for k := range uniqModules { 22 | modules[i] = k 23 | i++ 24 | } 25 | 26 | endpoint := fmt.Sprintf("%s:%d", config.RPCAddr, config.RPCPort) 27 | _, server, err := rpc.StartHTTPEndpoint( 28 | endpoint, apis, modules, config.RPCCORSDomains, config.RPCVHosts, timeouts, 29 | ) 30 | 31 | go func() { 32 | <-ctx.Done() 33 | fmt.Println("Shutting down server.") 34 | server.Stop() 35 | }() 36 | 37 | return server, err 38 | } 39 | -------------------------------------------------------------------------------- /server/rpc/rpc_test.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "math/rand" 9 | "net/http" 10 | "strings" 11 | "testing" 12 | "time" 13 | 14 | "github.com/ethereum/go-ethereum/rpc" 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | type TestService struct{} 19 | 20 | func (s *TestService) Foo(arg string) string { 21 | return arg 22 | } 23 | 24 | func TestStartHTTPEndpointStartStop(t *testing.T) { 25 | config := &Config{ 26 | RPCAddr: "127.0.0.1", 27 | RPCPort: randomPort(), 28 | } 29 | 30 | ctx, cancel := context.WithCancel(context.Background()) 31 | 32 | _, err := StartHTTPEndpoint( 33 | ctx, config, []rpc.API{ 34 | { 35 | Namespace: "test", 36 | Version: "1.0", 37 | Service: &TestService{}, 38 | Public: true, 39 | }, 40 | }, 41 | rpc.HTTPTimeouts{ 42 | ReadTimeout: 5 * time.Second, 43 | WriteTimeout: 5 * time.Second, 44 | IdleTimeout: 5 * time.Second, 45 | }, 46 | ) 47 | require.Nil(t, err, "unexpected error") 48 | 49 | res, err := rpcCall(config.RPCPort, "test_foo", []string{"baz"}) 50 | require.Nil(t, err, "unexpected error") 51 | 52 | resStr := res.(string) 53 | require.Equal(t, "baz", resStr) 54 | 55 | cancel() 56 | 57 | _, err = rpcCall(config.RPCPort, "test_foo", []string{"baz"}) 58 | require.NotNil(t, err) 59 | } 60 | 61 | func rpcCall(port int, method string, params []string) (interface{}, error) { 62 | parsedParams, err := json.Marshal(params) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | fullBody := fmt.Sprintf( 68 | `{ "id": 1, "jsonrpc": "2.0", "method": "%s", "params": %s }`, 69 | method, string(parsedParams), 70 | ) 71 | 72 | res, err := http.Post(fmt.Sprintf("http://127.0.0.1:%d", port), "application/json", strings.NewReader(fullBody)) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | data, err := ioutil.ReadAll(res.Body) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | var out map[string]interface{} 83 | err = json.Unmarshal(data, &out) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | result := out["result"].(interface{}) 89 | return result, nil 90 | } 91 | 92 | func randomPort() int { 93 | return rand.Intn(65535-1025) + 1025 94 | } 95 | -------------------------------------------------------------------------------- /types/account.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/x/auth" 8 | 9 | ethcmn "github.com/ethereum/go-ethereum/common" 10 | ) 11 | 12 | var _ auth.Account = (*Account)(nil) 13 | 14 | const ( 15 | // DenomDefault defines the single coin type/denomination supported in 16 | // Ethermint. 17 | DenomDefault = "Photon" 18 | ) 19 | 20 | // ---------------------------------------------------------------------------- 21 | // Main Ethermint account 22 | // ---------------------------------------------------------------------------- 23 | 24 | // BaseAccount implements the auth.Account interface and embeds an 25 | // auth.BaseAccount type. It is compatible with the auth.AccountMapper. 26 | type Account struct { 27 | *auth.BaseAccount 28 | 29 | // merkle root of the storage trie 30 | // 31 | // TODO: good chance we may not need this 32 | Root ethcmn.Hash 33 | 34 | CodeHash []byte 35 | } 36 | 37 | // ProtoBaseAccount defines the prototype function for BaseAccount used for an 38 | // account mapper. 39 | func ProtoBaseAccount() auth.Account { 40 | return &Account{BaseAccount: &auth.BaseAccount{}} 41 | } 42 | 43 | // Balance returns the balance of an account. 44 | func (acc Account) Balance() sdk.Int { 45 | return acc.GetCoins().AmountOf(DenomDefault) 46 | } 47 | 48 | // SetBalance sets an account's balance. 49 | func (acc Account) SetBalance(amt sdk.Int) { 50 | acc.SetCoins(sdk.Coins{sdk.NewCoin(DenomDefault, amt)}) 51 | } 52 | 53 | // ---------------------------------------------------------------------------- 54 | // Code & Storage 55 | // ---------------------------------------------------------------------------- 56 | 57 | // Account code and storage type aliases. 58 | type ( 59 | Code []byte 60 | Storage map[ethcmn.Hash]ethcmn.Hash 61 | ) 62 | 63 | func (c Code) String() string { 64 | return string(c) 65 | } 66 | 67 | func (c Storage) String() (str string) { 68 | for key, value := range c { 69 | str += fmt.Sprintf("%X : %X\n", key, value) 70 | } 71 | 72 | return 73 | } 74 | 75 | // Copy returns a copy of storage. 76 | func (c Storage) Copy() Storage { 77 | cpy := make(Storage) 78 | for key, value := range c { 79 | cpy[key] = value 80 | } 81 | 82 | return cpy 83 | } 84 | -------------------------------------------------------------------------------- /types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | var typesCodec = codec.New() 8 | 9 | func init() { 10 | RegisterCodec(typesCodec) 11 | } 12 | 13 | // RegisterCodec registers all the necessary types with amino for the given 14 | // codec. 15 | func RegisterCodec(cdc *codec.Codec) { 16 | cdc.RegisterConcrete(&Account{}, "types/Account", nil) 17 | } 18 | -------------------------------------------------------------------------------- /types/context.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // AppContext provides the ability for the application to pass around and 4 | // obtain immutable objects easily. More importantly, it allows for the 5 | // utilization of the object-capability model in which components gain access 6 | // to other components for which they truly need. 7 | type AppContext struct { 8 | } 9 | -------------------------------------------------------------------------------- /types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | ) 6 | 7 | // Ethermint error codes 8 | const ( 9 | // DefaultCodespace reserves a Codespace for Ethermint. 10 | DefaultCodespace sdk.CodespaceType = "ethermint" 11 | 12 | CodeInvalidValue sdk.CodeType = 1 13 | CodeInvalidChainID sdk.CodeType = 2 14 | ) 15 | 16 | func codeToDefaultMsg(code sdk.CodeType) string { 17 | switch code { 18 | case CodeInvalidValue: 19 | return "invalid value" 20 | case CodeInvalidChainID: 21 | return "invalid chain ID" 22 | default: 23 | return sdk.CodeToDefaultMsg(code) 24 | } 25 | } 26 | 27 | // ErrInvalidValue returns a standardized SDK error resulting from an invalid 28 | // value. 29 | func ErrInvalidValue(msg string) sdk.Error { 30 | return sdk.NewError(DefaultCodespace, CodeInvalidValue, msg) 31 | } 32 | 33 | // ErrInvalidChainID returns a standardized SDK error resulting from an invalid 34 | // chain ID. 35 | func ErrInvalidChainID(msg string) sdk.Error { 36 | return sdk.NewError(DefaultCodespace, CodeInvalidChainID, msg) 37 | } 38 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | // AppName represents the application name as the 'user agent' on the larger Ethereum network. 9 | const AppName = "Ethermint" 10 | 11 | // Version contains the application semantic version. 12 | // 13 | // TODO: How do we want to version this being that an initial Ethermint has 14 | // been developed? 15 | const Version = "0.0.0" 16 | 17 | // ProtocolVersion is the supported Ethereum protocol version (e.g., Homestead, Olympic, etc.) 18 | const ProtocolVersion = "63" 19 | 20 | // GitCommit contains the git SHA1 short hash set by build flags. 21 | var GitCommit = "" 22 | 23 | // ClientVersion returns the full version string for identification on the larger Ethereum network. 24 | func ClientVersion() string { 25 | return fmt.Sprintf("%s/%s+%s/%s/%s", AppName, Version, GitCommit, runtime.GOOS, runtime.Version()) 26 | } 27 | -------------------------------------------------------------------------------- /x/evm/handler.go: -------------------------------------------------------------------------------- 1 | package evm 2 | -------------------------------------------------------------------------------- /x/evm/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "github.com/cosmos/cosmos-sdk/codec" 4 | 5 | var msgCodec = codec.New() 6 | 7 | func init() { 8 | cdc := codec.New() 9 | 10 | RegisterCodec(cdc) 11 | codec.RegisterCrypto(cdc) 12 | 13 | msgCodec = cdc.Seal() 14 | } 15 | 16 | // Register concrete types and interfaces on the given codec. 17 | func RegisterCodec(cdc *codec.Codec) { 18 | cdc.RegisterConcrete(&EthereumTxMsg{}, "ethermint/MsgEthereumTx", nil) 19 | } 20 | -------------------------------------------------------------------------------- /x/evm/types/dump.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | ethstate "github.com/ethereum/go-ethereum/core/state" 5 | ) 6 | 7 | // RawDump returns a raw state dump. 8 | // 9 | // TODO: Implement if we need it, especially for the RPC API. 10 | func (csdb *CommitStateDB) RawDump() ethstate.Dump { 11 | return ethstate.Dump{} 12 | } 13 | -------------------------------------------------------------------------------- /x/evm/types/journal.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | ethcmn "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | var ripemd = ethcmn.HexToAddress("0000000000000000000000000000000000000003") 10 | 11 | // journalEntry is a modification entry in the state change journal that can be 12 | // reverted on demand. 13 | type journalEntry interface { 14 | // revert undoes the changes introduced by this journal entry. 15 | revert(*CommitStateDB) 16 | 17 | // dirtied returns the Ethereum address modified by this journal entry. 18 | dirtied() *ethcmn.Address 19 | } 20 | 21 | // journal contains the list of state modifications applied since the last state 22 | // commit. These are tracked to be able to be reverted in case of an execution 23 | // exception or revertal request. 24 | type journal struct { 25 | entries []journalEntry // Current changes tracked by the journal 26 | dirties map[ethcmn.Address]int // Dirty accounts and the number of changes 27 | } 28 | 29 | // newJournal create a new initialized journal. 30 | func newJournal() *journal { 31 | return &journal{ 32 | dirties: make(map[ethcmn.Address]int), 33 | } 34 | } 35 | 36 | // append inserts a new modification entry to the end of the change journal. 37 | func (j *journal) append(entry journalEntry) { 38 | j.entries = append(j.entries, entry) 39 | if addr := entry.dirtied(); addr != nil { 40 | j.dirties[*addr]++ 41 | } 42 | } 43 | 44 | // revert undoes a batch of journalled modifications along with any reverted 45 | // dirty handling too. 46 | func (j *journal) revert(statedb *CommitStateDB, snapshot int) { 47 | for i := len(j.entries) - 1; i >= snapshot; i-- { 48 | // Undo the changes made by the operation 49 | j.entries[i].revert(statedb) 50 | 51 | // Drop any dirty tracking induced by the change 52 | if addr := j.entries[i].dirtied(); addr != nil { 53 | if j.dirties[*addr]--; j.dirties[*addr] == 0 { 54 | delete(j.dirties, *addr) 55 | } 56 | } 57 | } 58 | j.entries = j.entries[:snapshot] 59 | } 60 | 61 | // dirty explicitly sets an address to dirty, even if the change entries would 62 | // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD 63 | // precompile consensus exception. 64 | func (j *journal) dirty(addr ethcmn.Address) { 65 | j.dirties[addr]++ 66 | } 67 | 68 | // length returns the current number of entries in the journal. 69 | func (j *journal) length() int { 70 | return len(j.entries) 71 | } 72 | 73 | type ( 74 | // Changes to the account trie. 75 | createObjectChange struct { 76 | account *ethcmn.Address 77 | } 78 | 79 | resetObjectChange struct { 80 | prev *stateObject 81 | } 82 | 83 | suicideChange struct { 84 | account *ethcmn.Address 85 | prev bool // whether account had already suicided 86 | prevBalance sdk.Int 87 | } 88 | 89 | // Changes to individual accounts. 90 | balanceChange struct { 91 | account *ethcmn.Address 92 | prev sdk.Int 93 | } 94 | 95 | nonceChange struct { 96 | account *ethcmn.Address 97 | prev uint64 98 | } 99 | 100 | storageChange struct { 101 | account *ethcmn.Address 102 | key, prevValue ethcmn.Hash 103 | } 104 | 105 | codeChange struct { 106 | account *ethcmn.Address 107 | prevCode, prevHash []byte 108 | } 109 | 110 | // Changes to other state values. 111 | refundChange struct { 112 | prev uint64 113 | } 114 | 115 | addLogChange struct { 116 | txhash ethcmn.Hash 117 | } 118 | 119 | addPreimageChange struct { 120 | hash ethcmn.Hash 121 | } 122 | 123 | touchChange struct { 124 | account *ethcmn.Address 125 | prev bool 126 | prevDirty bool 127 | } 128 | ) 129 | 130 | func (ch createObjectChange) revert(s *CommitStateDB) { 131 | delete(s.stateObjects, *ch.account) 132 | delete(s.stateObjectsDirty, *ch.account) 133 | } 134 | 135 | func (ch createObjectChange) dirtied() *ethcmn.Address { 136 | return ch.account 137 | } 138 | 139 | func (ch resetObjectChange) revert(s *CommitStateDB) { 140 | s.setStateObject(ch.prev) 141 | } 142 | 143 | func (ch resetObjectChange) dirtied() *ethcmn.Address { 144 | return nil 145 | } 146 | 147 | func (ch suicideChange) revert(s *CommitStateDB) { 148 | so := s.getStateObject(*ch.account) 149 | if so != nil { 150 | so.suicided = ch.prev 151 | so.setBalance(ch.prevBalance) 152 | } 153 | } 154 | 155 | func (ch suicideChange) dirtied() *ethcmn.Address { 156 | return ch.account 157 | } 158 | 159 | func (ch touchChange) revert(s *CommitStateDB) { 160 | } 161 | 162 | func (ch touchChange) dirtied() *ethcmn.Address { 163 | return ch.account 164 | } 165 | 166 | func (ch balanceChange) revert(s *CommitStateDB) { 167 | s.getStateObject(*ch.account).setBalance(ch.prev) 168 | } 169 | 170 | func (ch balanceChange) dirtied() *ethcmn.Address { 171 | return ch.account 172 | } 173 | 174 | func (ch nonceChange) revert(s *CommitStateDB) { 175 | s.getStateObject(*ch.account).setNonce(ch.prev) 176 | } 177 | 178 | func (ch nonceChange) dirtied() *ethcmn.Address { 179 | return ch.account 180 | } 181 | 182 | func (ch codeChange) revert(s *CommitStateDB) { 183 | s.getStateObject(*ch.account).setCode(ethcmn.BytesToHash(ch.prevHash), ch.prevCode) 184 | } 185 | 186 | func (ch codeChange) dirtied() *ethcmn.Address { 187 | return ch.account 188 | } 189 | 190 | func (ch storageChange) revert(s *CommitStateDB) { 191 | s.getStateObject(*ch.account).setState(ch.key, ch.prevValue) 192 | } 193 | 194 | func (ch storageChange) dirtied() *ethcmn.Address { 195 | return ch.account 196 | } 197 | 198 | func (ch refundChange) revert(s *CommitStateDB) { 199 | s.refund = ch.prev 200 | } 201 | 202 | func (ch refundChange) dirtied() *ethcmn.Address { 203 | return nil 204 | } 205 | 206 | func (ch addLogChange) revert(s *CommitStateDB) { 207 | logs := s.logs[ch.txhash] 208 | if len(logs) == 1 { 209 | delete(s.logs, ch.txhash) 210 | } else { 211 | s.logs[ch.txhash] = logs[:len(logs)-1] 212 | } 213 | 214 | s.logSize-- 215 | } 216 | 217 | func (ch addLogChange) dirtied() *ethcmn.Address { 218 | return nil 219 | } 220 | 221 | func (ch addPreimageChange) revert(s *CommitStateDB) { 222 | delete(s.preimages, ch.hash) 223 | } 224 | 225 | func (ch addPreimageChange) dirtied() *ethcmn.Address { 226 | return nil 227 | } 228 | -------------------------------------------------------------------------------- /x/evm/types/msg.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/big" 9 | "sync/atomic" 10 | 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | "github.com/cosmos/ethermint/types" 14 | 15 | ethcmn "github.com/ethereum/go-ethereum/common" 16 | ethtypes "github.com/ethereum/go-ethereum/core/types" 17 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 18 | "github.com/ethereum/go-ethereum/rlp" 19 | ) 20 | 21 | var ( 22 | _ sdk.Msg = EthereumTxMsg{} 23 | _ sdk.Tx = EthereumTxMsg{} 24 | ) 25 | 26 | var big8 = big.NewInt(8) 27 | 28 | // message type and route constants 29 | const ( 30 | TypeEthereumTxMsg = "ethereum_tx" 31 | RouteEthereumTxMsg = "evm" 32 | ) 33 | 34 | // EthereumTxMsg encapsulates an Ethereum transaction as an SDK message. 35 | type ( 36 | EthereumTxMsg struct { 37 | Data TxData 38 | 39 | // caches 40 | hash atomic.Value 41 | size atomic.Value 42 | from atomic.Value 43 | } 44 | 45 | // TxData implements the Ethereum transaction data structure. It is used 46 | // solely as intended in Ethereum abiding by the protocol. 47 | TxData struct { 48 | AccountNonce uint64 `json:"nonce"` 49 | Price *big.Int `json:"gasPrice"` 50 | GasLimit uint64 `json:"gas"` 51 | Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation 52 | Amount *big.Int `json:"value"` 53 | Payload []byte `json:"input"` 54 | 55 | // signature values 56 | V *big.Int `json:"v"` 57 | R *big.Int `json:"r"` 58 | S *big.Int `json:"s"` 59 | 60 | // hash is only used when marshaling to JSON 61 | Hash *ethcmn.Hash `json:"hash" rlp:"-"` 62 | } 63 | 64 | // sigCache is used to cache the derived sender and contains the signer used 65 | // to derive it. 66 | sigCache struct { 67 | signer ethtypes.Signer 68 | from ethcmn.Address 69 | } 70 | ) 71 | 72 | // NewEthereumTxMsg returns a reference to a new Ethereum transaction message. 73 | func NewEthereumTxMsg( 74 | nonce uint64, to ethcmn.Address, amount *big.Int, 75 | gasLimit uint64, gasPrice *big.Int, payload []byte, 76 | ) *EthereumTxMsg { 77 | 78 | return newEthereumTxMsg(nonce, &to, amount, gasLimit, gasPrice, payload) 79 | } 80 | 81 | // NewEthereumTxMsgContract returns a reference to a new Ethereum transaction 82 | // message designated for contract creation. 83 | func NewEthereumTxMsgContract( 84 | nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte, 85 | ) *EthereumTxMsg { 86 | 87 | return newEthereumTxMsg(nonce, nil, amount, gasLimit, gasPrice, payload) 88 | } 89 | 90 | func newEthereumTxMsg( 91 | nonce uint64, to *ethcmn.Address, amount *big.Int, 92 | gasLimit uint64, gasPrice *big.Int, payload []byte, 93 | ) *EthereumTxMsg { 94 | 95 | if len(payload) > 0 { 96 | payload = ethcmn.CopyBytes(payload) 97 | } 98 | 99 | txData := TxData{ 100 | AccountNonce: nonce, 101 | Recipient: to, 102 | Payload: payload, 103 | GasLimit: gasLimit, 104 | Amount: new(big.Int), 105 | Price: new(big.Int), 106 | V: new(big.Int), 107 | R: new(big.Int), 108 | S: new(big.Int), 109 | } 110 | 111 | if amount != nil { 112 | txData.Amount.Set(amount) 113 | } 114 | if gasPrice != nil { 115 | txData.Price.Set(gasPrice) 116 | } 117 | 118 | return &EthereumTxMsg{Data: txData} 119 | } 120 | 121 | // Route returns the route value of an EthereumTxMsg. 122 | func (msg EthereumTxMsg) Route() string { return RouteEthereumTxMsg } 123 | 124 | // Type returns the type value of an EthereumTxMsg. 125 | func (msg EthereumTxMsg) Type() string { return TypeEthereumTxMsg } 126 | 127 | // ValidateBasic implements the sdk.Msg interface. It performs basic validation 128 | // checks of a Transaction. If returns an sdk.Error if validation fails. 129 | func (msg EthereumTxMsg) ValidateBasic() sdk.Error { 130 | if msg.Data.Price.Sign() != 1 { 131 | return types.ErrInvalidValue("price must be positive") 132 | } 133 | 134 | if msg.Data.Amount.Sign() != 1 { 135 | return types.ErrInvalidValue("amount must be positive") 136 | } 137 | 138 | return nil 139 | } 140 | 141 | // To returns the recipient address of the transaction. It returns nil if the 142 | // transaction is a contract creation. 143 | func (msg EthereumTxMsg) To() *ethcmn.Address { 144 | if msg.Data.Recipient == nil { 145 | return nil 146 | } 147 | 148 | return msg.Data.Recipient 149 | } 150 | 151 | // GetMsgs returns a single EthereumTxMsg as an sdk.Msg. 152 | func (msg EthereumTxMsg) GetMsgs() []sdk.Msg { 153 | return []sdk.Msg{msg} 154 | } 155 | 156 | // GetSigners returns the expected signers for an Ethereum transaction message. 157 | // For such a message, there should exist only a single 'signer'. 158 | // 159 | // NOTE: This method cannot be used as a chain ID is needed to recover the signer 160 | // from the signature. Use 'VerifySig' instead. 161 | func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress { 162 | panic("must use 'VerifySig' with a chain ID to get the signer") 163 | } 164 | 165 | // GetSignBytes returns the Amino bytes of an Ethereum transaction message used 166 | // for signing. 167 | // 168 | // NOTE: This method cannot be used as a chain ID is needed to create valid bytes 169 | // to sign over. Use 'RLPSignBytes' instead. 170 | func (msg EthereumTxMsg) GetSignBytes() []byte { 171 | panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign") 172 | } 173 | 174 | // RLPSignBytes returns the RLP hash of an Ethereum transaction message with a 175 | // given chainID used for signing. 176 | func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash { 177 | return rlpHash([]interface{}{ 178 | msg.Data.AccountNonce, 179 | msg.Data.Price, 180 | msg.Data.GasLimit, 181 | msg.Data.Recipient, 182 | msg.Data.Amount, 183 | msg.Data.Payload, 184 | chainID, uint(0), uint(0), 185 | }) 186 | } 187 | 188 | // EncodeRLP implements the rlp.Encoder interface. 189 | func (msg *EthereumTxMsg) EncodeRLP(w io.Writer) error { 190 | return rlp.Encode(w, &msg.Data) 191 | } 192 | 193 | // DecodeRLP implements the rlp.Decoder interface. 194 | func (msg *EthereumTxMsg) DecodeRLP(s *rlp.Stream) error { 195 | _, size, _ := s.Kind() 196 | 197 | err := s.Decode(&msg.Data) 198 | if err == nil { 199 | msg.size.Store(ethcmn.StorageSize(rlp.ListSize(size))) 200 | } 201 | 202 | return err 203 | } 204 | 205 | // Hash hashes the RLP encoding of a transaction. 206 | func (msg *EthereumTxMsg) Hash() ethcmn.Hash { 207 | if hash := msg.hash.Load(); hash != nil { 208 | return hash.(ethcmn.Hash) 209 | } 210 | 211 | v := rlpHash(msg) 212 | msg.hash.Store(v) 213 | 214 | return v 215 | } 216 | 217 | // Sign calculates a secp256k1 ECDSA signature and signs the transaction. It 218 | // takes a private key and chainID to sign an Ethereum transaction according to 219 | // EIP155 standard. It mutates the transaction as it populates the V, R, S 220 | // fields of the Transaction's Signature. 221 | func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) { 222 | txHash := msg.RLPSignBytes(chainID) 223 | 224 | sig, err := ethcrypto.Sign(txHash[:], priv) 225 | if err != nil { 226 | panic(err) 227 | } 228 | 229 | if len(sig) != 65 { 230 | panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) 231 | } 232 | 233 | r := new(big.Int).SetBytes(sig[:32]) 234 | s := new(big.Int).SetBytes(sig[32:64]) 235 | 236 | var v *big.Int 237 | 238 | if chainID.Sign() == 0 { 239 | v = new(big.Int).SetBytes([]byte{sig[64] + 27}) 240 | } else { 241 | v = big.NewInt(int64(sig[64] + 35)) 242 | chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2)) 243 | 244 | v.Add(v, chainIDMul) 245 | } 246 | 247 | msg.Data.V = v 248 | msg.Data.R = r 249 | msg.Data.S = s 250 | } 251 | 252 | // VerifySig attempts to verify a Transaction's signature for a given chainID. 253 | // A derived address is returned upon success or an error if recovery fails. 254 | func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) { 255 | signer := ethtypes.NewEIP155Signer(chainID) 256 | 257 | if sc := msg.from.Load(); sc != nil { 258 | sigCache := sc.(sigCache) 259 | // If the signer used to derive from in a previous call is not the same as 260 | // used current, invalidate the cache. 261 | if sigCache.signer.Equal(signer) { 262 | return sigCache.from, nil 263 | } 264 | } 265 | 266 | // do not allow recovery for transactions with an unprotected chainID 267 | if chainID.Sign() == 0 { 268 | return ethcmn.Address{}, errors.New("chainID cannot be zero") 269 | } 270 | 271 | chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2)) 272 | V := new(big.Int).Sub(msg.Data.V, chainIDMul) 273 | V.Sub(V, big8) 274 | 275 | sigHash := msg.RLPSignBytes(chainID) 276 | sender, err := recoverEthSig(msg.Data.R, msg.Data.S, V, sigHash) 277 | if err != nil { 278 | return ethcmn.Address{}, err 279 | } 280 | 281 | msg.from.Store(sigCache{signer: signer, from: sender}) 282 | return sender, nil 283 | } 284 | 285 | // Cost returns amount + gasprice * gaslimit. 286 | func (msg EthereumTxMsg) Cost() *big.Int { 287 | total := msg.Fee() 288 | total.Add(total, msg.Data.Amount) 289 | return total 290 | } 291 | 292 | // Fee returns gasprice * gaslimit. 293 | func (msg EthereumTxMsg) Fee() *big.Int { 294 | return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit)) 295 | } 296 | 297 | // ---------------------------------------------------------------------------- 298 | // Auxiliary 299 | 300 | // TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and 301 | // EthereumTxMsg transactions. 302 | func TxDecoder(cdc *codec.Codec) sdk.TxDecoder { 303 | return func(txBytes []byte) (sdk.Tx, sdk.Error) { 304 | var tx sdk.Tx 305 | 306 | if len(txBytes) == 0 { 307 | return nil, sdk.ErrTxDecode("txBytes are empty") 308 | } 309 | 310 | err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) 311 | if err != nil { 312 | fmt.Println(err.Error()) 313 | return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error()) 314 | } 315 | 316 | return tx, nil 317 | } 318 | } 319 | 320 | // recoverEthSig recovers a signature according to the Ethereum specification and 321 | // returns the sender or an error. 322 | // 323 | // Ref: Ethereum Yellow Paper (BYZANTIUM VERSION 69351d5) Appendix F 324 | func recoverEthSig(R, S, Vb *big.Int, sigHash ethcmn.Hash) (ethcmn.Address, error) { 325 | if Vb.BitLen() > 8 { 326 | return ethcmn.Address{}, errors.New("invalid signature") 327 | } 328 | 329 | V := byte(Vb.Uint64() - 27) 330 | if !ethcrypto.ValidateSignatureValues(V, R, S, true) { 331 | return ethcmn.Address{}, errors.New("invalid signature") 332 | } 333 | 334 | // encode the signature in uncompressed format 335 | r, s := R.Bytes(), S.Bytes() 336 | sig := make([]byte, 65) 337 | 338 | copy(sig[32-len(r):32], r) 339 | copy(sig[64-len(s):64], s) 340 | sig[64] = V 341 | 342 | // recover the public key from the signature 343 | pub, err := ethcrypto.Ecrecover(sigHash[:], sig) 344 | if err != nil { 345 | return ethcmn.Address{}, err 346 | } 347 | 348 | if len(pub) == 0 || pub[0] != 4 { 349 | return ethcmn.Address{}, errors.New("invalid public key") 350 | } 351 | 352 | var addr ethcmn.Address 353 | copy(addr[:], ethcrypto.Keccak256(pub[1:])[12:]) 354 | 355 | return addr, nil 356 | } 357 | -------------------------------------------------------------------------------- /x/evm/types/msg_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/big" 7 | "testing" 8 | 9 | "github.com/cosmos/ethermint/crypto" 10 | ethcmn "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/rlp" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestMsgEthereumTx(t *testing.T) { 16 | addr := GenerateEthAddress() 17 | 18 | msg1 := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 19 | require.NotNil(t, msg1) 20 | require.Equal(t, *msg1.Data.Recipient, addr) 21 | 22 | msg2 := NewEthereumTxMsgContract(0, nil, 100000, nil, []byte("test")) 23 | require.NotNil(t, msg2) 24 | require.Nil(t, msg2.Data.Recipient) 25 | 26 | msg3 := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 27 | require.Equal(t, msg3.Route(), RouteEthereumTxMsg) 28 | require.Equal(t, msg3.Type(), TypeEthereumTxMsg) 29 | require.Panics(t, func() { msg3.GetSigners() }) 30 | require.Panics(t, func() { msg3.GetSignBytes() }) 31 | } 32 | 33 | func TestMsgEthereumTxValidation(t *testing.T) { 34 | testCases := []struct { 35 | nonce uint64 36 | to ethcmn.Address 37 | amount *big.Int 38 | gasLimit uint64 39 | gasPrice *big.Int 40 | payload []byte 41 | expectPass bool 42 | }{ 43 | {amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true}, 44 | {amount: big.NewInt(-1), gasPrice: big.NewInt(100000), expectPass: false}, 45 | {amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: false}, 46 | } 47 | 48 | for i, tc := range testCases { 49 | msg := NewEthereumTxMsg(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload) 50 | 51 | if tc.expectPass { 52 | require.Nil(t, msg.ValidateBasic(), "test: %v", i) 53 | } else { 54 | require.NotNil(t, msg.ValidateBasic(), "test: %v", i) 55 | } 56 | } 57 | } 58 | 59 | func TestMsgEthereumTxRLPSignBytes(t *testing.T) { 60 | addr := ethcmn.BytesToAddress([]byte("test_address")) 61 | chainID := big.NewInt(3) 62 | 63 | msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 64 | hash := msg.RLPSignBytes(chainID) 65 | require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash)) 66 | } 67 | 68 | func TestMsgEthereumTxRLPEncode(t *testing.T) { 69 | addr := ethcmn.BytesToAddress([]byte("test_address")) 70 | msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 71 | 72 | raw, err := rlp.EncodeToBytes(msg) 73 | require.NoError(t, err) 74 | require.Equal(t, ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080"), raw) 75 | } 76 | 77 | func TestMsgEthereumTxRLPDecode(t *testing.T) { 78 | var msg EthereumTxMsg 79 | 80 | raw := ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080") 81 | addr := ethcmn.BytesToAddress([]byte("test_address")) 82 | expectedMsg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 83 | 84 | err := rlp.Decode(bytes.NewReader(raw), &msg) 85 | require.NoError(t, err) 86 | require.Equal(t, expectedMsg.Data, msg.Data) 87 | } 88 | 89 | func TestMsgEthereumTxHash(t *testing.T) { 90 | addr := ethcmn.BytesToAddress([]byte("test_address")) 91 | msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 92 | 93 | hash := msg.Hash() 94 | require.Equal(t, "E2AA2E68E7586AE9700F1D3D643330866B6AC2B6CA4C804F7C85ECB11D0B0B29", fmt.Sprintf("%X", hash)) 95 | } 96 | 97 | func TestMsgEthereumTxSig(t *testing.T) { 98 | chainID := big.NewInt(3) 99 | 100 | priv1, _ := crypto.GenerateKey() 101 | priv2, _ := crypto.GenerateKey() 102 | addr1 := ethcmn.BytesToAddress(priv1.PubKey().Address().Bytes()) 103 | addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes()) 104 | 105 | // require valid signature passes validation 106 | msg := NewEthereumTxMsg(0, addr1, nil, 100000, nil, []byte("test")) 107 | msg.Sign(chainID, priv1.ToECDSA()) 108 | 109 | signer, err := msg.VerifySig(chainID) 110 | require.NoError(t, err) 111 | require.Equal(t, addr1, signer) 112 | require.NotEqual(t, addr2, signer) 113 | 114 | // require invalid chain ID fail validation 115 | msg = NewEthereumTxMsg(0, addr1, nil, 100000, nil, []byte("test")) 116 | msg.Sign(chainID, priv1.ToECDSA()) 117 | 118 | signer, err = msg.VerifySig(big.NewInt(4)) 119 | require.Error(t, err) 120 | require.Equal(t, ethcmn.Address{}, signer) 121 | } 122 | 123 | func TestMsgEthereumTxAmino(t *testing.T) { 124 | addr := GenerateEthAddress() 125 | msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) 126 | 127 | raw, err := msgCodec.MarshalBinaryBare(msg) 128 | require.NoError(t, err) 129 | 130 | var msg2 EthereumTxMsg 131 | 132 | err = msgCodec.UnmarshalBinaryBare(raw, &msg2) 133 | require.NoError(t, err) 134 | require.Equal(t, msg.Data, msg2.Data) 135 | } 136 | -------------------------------------------------------------------------------- /x/evm/types/state_object.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/big" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | auth "github.com/cosmos/cosmos-sdk/x/auth" 10 | 11 | "github.com/cosmos/ethermint/types" 12 | ethcmn "github.com/ethereum/go-ethereum/common" 13 | ethstate "github.com/ethereum/go-ethereum/core/state" 14 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 15 | ) 16 | 17 | var ( 18 | _ ethstate.StateObject = (*stateObject)(nil) 19 | 20 | emptyCodeHash = ethcrypto.Keccak256(nil) 21 | ) 22 | 23 | type ( 24 | // stateObject represents an Ethereum account which is being modified. 25 | // 26 | // The usage pattern is as follows: 27 | // First you need to obtain a state object. 28 | // Account values can be accessed and modified through the object. 29 | // Finally, call CommitTrie to write the modified storage trie into a database. 30 | stateObject struct { 31 | address ethcmn.Address 32 | stateDB *CommitStateDB 33 | account *types.Account 34 | 35 | // DB error. 36 | // State objects are used by the consensus core and VM which are 37 | // unable to deal with database-level errors. Any error that occurs 38 | // during a database read is memoized here and will eventually be returned 39 | // by StateDB.Commit. 40 | dbErr error 41 | 42 | code types.Code // contract bytecode, which gets set when code is loaded 43 | 44 | originStorage types.Storage // Storage cache of original entries to dedup rewrites 45 | dirtyStorage types.Storage // Storage entries that need to be flushed to disk 46 | 47 | // cache flags 48 | // 49 | // When an object is marked suicided it will be delete from the trie during 50 | // the "update" phase of the state transition. 51 | dirtyCode bool // true if the code was updated 52 | suicided bool 53 | deleted bool 54 | } 55 | ) 56 | 57 | func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { 58 | acc, ok := accProto.(*types.Account) 59 | if !ok { 60 | panic(fmt.Sprintf("invalid account type for state object: %T", acc)) 61 | } 62 | 63 | if acc.CodeHash == nil { 64 | acc.CodeHash = emptyCodeHash 65 | } 66 | 67 | return &stateObject{ 68 | stateDB: db, 69 | account: acc, 70 | address: ethcmn.BytesToAddress(acc.Address.Bytes()), 71 | originStorage: make(types.Storage), 72 | dirtyStorage: make(types.Storage), 73 | } 74 | } 75 | 76 | // ---------------------------------------------------------------------------- 77 | // Setters 78 | // ---------------------------------------------------------------------------- 79 | 80 | // SetState updates a value in account storage. Note, the key will be prefixed 81 | // with the address of the state object. 82 | func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { 83 | // if the new value is the same as old, don't set 84 | prev := so.GetState(db, key) 85 | if prev == value { 86 | return 87 | } 88 | 89 | prefixKey := so.GetStorageByAddressKey(key.Bytes()) 90 | 91 | // since the new value is different, update and journal the change 92 | so.stateDB.journal.append(storageChange{ 93 | account: &so.address, 94 | key: prefixKey, 95 | prevValue: prev, 96 | }) 97 | 98 | so.setState(prefixKey, value) 99 | } 100 | 101 | func (so *stateObject) setState(key, value ethcmn.Hash) { 102 | so.dirtyStorage[key] = value 103 | } 104 | 105 | // SetCode sets the state object's code. 106 | func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { 107 | prevCode := so.Code(nil) 108 | 109 | so.stateDB.journal.append(codeChange{ 110 | account: &so.address, 111 | prevHash: so.CodeHash(), 112 | prevCode: prevCode, 113 | }) 114 | 115 | so.setCode(codeHash, code) 116 | } 117 | 118 | func (so *stateObject) setCode(codeHash ethcmn.Hash, code []byte) { 119 | so.code = code 120 | so.account.CodeHash = codeHash.Bytes() 121 | so.dirtyCode = true 122 | } 123 | 124 | // AddBalance adds an amount to a state object's balance. It is used to add 125 | // funds to the destination account of a transfer. 126 | func (so *stateObject) AddBalance(amount *big.Int) { 127 | amt := sdk.NewIntFromBigInt(amount) 128 | 129 | // EIP158: We must check emptiness for the objects such that the account 130 | // clearing (0,0,0 objects) can take effect. 131 | if amt.Sign() == 0 { 132 | if so.empty() { 133 | so.touch() 134 | } 135 | 136 | return 137 | } 138 | 139 | newBalance := so.account.Balance().Add(amt) 140 | so.SetBalance(newBalance.BigInt()) 141 | } 142 | 143 | // SubBalance removes an amount from the stateObject's balance. It is used to 144 | // remove funds from the origin account of a transfer. 145 | func (so *stateObject) SubBalance(amount *big.Int) { 146 | amt := sdk.NewIntFromBigInt(amount) 147 | 148 | if amt.Sign() == 0 { 149 | return 150 | } 151 | 152 | newBalance := so.account.Balance().Sub(amt) 153 | so.SetBalance(newBalance.BigInt()) 154 | } 155 | 156 | // SetBalance sets the state object's balance. 157 | func (so *stateObject) SetBalance(amount *big.Int) { 158 | amt := sdk.NewIntFromBigInt(amount) 159 | 160 | so.stateDB.journal.append(balanceChange{ 161 | account: &so.address, 162 | prev: so.account.Balance(), 163 | }) 164 | 165 | so.setBalance(amt) 166 | } 167 | 168 | func (so *stateObject) setBalance(amount sdk.Int) { 169 | so.account.SetBalance(amount) 170 | } 171 | 172 | // SetNonce sets the state object's nonce (sequence number). 173 | func (so *stateObject) SetNonce(nonce uint64) { 174 | so.stateDB.journal.append(nonceChange{ 175 | account: &so.address, 176 | prev: so.account.Sequence, 177 | }) 178 | 179 | so.setNonce(nonce) 180 | } 181 | 182 | func (so *stateObject) setNonce(nonce uint64) { 183 | so.account.Sequence = nonce 184 | } 185 | 186 | // setError remembers the first non-nil error it is called with. 187 | func (so *stateObject) setError(err error) { 188 | if so.dbErr == nil { 189 | so.dbErr = err 190 | } 191 | } 192 | 193 | func (so *stateObject) markSuicided() { 194 | so.suicided = true 195 | } 196 | 197 | // commitState commits all dirty storage to a KVStore. 198 | func (so *stateObject) commitState() { 199 | ctx := so.stateDB.ctx 200 | store := ctx.KVStore(so.stateDB.storageKey) 201 | 202 | for key, value := range so.dirtyStorage { 203 | delete(so.dirtyStorage, key) 204 | 205 | // skip no-op changes, persist actual changes 206 | if value == so.originStorage[key] { 207 | continue 208 | } 209 | 210 | so.originStorage[key] = value 211 | 212 | // delete empty values 213 | if (value == ethcmn.Hash{}) { 214 | store.Delete(key.Bytes()) 215 | continue 216 | } 217 | 218 | store.Set(key.Bytes(), value.Bytes()) 219 | } 220 | 221 | // TODO: Set the account (storage) root (but we probably don't need this) 222 | } 223 | 224 | // commitCode persists the state object's code to the KVStore. 225 | func (so *stateObject) commitCode() { 226 | ctx := so.stateDB.ctx 227 | store := ctx.KVStore(so.stateDB.codeKey) 228 | store.Set(so.CodeHash(), so.code) 229 | } 230 | 231 | // ---------------------------------------------------------------------------- 232 | // Getters 233 | // ---------------------------------------------------------------------------- 234 | 235 | // Address returns the address of the state object. 236 | func (so stateObject) Address() ethcmn.Address { 237 | return so.address 238 | } 239 | 240 | // Balance returns the state object's current balance. 241 | func (so *stateObject) Balance() *big.Int { 242 | return so.account.Balance().BigInt() 243 | } 244 | 245 | // CodeHash returns the state object's code hash. 246 | func (so *stateObject) CodeHash() []byte { 247 | return so.account.CodeHash 248 | } 249 | 250 | // Nonce returns the state object's current nonce (sequence number). 251 | func (so *stateObject) Nonce() uint64 { 252 | return so.account.Sequence 253 | } 254 | 255 | // Code returns the contract code associated with this object, if any. 256 | func (so *stateObject) Code(_ ethstate.Database) []byte { 257 | if so.code != nil { 258 | return so.code 259 | } 260 | 261 | if bytes.Equal(so.CodeHash(), emptyCodeHash) { 262 | return nil 263 | } 264 | 265 | ctx := so.stateDB.ctx 266 | store := ctx.KVStore(so.stateDB.codeKey) 267 | code := store.Get(so.CodeHash()) 268 | 269 | if len(code) == 0 { 270 | so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address())) 271 | } 272 | 273 | so.code = code 274 | return code 275 | } 276 | 277 | // GetState retrieves a value from the account storage trie. Note, the key will 278 | // be prefixed with the address of the state object. 279 | func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash { 280 | prefixKey := so.GetStorageByAddressKey(key.Bytes()) 281 | 282 | // if we have a dirty value for this state entry, return it 283 | value, dirty := so.dirtyStorage[prefixKey] 284 | if dirty { 285 | return value 286 | } 287 | 288 | // otherwise return the entry's original value 289 | return so.GetCommittedState(db, key) 290 | } 291 | 292 | // GetCommittedState retrieves a value from the committed account storage trie. 293 | // Note, the key will be prefixed with the address of the state object. 294 | func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { 295 | prefixKey := so.GetStorageByAddressKey(key.Bytes()) 296 | 297 | // if we have the original value cached, return that 298 | value, cached := so.originStorage[prefixKey] 299 | if cached { 300 | return value 301 | } 302 | 303 | // otherwise load the value from the KVStore 304 | ctx := so.stateDB.ctx 305 | store := ctx.KVStore(so.stateDB.storageKey) 306 | rawValue := store.Get(prefixKey.Bytes()) 307 | 308 | if len(rawValue) > 0 { 309 | value.SetBytes(rawValue) 310 | } 311 | 312 | so.originStorage[prefixKey] = value 313 | return value 314 | } 315 | 316 | // ---------------------------------------------------------------------------- 317 | // Auxiliary 318 | // ---------------------------------------------------------------------------- 319 | 320 | // ReturnGas returns the gas back to the origin. Used by the Virtual machine or 321 | // Closures. It performs a no-op. 322 | func (so *stateObject) ReturnGas(gas *big.Int) {} 323 | 324 | func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject { 325 | newStateObj := newObject(db, so.account) 326 | 327 | newStateObj.code = so.code 328 | newStateObj.dirtyStorage = so.dirtyStorage.Copy() 329 | newStateObj.originStorage = so.originStorage.Copy() 330 | newStateObj.suicided = so.suicided 331 | newStateObj.dirtyCode = so.dirtyCode 332 | newStateObj.deleted = so.deleted 333 | 334 | return newStateObj 335 | } 336 | 337 | // empty returns whether the account is considered empty. 338 | func (so *stateObject) empty() bool { 339 | return so.account.Sequence == 0 && 340 | so.account.Balance().Sign() == 0 && 341 | bytes.Equal(so.account.CodeHash, emptyCodeHash) 342 | } 343 | 344 | func (so *stateObject) touch() { 345 | so.stateDB.journal.append(touchChange{ 346 | account: &so.address, 347 | }) 348 | 349 | if so.address == ripemd { 350 | // Explicitly put it in the dirty-cache, which is otherwise generated from 351 | // flattened journals. 352 | so.stateDB.journal.dirty(so.address) 353 | } 354 | } 355 | 356 | // GetStorageByAddressKey returns a hash of the composite key for a state 357 | // object's storage prefixed with it's address. 358 | func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { 359 | prefix := so.Address().Bytes() 360 | compositeKey := make([]byte, len(prefix)+len(key)) 361 | 362 | copy(compositeKey, prefix) 363 | copy(compositeKey[len(prefix):], key) 364 | 365 | return ethcrypto.Keccak256Hash(compositeKey) 366 | } 367 | -------------------------------------------------------------------------------- /x/evm/types/statedb.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "sort" 7 | "sync" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/cosmos/cosmos-sdk/x/auth" 11 | 12 | ethcmn "github.com/ethereum/go-ethereum/common" 13 | ethstate "github.com/ethereum/go-ethereum/core/state" 14 | ethtypes "github.com/ethereum/go-ethereum/core/types" 15 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 16 | ) 17 | 18 | var ( 19 | _ ethstate.StateDB = (*CommitStateDB)(nil) 20 | 21 | zeroBalance = sdk.ZeroInt().BigInt() 22 | ) 23 | 24 | // CommitStateDB implements the Geth state.StateDB interface. Instead of using 25 | // a trie and database for querying and persistence, the Keeper uses KVStores 26 | // and an account mapper is used to facilitate state transitions. 27 | // 28 | // TODO: This implementation is subject to change in regards to its statefull 29 | // manner. In otherwords, how this relates to the keeper in this module. 30 | type CommitStateDB struct { 31 | // TODO: We need to store the context as part of the structure itself opposed 32 | // to being passed as a parameter (as it should be) in order to implement the 33 | // StateDB interface. Perhaps there is a better way. 34 | ctx sdk.Context 35 | 36 | ak auth.AccountKeeper 37 | storageKey sdk.StoreKey 38 | codeKey sdk.StoreKey 39 | 40 | // maps that hold 'live' objects, which will get modified while processing a 41 | // state transition 42 | stateObjects map[ethcmn.Address]*stateObject 43 | stateObjectsDirty map[ethcmn.Address]struct{} 44 | 45 | // The refund counter, also used by state transitioning. 46 | refund uint64 47 | 48 | thash, bhash ethcmn.Hash 49 | txIndex int 50 | logs map[ethcmn.Hash][]*ethtypes.Log 51 | logSize uint 52 | 53 | // TODO: Determine if we actually need this as we do not need preimages in 54 | // the SDK, but it seems to be used elsewhere in Geth. 55 | preimages map[ethcmn.Hash][]byte 56 | 57 | // DB error. 58 | // State objects are used by the consensus core and VM which are 59 | // unable to deal with database-level errors. Any error that occurs 60 | // during a database read is memo-ized here and will eventually be returned 61 | // by StateDB.Commit. 62 | dbErr error 63 | 64 | // Journal of state modifications. This is the backbone of 65 | // Snapshot and RevertToSnapshot. 66 | journal *journal 67 | validRevisions []ethstate.Revision 68 | nextRevisionID int 69 | 70 | // mutex for state deep copying 71 | lock sync.Mutex 72 | } 73 | 74 | // NewCommitStateDB returns a reference to a newly initialized CommitStateDB 75 | // which implements Geth's state.StateDB interface. 76 | // 77 | // CONTRACT: Stores used for state must be cache-wrapped as the ordering of the 78 | // key/value space matters in determining the merkle root. 79 | func NewCommitStateDB(ctx sdk.Context, ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { 80 | return &CommitStateDB{ 81 | ctx: ctx, 82 | ak: ak, 83 | storageKey: storageKey, 84 | codeKey: codeKey, 85 | stateObjects: make(map[ethcmn.Address]*stateObject), 86 | stateObjectsDirty: make(map[ethcmn.Address]struct{}), 87 | logs: make(map[ethcmn.Hash][]*ethtypes.Log), 88 | preimages: make(map[ethcmn.Hash][]byte), 89 | journal: newJournal(), 90 | }, nil 91 | } 92 | 93 | // ---------------------------------------------------------------------------- 94 | // Setters 95 | // ---------------------------------------------------------------------------- 96 | 97 | // SetBalance sets the balance of an account. 98 | func (csdb *CommitStateDB) SetBalance(addr ethcmn.Address, amount *big.Int) { 99 | so := csdb.GetOrNewStateObject(addr) 100 | if so != nil { 101 | so.SetBalance(amount) 102 | } 103 | } 104 | 105 | // AddBalance adds amount to the account associated with addr. 106 | func (csdb *CommitStateDB) AddBalance(addr ethcmn.Address, amount *big.Int) { 107 | so := csdb.GetOrNewStateObject(addr) 108 | if so != nil { 109 | so.AddBalance(amount) 110 | } 111 | } 112 | 113 | // SubBalance subtracts amount from the account associated with addr. 114 | func (csdb *CommitStateDB) SubBalance(addr ethcmn.Address, amount *big.Int) { 115 | so := csdb.GetOrNewStateObject(addr) 116 | if so != nil { 117 | so.SubBalance(amount) 118 | } 119 | } 120 | 121 | // SetNonce sets the nonce (sequence number) of an account. 122 | func (csdb *CommitStateDB) SetNonce(addr ethcmn.Address, nonce uint64) { 123 | so := csdb.GetOrNewStateObject(addr) 124 | if so != nil { 125 | so.SetNonce(nonce) 126 | } 127 | } 128 | 129 | // SetState sets the storage state with a key, value pair for an account. 130 | func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash) { 131 | so := csdb.GetOrNewStateObject(addr) 132 | if so != nil { 133 | so.SetState(nil, key, value) 134 | } 135 | } 136 | 137 | // SetCode sets the code for a given account. 138 | func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) { 139 | so := csdb.GetOrNewStateObject(addr) 140 | if so != nil { 141 | so.SetCode(ethcrypto.Keccak256Hash(code), code) 142 | } 143 | } 144 | 145 | // AddLog adds a new log to the state and sets the log metadata from the state. 146 | func (csdb *CommitStateDB) AddLog(log *ethtypes.Log) { 147 | csdb.journal.append(addLogChange{txhash: csdb.thash}) 148 | 149 | log.TxHash = csdb.thash 150 | log.BlockHash = csdb.bhash 151 | log.TxIndex = uint(csdb.txIndex) 152 | log.Index = csdb.logSize 153 | csdb.logs[csdb.thash] = append(csdb.logs[csdb.thash], log) 154 | csdb.logSize++ 155 | } 156 | 157 | // AddPreimage records a SHA3 preimage seen by the VM. 158 | func (csdb *CommitStateDB) AddPreimage(hash ethcmn.Hash, preimage []byte) { 159 | if _, ok := csdb.preimages[hash]; !ok { 160 | csdb.journal.append(addPreimageChange{hash: hash}) 161 | 162 | pi := make([]byte, len(preimage)) 163 | copy(pi, preimage) 164 | csdb.preimages[hash] = pi 165 | } 166 | } 167 | 168 | // AddRefund adds gas to the refund counter. 169 | func (csdb *CommitStateDB) AddRefund(gas uint64) { 170 | csdb.journal.append(refundChange{prev: csdb.refund}) 171 | csdb.refund += gas 172 | } 173 | 174 | // SubRefund removes gas from the refund counter. It will panic if the refund 175 | // counter goes below zero. 176 | func (csdb *CommitStateDB) SubRefund(gas uint64) { 177 | csdb.journal.append(refundChange{prev: csdb.refund}) 178 | if gas > csdb.refund { 179 | panic("refund counter below zero") 180 | } 181 | 182 | csdb.refund -= gas 183 | } 184 | 185 | // ---------------------------------------------------------------------------- 186 | // Getters 187 | // ---------------------------------------------------------------------------- 188 | 189 | // GetBalance retrieves the balance from the given address or 0 if object not 190 | // found. 191 | func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int { 192 | so := csdb.getStateObject(addr) 193 | if so != nil { 194 | return so.Balance() 195 | } 196 | 197 | return zeroBalance 198 | } 199 | 200 | // GetNonce returns the nonce (sequence number) for a given account. 201 | func (csdb *CommitStateDB) GetNonce(addr ethcmn.Address) uint64 { 202 | so := csdb.getStateObject(addr) 203 | if so != nil { 204 | return so.Nonce() 205 | } 206 | 207 | return 0 208 | } 209 | 210 | // GetCode returns the code for a given account. 211 | func (csdb *CommitStateDB) GetCode(addr ethcmn.Address) []byte { 212 | so := csdb.getStateObject(addr) 213 | if so != nil { 214 | return so.Code(nil) 215 | } 216 | 217 | return nil 218 | } 219 | 220 | // GetCodeSize returns the code size for a given account. 221 | func (csdb *CommitStateDB) GetCodeSize(addr ethcmn.Address) int { 222 | so := csdb.getStateObject(addr) 223 | if so == nil { 224 | return 0 225 | } 226 | 227 | if so.code != nil { 228 | return len(so.code) 229 | } 230 | 231 | // TODO: we may need to cache these lookups directly 232 | return len(so.Code(nil)) 233 | } 234 | 235 | // GetCodeHash returns the code hash for a given account. 236 | func (csdb *CommitStateDB) GetCodeHash(addr ethcmn.Address) ethcmn.Hash { 237 | so := csdb.getStateObject(addr) 238 | if so == nil { 239 | return ethcmn.Hash{} 240 | } 241 | 242 | return ethcmn.BytesToHash(so.CodeHash()) 243 | } 244 | 245 | // GetState retrieves a value from the given account's storage store. 246 | func (csdb *CommitStateDB) GetState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { 247 | so := csdb.getStateObject(addr) 248 | if so != nil { 249 | return so.GetState(nil, hash) 250 | } 251 | 252 | return ethcmn.Hash{} 253 | } 254 | 255 | // GetCommittedState retrieves a value from the given account's committed 256 | // storage. 257 | func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { 258 | so := csdb.getStateObject(addr) 259 | if so != nil { 260 | return so.GetCommittedState(nil, hash) 261 | } 262 | 263 | return ethcmn.Hash{} 264 | } 265 | 266 | // GetLogs returns the current logs for a given hash in the state. 267 | func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) []*ethtypes.Log { 268 | return csdb.logs[hash] 269 | } 270 | 271 | // Logs returns all the current logs in the state. 272 | func (csdb *CommitStateDB) Logs() []*ethtypes.Log { 273 | var logs []*ethtypes.Log 274 | for _, lgs := range csdb.logs { 275 | logs = append(logs, lgs...) 276 | } 277 | 278 | return logs 279 | } 280 | 281 | // GetRefund returns the current value of the refund counter. 282 | func (csdb *CommitStateDB) GetRefund() uint64 { 283 | return csdb.refund 284 | } 285 | 286 | // Preimages returns a list of SHA3 preimages that have been submitted. 287 | func (csdb *CommitStateDB) Preimages() map[ethcmn.Hash][]byte { 288 | return csdb.preimages 289 | } 290 | 291 | // HasSuicided returns if the given account for the specified address has been 292 | // killed. 293 | func (csdb *CommitStateDB) HasSuicided(addr ethcmn.Address) bool { 294 | so := csdb.getStateObject(addr) 295 | if so != nil { 296 | return so.suicided 297 | } 298 | 299 | return false 300 | } 301 | 302 | // StorageTrie returns nil as the state in Ethermint does not use a direct 303 | // storage trie. 304 | func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { 305 | return nil 306 | } 307 | 308 | // ---------------------------------------------------------------------------- 309 | // Persistence 310 | // ---------------------------------------------------------------------------- 311 | 312 | // Commit writes the state to the appropriate KVStores. For each state object 313 | // in the cache, it will either be removed, or have it's code set and/or it's 314 | // state (storage) updated. In addition, the state object (account) itself will 315 | // be written. Finally, the root hash (version) will be returned. 316 | func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, err error) { 317 | defer csdb.clearJournalAndRefund() 318 | 319 | // remove dirty state object entries based on the journal 320 | for addr := range csdb.journal.dirties { 321 | csdb.stateObjectsDirty[addr] = struct{}{} 322 | } 323 | 324 | // set the state objects 325 | for addr, so := range csdb.stateObjects { 326 | _, isDirty := csdb.stateObjectsDirty[addr] 327 | 328 | switch { 329 | case so.suicided || (isDirty && deleteEmptyObjects && so.empty()): 330 | // If the state object has been removed, don't bother syncing it and just 331 | // remove it from the store. 332 | csdb.deleteStateObject(so) 333 | 334 | case isDirty: 335 | // write any contract code associated with the state object 336 | if so.code != nil && so.dirtyCode { 337 | so.commitCode() 338 | so.dirtyCode = false 339 | } 340 | 341 | // update the object in the KVStore 342 | csdb.updateStateObject(so) 343 | } 344 | 345 | delete(csdb.stateObjectsDirty, addr) 346 | } 347 | 348 | // NOTE: Ethereum returns the trie merkle root here, but as commitment 349 | // actually happens in the BaseApp at EndBlocker, we do not know the root at 350 | // this time. 351 | return 352 | } 353 | 354 | // Finalize finalizes the state objects (accounts) state by setting their state, 355 | // removing the csdb destructed objects and clearing the journal as well as the 356 | // refunds. 357 | func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { 358 | for addr := range csdb.journal.dirties { 359 | so, exist := csdb.stateObjects[addr] 360 | if !exist { 361 | // ripeMD is 'touched' at block 1714175, in tx: 362 | // 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 363 | // 364 | // That tx goes out of gas, and although the notion of 'touched' does not 365 | // exist there, the touch-event will still be recorded in the journal. 366 | // Since ripeMD is a special snowflake, it will persist in the journal even 367 | // though the journal is reverted. In this special circumstance, it may 368 | // exist in journal.dirties but not in stateObjects. Thus, we can safely 369 | // ignore it here. 370 | continue 371 | } 372 | 373 | if so.suicided || (deleteEmptyObjects && so.empty()) { 374 | csdb.deleteStateObject(so) 375 | } else { 376 | // Set all the dirty state storage items for the state object in the 377 | // KVStore and finally set the account in the account mapper. 378 | so.commitState() 379 | csdb.updateStateObject(so) 380 | } 381 | 382 | csdb.stateObjectsDirty[addr] = struct{}{} 383 | } 384 | 385 | // invalidate journal because reverting across transactions is not allowed 386 | csdb.clearJournalAndRefund() 387 | } 388 | 389 | // IntermediateRoot returns the current root hash of the state. It is called in 390 | // between transactions to get the root hash that goes into transaction 391 | // receipts. 392 | // 393 | // NOTE: The SDK has not concept or method of getting any intermediate merkle 394 | // root as commitment of the merkle-ized tree doesn't happen until the 395 | // BaseApps' EndBlocker. 396 | func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash { 397 | csdb.Finalize(deleteEmptyObjects) 398 | 399 | return ethcmn.Hash{} 400 | } 401 | 402 | // updateStateObject writes the given state object to the store. 403 | func (csdb *CommitStateDB) updateStateObject(so *stateObject) { 404 | csdb.ak.SetAccount(csdb.ctx, so.account) 405 | } 406 | 407 | // deleteStateObject removes the given state object from the state store. 408 | func (csdb *CommitStateDB) deleteStateObject(so *stateObject) { 409 | so.deleted = true 410 | csdb.ak.RemoveAccount(csdb.ctx, so.account) 411 | } 412 | 413 | // ---------------------------------------------------------------------------- 414 | // Snapshotting 415 | // ---------------------------------------------------------------------------- 416 | 417 | // Snapshot returns an identifier for the current revision of the state. 418 | func (csdb *CommitStateDB) Snapshot() int { 419 | id := csdb.nextRevisionID 420 | csdb.nextRevisionID++ 421 | 422 | csdb.validRevisions = append( 423 | csdb.validRevisions, 424 | ethstate.Revision{ 425 | ID: id, 426 | JournalIndex: csdb.journal.length(), 427 | }, 428 | ) 429 | 430 | return id 431 | } 432 | 433 | // RevertToSnapshot reverts all state changes made since the given revision. 434 | func (csdb *CommitStateDB) RevertToSnapshot(revID int) { 435 | // find the snapshot in the stack of valid snapshots 436 | idx := sort.Search(len(csdb.validRevisions), func(i int) bool { 437 | return csdb.validRevisions[i].ID >= revID 438 | }) 439 | 440 | if idx == len(csdb.validRevisions) || csdb.validRevisions[idx].ID != revID { 441 | panic(fmt.Errorf("revision ID %v cannot be reverted", revID)) 442 | } 443 | 444 | snapshot := csdb.validRevisions[idx].JournalIndex 445 | 446 | // replay the journal to undo changes and remove invalidated snapshots 447 | csdb.journal.revert(csdb, snapshot) 448 | csdb.validRevisions = csdb.validRevisions[:idx] 449 | } 450 | 451 | // ---------------------------------------------------------------------------- 452 | // Auxiliary 453 | // ---------------------------------------------------------------------------- 454 | 455 | // Database retrieves the low level database supporting the lower level trie 456 | // ops. It is not used in Ethermint, so it returns nil. 457 | func (csdb *CommitStateDB) Database() ethstate.Database { 458 | return nil 459 | } 460 | 461 | // Empty returns whether the state object is either non-existent or empty 462 | // according to the EIP161 specification (balance = nonce = code = 0). 463 | func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool { 464 | so := csdb.getStateObject(addr) 465 | return so == nil || so.empty() 466 | } 467 | 468 | // Exist reports whether the given account address exists in the state. Notably, 469 | // this also returns true for suicided accounts. 470 | func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool { 471 | return csdb.getStateObject(addr) != nil 472 | } 473 | 474 | // Error returns the first non-nil error the StateDB encountered. 475 | func (csdb *CommitStateDB) Error() error { 476 | return csdb.dbErr 477 | } 478 | 479 | // Suicide marks the given account as suicided and clears the account balance. 480 | // 481 | // The account's state object is still available until the state is committed, 482 | // getStateObject will return a non-nil account after Suicide. 483 | func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool { 484 | so := csdb.getStateObject(addr) 485 | if so == nil { 486 | return false 487 | } 488 | 489 | csdb.journal.append(suicideChange{ 490 | account: &addr, 491 | prev: so.suicided, 492 | prevBalance: sdk.NewIntFromBigInt(so.Balance()), 493 | }) 494 | 495 | so.markSuicided() 496 | so.SetBalance(new(big.Int)) 497 | 498 | return true 499 | } 500 | 501 | // Reset clears out all ephemeral state objects from the state db, but keeps 502 | // the underlying account mapper and store keys to avoid reloading data for the 503 | // next operations. 504 | func (csdb *CommitStateDB) Reset(root ethcmn.Hash) error { 505 | csdb.stateObjects = make(map[ethcmn.Address]*stateObject) 506 | csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) 507 | csdb.thash = ethcmn.Hash{} 508 | csdb.bhash = ethcmn.Hash{} 509 | csdb.txIndex = 0 510 | csdb.logs = make(map[ethcmn.Hash][]*ethtypes.Log) 511 | csdb.logSize = 0 512 | csdb.preimages = make(map[ethcmn.Hash][]byte) 513 | 514 | csdb.clearJournalAndRefund() 515 | return nil 516 | } 517 | 518 | func (csdb *CommitStateDB) clearJournalAndRefund() { 519 | csdb.journal = newJournal() 520 | csdb.validRevisions = csdb.validRevisions[:0] 521 | csdb.refund = 0 522 | } 523 | 524 | // Prepare sets the current transaction hash and index and block hash which is 525 | // used when the EVM emits new state logs. 526 | func (csdb *CommitStateDB) Prepare(thash, bhash ethcmn.Hash, txi int) { 527 | csdb.thash = thash 528 | csdb.bhash = bhash 529 | csdb.txIndex = txi 530 | } 531 | 532 | // CreateAccount explicitly creates a state object. If a state object with the 533 | // address already exists the balance is carried over to the new account. 534 | // 535 | // CreateAccount is called during the EVM CREATE operation. The situation might 536 | // arise that a contract does the following: 537 | // 538 | // 1. sends funds to sha(account ++ (nonce + 1)) 539 | // 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) 540 | // 541 | // Carrying over the balance ensures that Ether doesn't disappear. 542 | func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) { 543 | newobj, prevobj := csdb.createObject(addr) 544 | if prevobj != nil { 545 | newobj.setBalance(sdk.NewIntFromBigInt(prevobj.Balance())) 546 | } 547 | } 548 | 549 | // Copy creates a deep, independent copy of the state. 550 | // 551 | // NOTE: Snapshots of the copied state cannot be applied to the copy. 552 | func (csdb *CommitStateDB) Copy() ethstate.StateDB { 553 | csdb.lock.Lock() 554 | defer csdb.lock.Unlock() 555 | 556 | // copy all the basic fields, initialize the memory ones 557 | state := &CommitStateDB{ 558 | ctx: csdb.ctx, 559 | ak: csdb.ak, 560 | storageKey: csdb.storageKey, 561 | codeKey: csdb.codeKey, 562 | stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)), 563 | stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)), 564 | refund: csdb.refund, 565 | logs: make(map[ethcmn.Hash][]*ethtypes.Log, len(csdb.logs)), 566 | logSize: csdb.logSize, 567 | preimages: make(map[ethcmn.Hash][]byte), 568 | journal: newJournal(), 569 | } 570 | 571 | // copy the dirty states, logs, and preimages 572 | for addr := range csdb.journal.dirties { 573 | // There is a case where an object is in the journal but not in the 574 | // stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we 575 | // need to check for nil. 576 | // 577 | // Ref: https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527 578 | if object, exist := csdb.stateObjects[addr]; exist { 579 | state.stateObjects[addr] = object.deepCopy(state) 580 | state.stateObjectsDirty[addr] = struct{}{} 581 | } 582 | } 583 | 584 | // Above, we don't copy the actual journal. This means that if the copy is 585 | // copied, the loop above will be a no-op, since the copy's journal is empty. 586 | // Thus, here we iterate over stateObjects, to enable copies of copies. 587 | for addr := range csdb.stateObjectsDirty { 588 | if _, exist := state.stateObjects[addr]; !exist { 589 | state.stateObjects[addr] = csdb.stateObjects[addr].deepCopy(state) 590 | state.stateObjectsDirty[addr] = struct{}{} 591 | } 592 | } 593 | 594 | // copy logs 595 | for hash, logs := range csdb.logs { 596 | cpy := make([]*ethtypes.Log, len(logs)) 597 | for i, l := range logs { 598 | cpy[i] = new(ethtypes.Log) 599 | *cpy[i] = *l 600 | } 601 | state.logs[hash] = cpy 602 | } 603 | 604 | // copy pre-images 605 | for hash, preimage := range csdb.preimages { 606 | state.preimages[hash] = preimage 607 | } 608 | 609 | return state 610 | } 611 | 612 | // ForEachStorage iterates over each storage items, all invokes the provided 613 | // callback on each key, value pair . 614 | func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) { 615 | so := csdb.getStateObject(addr) 616 | if so == nil { 617 | return 618 | } 619 | 620 | store := csdb.ctx.KVStore(csdb.storageKey) 621 | iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes()) 622 | 623 | for ; iter.Valid(); iter.Next() { 624 | key := ethcmn.BytesToHash(iter.Key()) 625 | value := iter.Value() 626 | 627 | if value, dirty := so.dirtyStorage[key]; dirty { 628 | cb(key, value) 629 | continue 630 | } 631 | 632 | cb(key, ethcmn.BytesToHash(value)) 633 | } 634 | 635 | iter.Close() 636 | } 637 | 638 | // GetOrNewStateObject retrieves a state object or create a new state object if 639 | // nil. 640 | func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) ethstate.StateObject { 641 | so := csdb.getStateObject(addr) 642 | if so == nil || so.deleted { 643 | so, _ = csdb.createObject(addr) 644 | } 645 | 646 | return so 647 | } 648 | 649 | // createObject creates a new state object. If there is an existing account with 650 | // the given address, it is overwritten and returned as the second return value. 651 | func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) { 652 | prevObj = csdb.getStateObject(addr) 653 | 654 | acc := csdb.ak.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) 655 | newObj = newObject(csdb, acc) 656 | newObj.setNonce(0) // sets the object to dirty 657 | 658 | if prevObj == nil { 659 | csdb.journal.append(createObjectChange{account: &addr}) 660 | } else { 661 | csdb.journal.append(resetObjectChange{prev: prevObj}) 662 | } 663 | 664 | csdb.setStateObject(newObj) 665 | return newObj, prevObj 666 | } 667 | 668 | // setError remembers the first non-nil error it is called with. 669 | func (csdb *CommitStateDB) setError(err error) { 670 | if csdb.dbErr == nil { 671 | csdb.dbErr = err 672 | } 673 | } 674 | 675 | // getStateObject attempts to retrieve a state object given by the address. 676 | // Returns nil and sets an error if not found. 677 | func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { 678 | // prefer 'live' (cached) objects 679 | if so := csdb.stateObjects[addr]; so != nil { 680 | if so.deleted { 681 | return nil 682 | } 683 | 684 | return so 685 | } 686 | 687 | // otherwise, attempt to fetch the account from the account mapper 688 | acc := csdb.ak.GetAccount(csdb.ctx, addr.Bytes()) 689 | if acc == nil { 690 | csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes())) 691 | return nil 692 | } 693 | 694 | // insert the state object into the live set 695 | so := newObject(csdb, acc) 696 | csdb.setStateObject(so) 697 | 698 | return so 699 | } 700 | 701 | func (csdb *CommitStateDB) setStateObject(so *stateObject) { 702 | csdb.stateObjects[so.Address()] = so 703 | } 704 | -------------------------------------------------------------------------------- /x/evm/types/utils.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/ethermint/crypto" 7 | ethcmn "github.com/ethereum/go-ethereum/common" 8 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 9 | ethsha "github.com/ethereum/go-ethereum/crypto/sha3" 10 | "github.com/ethereum/go-ethereum/rlp" 11 | 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | // GenerateAddress generates an Ethereum address. 16 | func GenerateEthAddress() ethcmn.Address { 17 | priv, err := crypto.GenerateKey() 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | return ethcrypto.PubkeyToAddress(priv.PublicKey) 23 | } 24 | 25 | // ValidateSigner attempts to validate a signer for a given slice of bytes over 26 | // which a signature and signer is given. An error is returned if address 27 | // derived from the signature and bytes signed does not match the given signer. 28 | func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error { 29 | pk, err := ethcrypto.SigToPub(signBytes, sig) 30 | 31 | if err != nil { 32 | return errors.Wrap(err, "failed to derive public key from signature") 33 | } else if ethcrypto.PubkeyToAddress(*pk) != signer { 34 | return fmt.Errorf("invalid signature for signer: %s", signer) 35 | } 36 | 37 | return nil 38 | } 39 | 40 | func rlpHash(x interface{}) (hash ethcmn.Hash) { 41 | hasher := ethsha.NewKeccak256() 42 | 43 | rlp.Encode(hasher, x) 44 | hasher.Sum(hash[:0]) 45 | 46 | return 47 | } 48 | --------------------------------------------------------------------------------