Fix merge conflicts

This commit is contained in:
Aleksandr Bezobchuk 2018-08-31 14:26:45 -04:00
commit e2c0645a10
26 changed files with 1391 additions and 569 deletions

277
Gopkg.lock generated
View File

@ -3,22 +3,29 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:60d3b49df18861c92ac49cce49e49f61b3ec927e5b7f39c5ae1128ec5c197b98"
name = "github.com/aristanetworks/goarista" name = "github.com/aristanetworks/goarista"
packages = ["monotime"] packages = ["monotime"]
revision = "b2d71c282dc706f4b4f6c15b65810e1202ecd53f" pruneopts = "T"
revision = "fb622b9b46608fdb39d36447f4d8ef52fe37fc3d"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:b9f5e0f033febe59a62d01e78486c0dd9e4afc9ac5d240aee6ce78a927142e8b"
name = "github.com/btcsuite/btcd" name = "github.com/btcsuite/btcd"
packages = ["btcec"] packages = ["btcec"]
revision = "cf05f92c3f815bbd5091ed6c73eff51f7b1945e8" pruneopts = "T"
revision = "79e00513b1011888b1e675157ab89f527f901cae"
[[projects]] [[projects]]
digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533"
name = "github.com/btcsuite/btcutil" name = "github.com/btcsuite/btcutil"
packages = ["bech32"] packages = ["bech32"]
pruneopts = "T"
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]] [[projects]]
digest = "1:bc28e755cf6a9fd8e65497514d20c4907973e7a6a6409d30ead3fd37bfeb19a9"
name = "github.com/cosmos/cosmos-sdk" name = "github.com/cosmos/cosmos-sdk"
packages = [ packages = [
"baseapp", "baseapp",
@ -27,26 +34,47 @@
"version", "version",
"wire", "wire",
"x/auth", "x/auth",
"x/stake/types" "x/bank",
"x/gov",
"x/gov/tags",
"x/mock",
"x/params",
"x/slashing",
"x/stake",
"x/stake/keeper",
"x/stake/tags",
"x/stake/types",
] ]
revision = "23e3d5ac12145c02fcb4b4767d7dfccad782aee5" pruneopts = "T"
version = "v0.23.1" revision = "1c38c70468ec721e3a555ba2f3bf5f9da31f0cc9"
version = "v0.24.2"
[[projects]] [[projects]]
digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a"
name = "github.com/davecgh/go-spew" name = "github.com/davecgh/go-spew"
packages = ["spew"] packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76" pruneopts = "T"
version = "v1.1.0" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:67d0b50be0549e610017cb91e0b0b745ec0cad7c613bc8e18ff2d1c1fc8825a7"
name = "github.com/edsrzf/mmap-go" name = "github.com/edsrzf/mmap-go"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e" revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e"
[[projects]] [[projects]]
digest = "1:3238a0c355a81640974751f7d3bab21bf91035165f75c2c457959425c0422a4b"
name = "github.com/ethereum/go-ethereum" name = "github.com/ethereum/go-ethereum"
packages = [ packages = [
".",
"accounts",
"accounts/abi",
"accounts/keystore",
"accounts/usbwallet",
"accounts/usbwallet/internal/trezor",
"common", "common",
"common/bitutil", "common/bitutil",
"common/hexutil", "common/hexutil",
@ -64,44 +92,61 @@
"crypto/bn256", "crypto/bn256",
"crypto/bn256/cloudflare", "crypto/bn256/cloudflare",
"crypto/bn256/google", "crypto/bn256/google",
"crypto/ecies",
"crypto/randentropy",
"crypto/secp256k1", "crypto/secp256k1",
"crypto/sha3", "crypto/sha3",
"eth/downloader",
"ethdb", "ethdb",
"event", "event",
"internal/ethapi",
"log", "log",
"metrics", "metrics",
"p2p",
"p2p/discover",
"p2p/discv5",
"p2p/nat",
"p2p/netutil", "p2p/netutil",
"params", "params",
"rlp", "rlp",
"rpc", "rpc",
"trie" "signer/core",
"trie",
] ]
pruneopts = "T"
revision = "dea1ce052a10cd7d401a5c04f83f371a06fe293c" revision = "dea1ce052a10cd7d401a5c04f83f371a06fe293c"
version = "v1.8.11" version = "v1.8.11"
[[projects]] [[projects]]
digest = "1:0b9c3ad6c948d57a379da9c4e1cdd989b1c73ddc5ec8673f52a9539ce60a109b"
name = "github.com/go-kit/kit" name = "github.com/go-kit/kit"
packages = [ packages = [
"log", "log",
"log/level", "log/level",
"log/term" "log/term",
] ]
pruneopts = "T"
revision = "4dc7be5d2d12881735283bcab7352178e190fc71" revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0" version = "v0.6.0"
[[projects]] [[projects]]
digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659"
name = "github.com/go-logfmt/logfmt" name = "github.com/go-logfmt/logfmt"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0" version = "v0.3.0"
[[projects]] [[projects]]
digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
name = "github.com/go-stack/stack" name = "github.com/go-stack/stack"
packages = ["."] packages = ["."]
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" pruneopts = "T"
version = "v1.7.0" revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
version = "v1.8.0"
[[projects]] [[projects]]
digest = "1:da39f4a22829ca95e63566208e0ea42d6f055f41dff1b14fdab88d88f62df653"
name = "github.com/gogo/protobuf" name = "github.com/gogo/protobuf"
packages = [ packages = [
"gogoproto", "gogoproto",
@ -109,96 +154,172 @@
"proto", "proto",
"protoc-gen-gogo/descriptor", "protoc-gen-gogo/descriptor",
"sortkeys", "sortkeys",
"types" "types",
] ]
pruneopts = "T"
revision = "636bf0302bc95575d69441b25a2603156ffdddf1" revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
version = "v1.1.1" version = "v1.1.1"
[[projects]] [[projects]]
digest = "1:832e17df5ff8bbe0e0693d2fb46c5e53f96c662ee804049ce3ab6557df74e3ab"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
packages = [ packages = [
"proto", "proto",
"protoc-gen-go/descriptor",
"ptypes", "ptypes",
"ptypes/any", "ptypes/any",
"ptypes/duration", "ptypes/duration",
"ptypes/timestamp" "ptypes/timestamp",
] ]
pruneopts = "T"
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0" version = "v1.1.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:6027b20c168728321bd99ad01f35118eded457b01c03e647a84833ab331f2f5b"
name = "github.com/golang/snappy" name = "github.com/golang/snappy"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]] [[projects]]
digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db"
name = "github.com/hashicorp/golang-lru" name = "github.com/hashicorp/golang-lru"
packages = [ packages = [
".", ".",
"simplelru" "simplelru",
] ]
pruneopts = "T"
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3" revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
[[projects]] [[projects]]
branch = "master"
digest = "1:202e4a1a283dd740ca9d131787e73bb9d69611a01ef86e82ed262e035b0dd792"
name = "github.com/huin/goupnp"
packages = [
".",
"dcps/internetgateway1",
"dcps/internetgateway2",
"httpu",
"scpd",
"soap",
"ssdp",
]
pruneopts = "T"
revision = "1395d1447324cbea88d249fbfcfd70ea878fdfca"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap" name = "github.com/inconshreveable/mousetrap"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0" version = "v1.0"
[[projects]]
digest = "1:32b82e71cf24f8b78323e0d7903c4b90278486283965aa2a19b1ea13763b8f34"
name = "github.com/jackpal/go-nat-pmp"
packages = ["."]
pruneopts = "T"
revision = "c9cfead9f2a36ddf3daa40ba269aa7f4bbba6b62"
version = "v1.0.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:dc6b1a6801b3055e9bd3da4cd1e568606eb48118cc6f28e947783aa5d998ad74"
name = "github.com/jmhodges/levigo" name = "github.com/jmhodges/levigo"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:01f1325bf6f105bb633029a2d8b63f1a2357181e60af8dadabf14ad2e84398c5"
name = "github.com/karalabe/hid"
packages = ["."]
pruneopts = "T"
revision = "2b4488a37358b7283de4f9622553e85ebbe73125"
[[projects]]
branch = "master"
digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72"
name = "github.com/kr/logfmt" name = "github.com/kr/logfmt"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]] [[projects]]
digest = "1:361de06aa7ae272616cbe71c3994a654cc6316324e30998e650f7765b20c5b33"
name = "github.com/pborman/uuid"
packages = ["."]
pruneopts = "T"
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
version = "v1.1"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors" name = "github.com/pkg/errors"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d" revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0" version = "v0.8.0"
[[projects]] [[projects]]
digest = "1:22aa691fe0213cb5c07d103f9effebcb7ad04bee45a0ce5fe5369d0ca2ec3a1f"
name = "github.com/pmezard/go-difflib" name = "github.com/pmezard/go-difflib"
packages = ["difflib"] packages = ["difflib"]
pruneopts = "T"
revision = "792786c7400a136282c1664665ae0a8db921c6c2" revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
digest = "1:602081d2a289d1f76ea90b806b0c61c19038d76504e9005ccb969864dbaee339"
name = "github.com/rjeczalik/notify"
packages = ["."]
pruneopts = "T"
revision = "0f065fa99b48b842c3fd3e2c8b194c6f2b69f6b8"
version = "v0.9.1"
[[projects]]
digest = "1:6cae6970d70fc5fe75bf83c48ee33e9c4c561a62d0b033254bee8dd5942b815a"
name = "github.com/rs/cors" name = "github.com/rs/cors"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "3fb1b69b103a84de38a19c3c6ec073dd6caa4d3f" revision = "3fb1b69b103a84de38a19c3c6ec073dd6caa4d3f"
version = "v1.5.0" version = "v1.5.0"
[[projects]] [[projects]]
digest = "1:8be8b3743fc9795ec21bbd3e0fc28ff6234018e1a269b0a7064184be95ac13e0"
name = "github.com/spf13/cobra" name = "github.com/spf13/cobra"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385" revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
version = "v0.0.3" version = "v0.0.3"
[[projects]] [[projects]]
digest = "1:9ba911fe3884995431690e7eb180cf848da0d637ba5f61711783b795d031793f"
name = "github.com/spf13/pflag" name = "github.com/spf13/pflag"
packages = ["."] packages = ["."]
revision = "583c0c0531f06d5278b7d917446061adc344b5cd" pruneopts = "T"
version = "v1.0.1" revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
version = "v1.0.2"
[[projects]] [[projects]]
digest = "1:8f39978e4fb2a11d43cc954f2ab458cb38995d4c1557b6d3a7c8cafe0ec2277c"
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"
packages = [ packages = [
"assert", "assert",
"require" "require",
"suite",
] ]
pruneopts = "T"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1" version = "v1.2.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:ee395d0d8c1719b5a1407f34af93953b4763bacb19a8961aba5b6d312824da41"
name = "github.com/syndtr/goleveldb" name = "github.com/syndtr/goleveldb"
packages = [ packages = [
"leveldb", "leveldb",
@ -212,33 +333,41 @@
"leveldb/opt", "leveldb/opt",
"leveldb/storage", "leveldb/storage",
"leveldb/table", "leveldb/table",
"leveldb/util" "leveldb/util",
] ]
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445" pruneopts = "T"
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:2b15c0442dc80b581ce7028b2e43029d2f3f985da43cb1d55f7bcdeca785bda0"
name = "github.com/tendermint/ed25519" name = "github.com/tendermint/ed25519"
packages = [ packages = [
".", ".",
"edwards25519", "edwards25519",
"extra25519" "extra25519",
] ]
pruneopts = "T"
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]] [[projects]]
digest = "1:0e2addab3f64ece97ca434b2bf2d4e8cb54a4509904a03be8c81da3fc2ddb245"
name = "github.com/tendermint/go-amino" name = "github.com/tendermint/go-amino"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412" revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1" version = "0.10.1"
[[projects]] [[projects]]
digest = "1:bf042d2f7d1252b9dcae8e694e2f0a9b5294cb357c086fd86dc540d2f32c9fdf"
name = "github.com/tendermint/iavl" name = "github.com/tendermint/iavl"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9" revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9"
version = "v0.9.2" version = "v0.9.2"
[[projects]] [[projects]]
digest = "1:5a60cb048b401c0263c227baf8778ecaf038be531707057607949540486874ef"
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
packages = [ packages = [
"abci/server", "abci/server",
@ -255,58 +384,100 @@
"libs/log", "libs/log",
"libs/pubsub", "libs/pubsub",
"libs/pubsub/query", "libs/pubsub/query",
"types" "types",
] ]
revision = "d542d2c3945116697f60451e6a407082c41c3cc9" pruneopts = "T"
version = "v0.22.8" revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e"
version = "v0.23.1-rc0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:da29cbeb9d244918393b37243c008ab7128688fb017c966aaf876587c010bcdd"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ripemd160"] packages = [
revision = "f027049dab0ad238e394a753dba2d14753473a04" "pbkdf2",
"ripemd160",
"scrypt",
"ssh/terminal",
]
pruneopts = "T"
revision = "182538f80094b6a8efaade63a8fd8e0d9d5843dd"
[[projects]] [[projects]]
digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = [ packages = [
"context", "context",
"html",
"html/atom",
"html/charset",
"http/httpguts", "http/httpguts",
"http2", "http2",
"http2/hpack", "http2/hpack",
"idna", "idna",
"internal/timeseries", "internal/timeseries",
"trace", "trace",
"websocket" "websocket",
] ]
pruneopts = "T"
revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f"
[[projects]] [[projects]]
branch = "master"
digest = "1:bfa444982d49ce4ca1360599270a94de12a573ccd3bf04493c79bee09da3170b"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "T"
revision = "fa5fdf94c78965f1aa8423f0cc50b8b8d728b05a"
[[projects]]
digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a"
name = "golang.org/x/text" name = "golang.org/x/text"
packages = [ packages = [
"collate", "collate",
"collate/build", "collate/build",
"encoding",
"encoding/charmap",
"encoding/htmlindex",
"encoding/internal",
"encoding/internal/identifier",
"encoding/japanese",
"encoding/korean",
"encoding/simplifiedchinese",
"encoding/traditionalchinese",
"encoding/unicode",
"internal/colltab", "internal/colltab",
"internal/gen", "internal/gen",
"internal/tag", "internal/tag",
"internal/triegen", "internal/triegen",
"internal/ucd", "internal/ucd",
"internal/utf8internal",
"language", "language",
"runes",
"secure/bidirule", "secure/bidirule",
"transform", "transform",
"unicode/bidi", "unicode/bidi",
"unicode/cldr", "unicode/cldr",
"unicode/norm", "unicode/norm",
"unicode/rangetable" "unicode/rangetable",
] ]
pruneopts = "T"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0" version = "v0.3.0"
[[projects]] [[projects]]
branch = "master"
digest = "1:e43f1cb3f488a0c2be85939c2a594636f60b442a12a196c778bd2d6c9aca3df7"
name = "google.golang.org/genproto" name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"] packages = ["googleapis/rpc/status"]
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200" pruneopts = "T"
revision = "11092d34479b07829b72e10713b159248caf5dad"
[[projects]] [[projects]]
digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770"
name = "google.golang.org/grpc" name = "google.golang.org/grpc"
packages = [ packages = [
".", ".",
@ -333,32 +504,78 @@
"stats", "stats",
"status", "status",
"tap", "tap",
"transport" "transport",
] ]
pruneopts = "T"
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
version = "v1.13.0" version = "v1.13.0"
[[projects]] [[projects]]
digest = "1:3ccd10c863188cfe0d936fcfe6a055c95362e43af8e7039e33baade846928e74"
name = "gopkg.in/fatih/set.v0" name = "gopkg.in/fatih/set.v0"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "57907de300222151a123d29255ed17f5ed43fad3" revision = "57907de300222151a123d29255ed17f5ed43fad3"
version = "v0.1.0" version = "v0.1.0"
[[projects]] [[projects]]
branch = "v2" branch = "v2"
digest = "1:dae137be246befa42ce4b48c0feff2c5796b8a5027139a283f31a21173744410"
name = "gopkg.in/karalabe/cookiejar.v2" name = "gopkg.in/karalabe/cookiejar.v2"
packages = ["collections/prque"] packages = ["collections/prque"]
pruneopts = "T"
revision = "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57" revision = "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57"
[[projects]] [[projects]]
branch = "v2" branch = "v2"
digest = "1:3d3f9391ab615be8655ae0d686a1564f3fec413979bb1aaf018bac1ec1bb1cc7"
name = "gopkg.in/natefinch/npipe.v2" name = "gopkg.in/natefinch/npipe.v2"
packages = ["."] packages = ["."]
pruneopts = "T"
revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6" revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "353f6762943df5f67c2fba13bc530c800977ed507155bb9edc3b44947f0abc6e" input-imports = [
"github.com/cosmos/cosmos-sdk/baseapp",
"github.com/cosmos/cosmos-sdk/store",
"github.com/cosmos/cosmos-sdk/types",
"github.com/cosmos/cosmos-sdk/wire",
"github.com/cosmos/cosmos-sdk/x/auth",
"github.com/cosmos/cosmos-sdk/x/bank",
"github.com/cosmos/cosmos-sdk/x/gov",
"github.com/cosmos/cosmos-sdk/x/params",
"github.com/cosmos/cosmos-sdk/x/slashing",
"github.com/cosmos/cosmos-sdk/x/stake",
"github.com/cosmos/cosmos-sdk/x/stake/types",
"github.com/ethereum/go-ethereum/common",
"github.com/ethereum/go-ethereum/common/hexutil",
"github.com/ethereum/go-ethereum/common/math",
"github.com/ethereum/go-ethereum/consensus",
"github.com/ethereum/go-ethereum/consensus/ethash",
"github.com/ethereum/go-ethereum/consensus/misc",
"github.com/ethereum/go-ethereum/core",
"github.com/ethereum/go-ethereum/core/state",
"github.com/ethereum/go-ethereum/core/types",
"github.com/ethereum/go-ethereum/core/vm",
"github.com/ethereum/go-ethereum/crypto",
"github.com/ethereum/go-ethereum/crypto/sha3",
"github.com/ethereum/go-ethereum/ethdb",
"github.com/ethereum/go-ethereum/params",
"github.com/ethereum/go-ethereum/rlp",
"github.com/ethereum/go-ethereum/rpc",
"github.com/ethereum/go-ethereum/signer/core",
"github.com/ethereum/go-ethereum/trie",
"github.com/hashicorp/golang-lru",
"github.com/pkg/errors",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"github.com/stretchr/testify/suite",
"github.com/tendermint/tendermint/abci/types",
"github.com/tendermint/tendermint/libs/common",
"github.com/tendermint/tendermint/libs/db",
"github.com/tendermint/tendermint/libs/log",
]
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -4,7 +4,7 @@
[[constraint]] [[constraint]]
name = "github.com/cosmos/cosmos-sdk" name = "github.com/cosmos/cosmos-sdk"
version = "=0.23.1" version = "=0.24.2"
[[constraint]] [[constraint]]
name = "github.com/hashicorp/golang-lru" name = "github.com/hashicorp/golang-lru"
@ -15,12 +15,12 @@
version = "~0.0.1" version = "~0.0.1"
[[override]] [[override]]
name = "google.golang.org/genproto" name = "github.com/tendermint/iavl"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200" version = "=v0.9.2"
[[override]] [[override]]
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
version = "=v0.22.8" version = "=v0.23.1-rc0"
[[constraint]] [[constraint]]
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"

View File

@ -28,16 +28,16 @@ all: tools deps install
build: build:
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY).exe ./cmd/ethermintd go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY).exe ./cmd/emintd
go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY).exe ./cmd/ethermintcli go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY).exe ./cmd/emintcli
else else
go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY) ./cmd/ethermintd/ go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY) ./cmd/emintd/
go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY) ./cmd/ethermintcli/ go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY) ./cmd/emintcli/
endif endif
install: install:
go install $(BUILD_FLAGS) ./cmd/ethermintd go install $(BUILD_FLAGS) ./cmd/emintd
go install $(BUILD_FLAGS) ./cmd/ethermintcli go install $(BUILD_FLAGS) ./cmd/emintcli
clean: clean:
@rm -rf ./build ./vendor @rm -rf ./build ./vendor

View File

@ -1,5 +1,5 @@
[![](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) [![CircleCI](https://circleci.com/gh/cosmos/ethermint.svg?style=svg)](https://circleci.com/gh/cosmos/ethermint) [![CircleCI](https://circleci.com/gh/cosmos/ethermint.svg?style=svg)](https://circleci.com/gh/cosmos/ethermint)
[![](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)
# Ethermint # Ethermint
__**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. __**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,18 +13,26 @@ __**WARNING:**__ Ethermint is under VERY ACTIVE DEVELOPMENT and should be treate
### Implementation ### Implementation
- [x] Have a working implementation that can parse and validate the existing ETH Chain and persist it in a Tendermint store #### Completed
- [ ] Benchmark this implementation to ensure performance - Have a working implementation that can parse and validate the existing ETH Chain and persist it in a Tendermint store
- [ ] Allow the Ethermint EVM to interact with other [Cosmos SDK modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/app3.md) - Implement Ethereum transactions in the CosmosSDK
- [ ] Implement the Web3 APIs as a Cosmos Light Client for Ethermint
- [ ] Ethermint is a full Cosmos SDK application and can be deployed as it's own zone #### Current Work
- Implement web3 compatible API layer
- Implement the EVM as a CosmosSDK module
- Allow the Ethermint EVM to interact with other [Cosmos SDK modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/app3.md)
#### Next Steps
- Hard spoon enablement: The ability to export state from `geth` and import token balances into Ethermint
- Ethermint is a functioning Cosmos SDK application and can be deployed as its own zone
- Full web3 compatibility will enable existing Ethereum applications to use Ethermint
### Building Ethermint ### Building Ethermint
To build, execute the following commands: To build, execute the following commands:
```bash ```bash
# To build the binary and put the results in ./build # To build the binary and put the resulting binary in ./build
$ make tools deps build $ make tools deps build
# To build the project and install it in $GOBIN # To build the project and install it in $GOBIN

View File

@ -5,13 +5,19 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/pkg/errors"
"github.com/cosmos/ethermint/handlers" "github.com/cosmos/ethermint/handlers"
"github.com/cosmos/ethermint/types" "github.com/cosmos/ethermint/types"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethparams "github.com/ethereum/go-ethereum/params"
abci "github.com/tendermint/tendermint/abci/types"
tmcmn "github.com/tendermint/tendermint/libs/common" tmcmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db" dbm "github.com/tendermint/tendermint/libs/db"
tmlog "github.com/tendermint/tendermint/libs/log" tmlog "github.com/tendermint/tendermint/libs/log"
@ -29,62 +35,158 @@ type (
*bam.BaseApp *bam.BaseApp
codec *wire.Codec codec *wire.Codec
sealed bool
accountKey *sdk.KVStoreKey accountKey *sdk.KVStoreKey
accountMapper auth.AccountMapper mainKey *sdk.KVStoreKey
// TODO: keys, stores, mappers, and keepers stakeKey *sdk.KVStoreKey
} slashingKey *sdk.KVStoreKey
govKey *sdk.KVStoreKey
feeCollKey *sdk.KVStoreKey
paramsKey *sdk.KVStoreKey
tParamsKey *sdk.TransientStoreKey
// Options is a function signature that provides the ability to modify accountMapper auth.AccountMapper
// options of an EthermintApp during initialization. feeCollKeeper auth.FeeCollectionKeeper
Options func(*EthermintApp) coinKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
}
) )
// NewEthermintApp returns a reference to a new initialized Ethermint // NewEthermintApp returns a reference to a new initialized Ethermint
// application. // application.
func NewEthermintApp( func NewEthermintApp(
logger tmlog.Logger, db dbm.DB, ethChainCfg *ethparams.ChainConfig, logger tmlog.Logger, db dbm.DB, sdkAddr ethcmn.Address, baseAppOpts ...func(*bam.BaseApp),
sdkAddr ethcmn.Address, opts ...Options,
) *EthermintApp { ) *EthermintApp {
codec := CreateCodec() codec := CreateCodec()
app := &EthermintApp{ app := &EthermintApp{
BaseApp: bam.NewBaseApp(appName, codec, logger, db), BaseApp: bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec, sdkAddr), baseAppOpts...),
codec: codec, codec: codec,
accountKey: sdk.NewKVStoreKey("accounts"), accountKey: sdk.NewKVStoreKey("acc"),
mainKey: sdk.NewKVStoreKey("main"),
stakeKey: sdk.NewKVStoreKey("stake"),
slashingKey: sdk.NewKVStoreKey("slashing"),
govKey: sdk.NewKVStoreKey("gov"),
feeCollKey: sdk.NewKVStoreKey("fee"),
paramsKey: sdk.NewKVStoreKey("params"),
tParamsKey: sdk.NewTransientStoreKey("transient_params"),
} }
// set application keepers and mappers
app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount) app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount)
app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.paramsKeeper = params.NewKeeper(app.codec, app.paramsKey)
app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.codec, app.feeCollKey)
app.stakeKeeper = stake.NewKeeper(
app.codec, app.stakeKey, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace),
)
app.govKeeper = gov.NewKeeper(
app.codec, app.govKey, app.paramsKeeper.Setter(), app.coinKeeper,
app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace),
)
app.slashingKeeper = slashing.NewKeeper(
app.codec, app.slashingKey, app.stakeKeeper,
app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace),
)
app.SetTxDecoder(types.TxDecoder(codec, sdkAddr)) // register message handlers
app.SetAnteHandler(handlers.AnteHandler(app.accountMapper)) app.Router().
app.MountStoresIAVL(app.accountKey) // TODO: Do we need to mount bank and IBC handlers? Should be handled
// directly in the EVM.
AddRoute("stake", stake.NewHandler(app.stakeKeeper)).
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)).
AddRoute("gov", gov.NewHandler(app.govKeeper))
for _, opt := range opts { // initialize the underlying ABCI BaseApp
opt(app) app.SetInitChainer(app.initChainer)
} app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(handlers.AnteHandler(app.accountMapper, app.feeCollKeeper))
err := app.LoadLatestVersion(app.accountKey) app.MountStoresIAVL(
if err != nil { app.mainKey, app.accountKey, app.stakeKey, app.slashingKey,
app.govKey, app.feeCollKey, app.paramsKey,
)
app.MountStore(app.tParamsKey, sdk.StoreTypeTransient)
if err := app.LoadLatestVersion(app.accountKey); err != nil {
tmcmn.Exit(err.Error()) tmcmn.Exit(err.Error())
} }
app.seal() app.BaseApp.Seal()
return app return app
} }
// seal seals the Ethermint application and prohibits any future modifications // BeginBlocker signals the beginning of a block. It performs application
// that change critical components. // updates on the start of every block.
func (app *EthermintApp) seal() { func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
app.sealed = true tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
}
// EndBlocker signals the end of a block. It performs application updates on
// the end of every block.
func (app *EthermintApp) EndBlocker(ctx sdk.Context, _ abci.RequestEndBlock) abci.ResponseEndBlock {
tags := gov.EndBlocker(ctx, app.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
}
}
// initChainer initializes the application blockchain with validators and other
// state data from TendermintCore.
func (app *EthermintApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var genesisState GenesisState
stateJSON := req.AppStateBytes
err := app.codec.UnmarshalJSON(stateJSON, &genesisState)
if err != nil {
panic(errors.Wrap(err, "failed to parse application genesis state"))
}
// load the genesis accounts
for _, genAcc := range genesisState.Accounts {
acc := genAcc.ToAccount()
acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx)
app.accountMapper.SetAccount(ctx, acc)
}
// load the genesis stake information
validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
if err != nil {
panic(errors.Wrap(err, "failed to initialize genesis validators"))
}
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
return abci.ResponseInitChain{
Validators: validators,
}
} }
// CreateCodec creates a new amino wire codec and registers all the necessary // CreateCodec creates a new amino wire codec and registers all the necessary
// structures and interfaces needed for the application. // concrete types and interfaces needed for the application.
func CreateCodec() *wire.Codec { func CreateCodec() *wire.Codec {
codec := wire.NewCodec() codec := wire.NewCodec()
// Register other modules, types, and messages...
types.RegisterWire(codec) types.RegisterWire(codec)
auth.RegisterWire(codec)
gov.RegisterWire(codec)
slashing.RegisterWire(codec)
stake.RegisterWire(codec)
wire.RegisterCrypto(codec)
return codec return codec
} }

47
app/genesis.go Normal file
View File

@ -0,0 +1,47 @@
package app
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/ethermint/types"
)
type (
// GenesisState defines the application's genesis state. It contains all the
// information required and accounts to initialize the blockchain.
GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
GovData gov.GenesisState `json:"gov"`
}
// GenesisAccount defines an account to be initialized in the genesis state.
GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Code []byte `json:"code,omitempty"`
Storage types.Storage `json:"storage,omitempty"`
}
)
// NewGenesisAccount returns a reference to a new initialized genesis account.
func NewGenesisAccount(acc *types.Account) GenesisAccount {
return GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
Code: acc.Code,
Storage: acc.Storage,
}
}
// ToAccount converts a genesis account to an initialized Ethermint account.
func (ga *GenesisAccount) ToAccount() (acc *types.Account) {
base := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}
return types.NewAccount(base, ga.Code, ga.Storage)
}

50
docs/intro/README.md Normal file
View File

@ -0,0 +1,50 @@
# Introduction
## What is Ethermint
Ethermint is a high throughput PoS blockchain that is fully compatible and
interoperable with Ethereum. In other words, it allows for running vanilla Ethereum
on top of [Tendermint](https://github.com/tendermint/tendermint) consensus via
the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/). This allows developers
to have all the desired features of Ethereum, while at the same time benefit
from Tendermints PoS implementation. Also, because it is built on top of the
Cosmos SDK, it will be able to exchange value with the rest of the Cosmos Ecosystem.
Heres a glance at some of the key features of Ethermint:
* Web3 compatibility
* High throughput
* Horizontal scalability
* Transaction finality
Ethermint enables these key features through:
* Implementing Tendermint's ABCI application interface to manage the base Blockchain
* Leveraging [modules](https://github.com/cosmos/cosmos-sdk/tree/master/x/) and other mechanisms implemented by the Cosmos SDK
* Utilizing [`geth`](https://github.com/ethereum/go-ethereum) as a library to avoid code reuse and improve maintainability
* Exposing a fully compatible Web3 RPC layer for interacting with the system
The sum of these features allows developers to leverage existing Ethereum ecosystem
tooling and software to seamlessly deploy smart contracts which interact with the rest of the Cosmos
ecosystem!
## In-depth Topics
### Tendermint Core & the Application Blockchain Interface (ABCI)
Tendermint consists of two chief technical components: a blockchain consensus
engine and a generic application interface. The consensus engine, called
Tendermint Core, ensures that the same transactions are recorded on every machine
in the same order. The application interface, called the Application Blockchain
Interface (ABCI), enables the transactions to be processed in any programming
language.
Tendermint has evolved to be a general purpose blockchain consensus engine that
can host arbitrary application states. Since Tendermint can replicate arbitrary
applications, it can be used as a plug-and-play replacement for the consensus
engines of other blockchains. Ethermint is such an example of an ABCI application
replacing Ethereum's PoW via Tendermint's consensus engine.
Another example of a cryptocurrency application built on Tendermint is the Cosmos
network. Tendermint is able to decompose the blockchain design by offering a very
simple API (ie. the ABCI) between the application process and consensus process.

View File

@ -0,0 +1,64 @@
# Transactions
> NOTE: The specification documented below is still highly active in development
and subject to change.
## Routing
Ethermint needs to parse and handle transactions routed for both the EVM and for
the Cosmos hub. We attempt to achieve this by mimicking [Geth's](https://github.com/ethereum/go-ethereum) `Transaction` structure and utilizing
the `Payload` as the potential encoding of a Cosmos-routed transaction. What
designates this encoding, and ultimately routing, is the `Recipient` address --
if this address matches some global unique predefined and configured address,
we regard it as a transaction meant for Cosmos, otherwise, the transaction is a
pure Ethereum transaction and will be executed in the EVM.
For Cosmos routed transactions, the `Transaction.Payload` will contain an [Amino](https://github.com/tendermint/go-amino) encoded embedded transaction that must
implement the `sdk.Tx` interface. Note, the embedding (outer) `Transaction` is
still RLP encoded in order to preserve compatibility with existing tooling. In
addition, at launch, Ethermint will only support the `auth.StdTx` embedded Cosmos
transaction type.
Being that Ethermint implements the Tendermint ABCI application interface, as
transactions are consumed, they are passed through a series of handlers. Once such
handler, `runTx`, is responsible for invoking the `TxDecoder` which performs the
business logic of properly deserializing raw transaction bytes into either an
Ethereum transaction or a Cosmos transaction.
__Note__: Our goal is to utilize Geth as a library, at least as much as possible,
so it should be expected that these types and the operations you may perform on
them will keep in line with Ethereum (e.g. signature algorithms and gas/fees).
In addition, we aim to have existing tooling and frameworks in the Ethereum
ecosystem have 100% compatibility with creating transactions in Ethermint.
## Transactions & Messages
The SDK distinguishes between transactions (`sdk.Tx`) and messages (`sdk.Msg`).
A `sdk.Tx` is a list of `sdk.Msg` wrapped with authentication and fee data. Users
can create messages containing arbitrary information by implementing the `sdk.Msg`
interface.
In Ethermint, the `Transaction` type implements the Cosmos SDK `sdk.Tx` interface.
It addition, it implements the Cosmos SDK `sdk.Msg` interface for the sole purpose
of being to perform basic validation checks in the `BaseApp`. It, however, has
no distinction between transactions and messages.
## Signatures
Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
signatures. A `Transaction` is expected to have a single signature for Ethereum
routed transactions. However, just as in Cosmos, Ethermint will support multiple
signers for embedded Cosmos routed transactions. Signatures over the
`Transaction` type are identical to Ethereum. However, the embedded transaction contains
a canonical signature structure that contains the signature itself and other
information such as an account's sequence number. This, in addition to the chainID,
helps prevent "replay attacks", where the same message could be executed over and
over again.
An embedded transaction's list of signatures must much the unique list of addresses
returned by each message's `GetSigners` call. In addition, the address of first
signer of the embedded transaction is responsible for paying the fees.
## Gas & Fees
TODO

View File

@ -24,12 +24,11 @@ type internalAnteHandler func(
sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper, sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper,
) (newCtx sdk.Context, res sdk.Result, abort bool) ) (newCtx sdk.Context, res sdk.Result, abort bool)
// AnteHandler handles Ethereum transactions and passes SDK transactions to the // AnteHandler is responsible for attempting to route an Ethereum or SDK
// embeddedAnteHandler if it's an Ethermint transaction. The ante handler gets // transaction to an internal ante handler for performing transaction-level
// invoked after the BaseApp performs the runTx. At this point, the transaction // processing (e.g. fee payment, signature verification) before being passed
// should be properly decoded via the TxDecoder and should be of a proper type, // onto it's respective handler.
// Transaction or EmbeddedTx. func AnteHandler(am auth.AccountMapper, _ auth.FeeCollectionKeeper) sdk.AnteHandler {
func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
var ( var (
handler internalAnteHandler handler internalAnteHandler
@ -38,9 +37,9 @@ func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
switch tx := tx.(type) { switch tx := tx.(type) {
case types.Transaction: case types.Transaction:
gasLimit = int64(tx.Data.GasLimit) gasLimit = int64(tx.Data().GasLimit)
handler = handleEthTx handler = handleEthTx
case types.EmbeddedTx: case auth.StdTx:
gasLimit = tx.Fee.Gas gasLimit = tx.Fee.Gas
handler = handleEmbeddedTx handler = handleEmbeddedTx
default: default:
@ -90,73 +89,75 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper) (sdk.Cont
return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid chainID: %s", sdkCtx.ChainID())).Result(), true return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid chainID: %s", sdkCtx.ChainID())).Result(), true
} }
// validate signature sdkCtx.GasMeter().ConsumeGas(verifySigCost, "ante: verify Ethereum signature")
sdkCtx.GasMeter().ConsumeGas(verifySigCost, "ante verify")
addr, err := ethTx.VerifySig(chainID)
addr, err := ethTx.VerifySig(chainID)
if err != nil { if err != nil {
return sdkCtx, sdk.ErrUnauthorized("signature verification failed").Result(), true return sdkCtx, sdk.ErrUnauthorized("signature verification failed").Result(), true
} }
// validate AccountNonce (called Sequence in AccountMapper) acc := am.GetAccount(sdkCtx, addr.Bytes())
acc := am.GetAccount(sdkCtx, addr[:])
// validate the account nonce (referred to as sequence in the AccountMapper)
seq := acc.GetSequence() seq := acc.GetSequence()
if ethTx.Data.AccountNonce != uint64(seq) { if ethTx.Data().AccountNonce != uint64(seq) {
return sdkCtx, sdk.ErrInvalidSequence(fmt.Sprintf("Wrong AccountNonce: expected %d", seq)).Result(), true return sdkCtx, sdk.ErrInvalidSequence(fmt.Sprintf("invalid account nonce; expected: %d", seq)).Result(), true
} }
err = acc.SetSequence(seq + 1) err = acc.SetSequence(seq + 1)
if err != nil { if err != nil {
panic(err) return sdkCtx, sdk.ErrInternal(err.Error()).Result(), true
} }
am.SetAccount(sdkCtx, acc)
return sdkCtx, sdk.Result{GasWanted: int64(ethTx.Data.GasLimit)}, false am.SetAccount(sdkCtx, acc)
return sdkCtx, sdk.Result{GasWanted: int64(ethTx.Data().GasLimit)}, false
} }
// handleEmbeddedTx implements an ante handler for an SDK transaction. It // handleEmbeddedTx implements an ante handler for an SDK transaction. It
// validates the signature and if valid returns an OK result. // validates the signature and if valid returns an OK result.
func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper) (sdk.Context, sdk.Result, bool) { func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper) (sdk.Context, sdk.Result, bool) {
etx, ok := tx.(types.EmbeddedTx) stdTx, ok := tx.(auth.StdTx)
if !ok { if !ok {
return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid transaction: %T", tx)).Result(), true return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid transaction: %T", tx)).Result(), true
} }
if err := validateEmbeddedTxBasic(etx); err != nil { if err := validateStdTxBasic(stdTx); err != nil {
return sdkCtx, err.Result(), true return sdkCtx, err.Result(), true
} }
signerAddrs := etx.GetRequiredSigners() signerAddrs := stdTx.GetSigners()
signerAccs := make([]auth.Account, len(signerAddrs)) signerAccs := make([]auth.Account, len(signerAddrs))
// validate signatures // validate signatures
for i, sig := range etx.Signatures { for i, sig := range stdTx.Signatures {
signer := ethcmn.BytesToAddress(signerAddrs[i].Bytes()) signer := ethcmn.BytesToAddress(signerAddrs[i].Bytes())
signerAcc, err := validateSignature(sdkCtx, etx, signer, sig, am) acc, err := validateSignature(sdkCtx, stdTx, signer, sig, am)
// err.Code() != sdk.CodeOK
if err != nil { if err != nil {
return sdkCtx, err.Result(), true return sdkCtx, err.Result(), true
} }
// TODO: Fees! // TODO: Fees!
am.SetAccount(sdkCtx, signerAcc) am.SetAccount(sdkCtx, acc)
signerAccs[i] = signerAcc signerAccs[i] = acc
} }
newCtx := auth.WithSigners(sdkCtx, signerAccs) newCtx := auth.WithSigners(sdkCtx, signerAccs)
return newCtx, sdk.Result{GasWanted: etx.Fee.Gas}, false return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false
} }
// validateEmbeddedTxBasic validates an EmbeddedTx based on things that don't // validateStdTxBasic validates an auth.StdTx based on parameters that do not
// depend on the context. // depend on the context.
func validateEmbeddedTxBasic(etx types.EmbeddedTx) (err sdk.Error) { func validateStdTxBasic(stdTx auth.StdTx) (err sdk.Error) {
sigs := etx.Signatures sigs := stdTx.Signatures
if len(sigs) == 0 { if len(sigs) == 0 {
return sdk.ErrUnauthorized("transaction missing signatures") return sdk.ErrUnauthorized("transaction missing signatures")
} }
signerAddrs := etx.GetRequiredSigners() signerAddrs := stdTx.GetSigners()
if len(sigs) != len(signerAddrs) { if len(sigs) != len(signerAddrs) {
return sdk.ErrUnauthorized("invalid number of transaction signers") return sdk.ErrUnauthorized("invalid number of transaction signers")
} }
@ -165,8 +166,8 @@ func validateEmbeddedTxBasic(etx types.EmbeddedTx) (err sdk.Error) {
} }
func validateSignature( func validateSignature(
sdkCtx sdk.Context, etx types.EmbeddedTx, signer ethcmn.Address, sdkCtx sdk.Context, stdTx auth.StdTx, signer ethcmn.Address,
sig []byte, am auth.AccountMapper, sig auth.StdSignature, am auth.AccountMapper,
) (acc auth.Account, sdkErr sdk.Error) { ) (acc auth.Account, sdkErr sdk.Error) {
chainID := sdkCtx.ChainID() chainID := sdkCtx.ChainID()
@ -176,28 +177,29 @@ func validateSignature(
return nil, sdk.ErrUnknownAddress(fmt.Sprintf("no account with address %s found", signer)) return nil, sdk.ErrUnknownAddress(fmt.Sprintf("no account with address %s found", signer))
} }
signEtx := types.EmbeddedTxSign{ accNum := acc.GetAccountNumber()
ChainID: chainID, if accNum != sig.AccountNumber {
AccountNumber: acc.GetAccountNumber(), return nil, sdk.ErrInvalidSequence(
Sequence: acc.GetSequence(), fmt.Sprintf("invalid account number; got %d, expected %d", sig.AccountNumber, accNum))
Messages: etx.Messages,
Fee: etx.Fee,
} }
err := acc.SetSequence(signEtx.Sequence + 1) accSeq := acc.GetSequence()
if accSeq != sig.Sequence {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("invalid account sequence; got %d, expected %d", sig.Sequence, accSeq))
}
err := acc.SetSequence(accSeq + 1)
if err != nil { if err != nil {
return nil, sdk.ErrInternal(err.Error()) return nil, sdk.ErrInternal(err.Error())
} }
signBytes, err := signEtx.Bytes() signBytes := types.GetStdTxSignBytes(chainID, accNum, accSeq, stdTx.Fee, stdTx.GetMsgs(), stdTx.Memo)
if err != nil {
return nil, sdk.ErrInternal(err.Error())
}
// consume gas for signature verification // consume gas for signature verification
sdkCtx.GasMeter().ConsumeGas(verifySigCost, "ante verify") sdkCtx.GasMeter().ConsumeGas(verifySigCost, "ante signature verification")
if err := types.ValidateSigner(signBytes, sig, signer); err != nil { if err := types.ValidateSigner(signBytes, sig.Signature, signer); err != nil {
return nil, sdk.ErrUnauthorized(err.Error()) return nil, sdk.ErrUnauthorized(err.Error())
} }

45
server/rpc/apis.go Normal file
View File

@ -0,0 +1,45 @@
// Package rpc contains RPC handler methods and utilities to start
// Ethermint's Web3-compatibly JSON-RPC server.
package rpc
import (
"github.com/cosmos/ethermint/version"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
)
// returns the master list of public APIs for use with StartHTTPEndpoint
func GetRPCAPIs() []rpc.API {
return []rpc.API{
{
Namespace: "web3",
Version: "1.0",
Service: NewPublicWeb3API(),
},
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthAPI(),
},
}
}
// PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PublicWeb3API struct {
}
// NewPublicWeb3API creates an instance of the Web3 API.
func NewPublicWeb3API() *PublicWeb3API {
return &PublicWeb3API{}
}
// ClientVersion returns the client version in the Web3 user agent format.
func (a *PublicWeb3API) ClientVersion() string {
return version.ClientVersion()
}
// Sha3 returns the keccak-256 hash of the passed-in input.
func (a *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes {
return crypto.Keccak256(input)
}

65
server/rpc/apis_test.go Normal file
View File

@ -0,0 +1,65 @@
package rpc
import (
"context"
"github.com/cosmos/ethermint/version"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"testing"
)
type apisTestSuite struct {
suite.Suite
Stop context.CancelFunc
Port int
}
func (s *apisTestSuite) SetupSuite() {
stop, port, err := startAPIServer()
require.Nil(s.T(), err, "unexpected error")
s.Stop = stop
s.Port = port
}
func (s *apisTestSuite) TearDownSuite() {
s.Stop()
}
func (s *apisTestSuite) TestPublicWeb3APIClientVersion() {
res, err := rpcCall(s.Port, "web3_clientVersion", []string{})
require.Nil(s.T(), err, "unexpected error")
require.Equal(s.T(), version.ClientVersion(), res)
}
func (s *apisTestSuite) TestPublicWeb3APISha3() {
res, err := rpcCall(s.Port, "web3_sha3", []string{"0x67656c6c6f20776f726c64"})
require.Nil(s.T(), err, "unexpected error")
require.Equal(s.T(), "0x1b84adea42d5b7d192fd8a61a85b25abe0757e9a65cab1da470258914053823f", res)
}
func (s *apisTestSuite) TestMiningAPIs() {
res, err := rpcCall(s.Port, "eth_mining", nil)
require.Nil(s.T(), err, "unexpected error")
require.Equal(s.T(), false, res)
res, err = rpcCall(s.Port, "eth_hashrate", nil)
require.Nil(s.T(), err, "unexpected error")
require.Equal(s.T(), "0x0", res)
}
func TestAPIsTestSuite(t *testing.T) {
suite.Run(t, new(apisTestSuite))
}
func startAPIServer() (context.CancelFunc, int, error) {
config := &Config{
RPCAddr: "127.0.0.1",
RPCPort: randomPort(),
}
ctx, cancel := context.WithCancel(context.Background())
_, err := StartHTTPEndpoint(ctx, config, GetRPCAPIs())
if err != nil {
return cancel, 0, err
}
return cancel, config.RPCPort, nil
}

16
server/rpc/config.go Normal file
View File

@ -0,0 +1,16 @@
package rpc
// Config contains configuration fields that determine the
// behavior of the RPC HTTP server.
type Config struct {
// EnableRPC defines whether or not to enable the RPC server
EnableRPC bool
// RPCAddr defines the IP address to listen on
RPCAddr string
// RPCPort defines the port to listen on
RPCPort int
// RPCCORSDomains defines list of domains to enable CORS headers for (used by browsers)
RPCCORSDomains []string
// RPCVhosts defines list of domains to listen on (useful if Tendermint is addressable via DNS)
RPCVHosts []string
}

196
server/rpc/eth_api.go Normal file
View File

@ -0,0 +1,196 @@
package rpc
import (
"github.com/cosmos/ethermint/version"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/signer/core"
"math/big"
)
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PublicEthAPI struct{}
// NewPublicEthAPI creates an instance of the public ETH Web3 API.
func NewPublicEthAPI() *PublicEthAPI {
return &PublicEthAPI{}
}
// ProtocolVersion returns the supported Ethereum protocol version.
func (e *PublicEthAPI) ProtocolVersion() string {
return version.ProtocolVersion
}
// Syncing returns whether or not the current node is syncing with other peers. Returns false if not, or a struct
// outlining the state of the sync if it is.
func (e *PublicEthAPI) Syncing() interface{} {
return false
}
// Coinbase returns this node's coinbase address. Not used in Ethermint.
func (e *PublicEthAPI) Coinbase() (addr common.Address) {
return
}
// Mining returns whether or not this node is currently mining. Always false.
func (e *PublicEthAPI) Mining() bool {
return false
}
// Hashrate returns the current node's hashrate. Always 0.
func (e *PublicEthAPI) Hashrate() hexutil.Uint64 {
return 0
}
// GasPrice returns the current gas price based on Ethermint's gas price oracle.
func (e *PublicEthAPI) GasPrice() *hexutil.Big {
out := big.NewInt(0)
return (*hexutil.Big)(out)
}
// Accounts returns the list of accounts available to this node.
func (e *PublicEthAPI) Accounts() []common.Address {
return nil
}
// BlockNumber returns the current block number.
func (e *PublicEthAPI) BlockNumber() *big.Int {
return big.NewInt(0)
}
// GetBalance returns the provided account's balance up to the provided block number.
func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumber) *hexutil.Big {
out := big.NewInt(0)
return (*hexutil.Big)(out)
}
// GetStorageAt returns the contract storage at the given address, block number, and key.
func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum rpc.BlockNumber) hexutil.Bytes {
return nil
}
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
func (e *PublicEthAPI) GetTransactionCount(address common.Address, blockNum rpc.BlockNumber) hexutil.Uint64 {
return 0
}
// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash.
func (e *PublicEthAPI) GetBlockTransactionCountByHash(hash common.Hash) hexutil.Uint {
return 0
}
// GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number.
func (e *PublicEthAPI) GetBlockTransactionCountByNumber(blockNum rpc.BlockNumber) hexutil.Uint {
return 0
}
// GetUncleCountByBlockHash returns the number of uncles in the block idenfied by hash. Always zero.
func (e *PublicEthAPI) GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint {
return 0
}
// GetUncleCountByBlockNumber returns the number of uncles in the block idenfied by number. Always zero.
func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum rpc.BlockNumber) hexutil.Uint {
return 0
}
// GetCode returns the contract code at the given address and block number.
func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumber) hexutil.Bytes {
return nil
}
// Sign signs the provided data using the private key of address via Geth's signature standard.
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) hexutil.Bytes {
return nil
}
// SendTransaction sends an Ethereum transaction.
func (e *PublicEthAPI) SendTransaction(args core.SendTxArgs) common.Hash {
var h common.Hash
return h
}
// SendRawTransaction send a raw Ethereum transaction.
func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) common.Hash {
var h common.Hash
return h
}
// CallArgs represents arguments to a smart contract call as provided by RPC clients.
type CallArgs struct {
From common.Address `json:"from"`
To common.Address `json:"to"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice hexutil.Big `json:"gasPrice"`
Value hexutil.Big `json:"value"`
Data hexutil.Bytes `json:"data"`
}
// Call performs a raw contract call.
func (e *PublicEthAPI) Call(args CallArgs, blockNum rpc.BlockNumber) hexutil.Bytes {
return nil
}
// EstimateGas estimates gas usage for the given smart contract call.
func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNum rpc.BlockNumber) hexutil.Uint64 {
return 0
}
// GetBlockByHash returns the block identified by hash.
func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) map[string]interface{} {
return nil
}
// GetBlockByNumber returns the block identified by number.
func (e *PublicEthAPI) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) map[string]interface{} {
return nil
}
// Transaction represents a transaction returned to RPC clients.
type Transaction struct {
BlockHash common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex hexutil.Uint `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
}
// GetTransactionByHash returns the transaction identified by hash.
func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) *Transaction {
return nil
}
// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index.
func (e *PublicEthAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) *Transaction {
return nil
}
// GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index.
func (e *PublicEthAPI) GetTransactionByBlockNumberAndIndex(blockNumber rpc.BlockNumber, idx hexutil.Uint) *Transaction {
return nil
}
// GetTransactionReceipt returns the transaction receipt identified by hash.
func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) map[string]interface{} {
return nil
}
// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil.
func (e *PublicEthAPI) GetUncleByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) map[string]interface{} {
return nil
}
// GetUncleByBlockNumberAndIndex returns the uncle identified by number and index. Always returns nil.
func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexutil.Uint) map[string]interface{} {
return nil
}

33
server/rpc/rpc.go Normal file
View File

@ -0,0 +1,33 @@
package rpc
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/rpc"
)
// StartHTTPEndpoint starts the Tendermint Web3-compatible RPC layer. Consumes a Context for cancellation, a config
// struct, and a list of rpc.API interfaces that will be automatically wired into a JSON-RPC webserver.
func StartHTTPEndpoint(ctx context.Context, config *Config, apis []rpc.API) (*rpc.Server, error) {
uniqModules := make(map[string]string)
for _, api := range apis {
uniqModules[api.Namespace] = api.Namespace
}
modules := make([]string, len(uniqModules))
i := 0
for k := range uniqModules {
modules[i] = k
i++
}
endpoint := fmt.Sprintf("%s:%d", config.RPCAddr, config.RPCPort)
_, server, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, config.RPCCORSDomains, config.RPCVHosts)
go func() {
<-ctx.Done()
fmt.Println("Shutting down server.")
server.Stop()
}()
return server, err
}

74
server/rpc/rpc_test.go Normal file
View File

@ -0,0 +1,74 @@
package rpc
import (
"context"
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
"io/ioutil"
"math/rand"
"net/http"
"strings"
"testing"
)
type TestService struct{}
func (s *TestService) Foo(arg string) string {
return arg
}
func TestStartHTTPEndpointStartStop(t *testing.T) {
config := &Config{
RPCAddr: "127.0.0.1",
RPCPort: randomPort(),
}
ctx, cancel := context.WithCancel(context.Background())
_, err := StartHTTPEndpoint(ctx, config, []rpc.API{
{
Namespace: "test",
Version: "1.0",
Service: &TestService{},
Public: true,
},
})
require.Nil(t, err, "unexpected error")
res, err := rpcCall(config.RPCPort, "test_foo", []string{"baz"})
require.Nil(t, err, "unexpected error")
resStr := res.(string)
require.Equal(t, "baz", resStr)
cancel()
_, err = rpcCall(config.RPCPort, "test_foo", []string{"baz"})
require.NotNil(t, err)
}
func rpcCall(port int, method string, params []string) (interface{}, error) {
parsedParams, err := json.Marshal(params)
if err != nil {
return nil, err
}
fullBody := fmt.Sprintf(`{ "id": 1, "jsonrpc": "2.0", "method": "%s", "params": %s }`,
method, string(parsedParams))
res, err := http.Post(fmt.Sprintf("http://127.0.0.1:%d", port), "application/json", strings.NewReader(fullBody))
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
var out map[string]interface{}
err = json.Unmarshal(data, &out)
if err != nil {
return nil, err
}
result := out["result"].(interface{})
return result, nil
}
func randomPort() int {
return rand.Intn(65535-1025) + 1025
}

View File

@ -1 +0,0 @@
package server

53
types/account.go Normal file
View File

@ -0,0 +1,53 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common"
)
var _ auth.Account = (*Account)(nil)
type (
// Storage defines account storage
Storage map[ethcmn.Hash]ethcmn.Hash
// Account defines an auth.BaseAccount extension for Ethermint. It is
// compatible with the auth.AccountMapper.
Account struct {
auth.BaseAccount
Code []byte
Storage Storage
}
)
// NewAccount returns a reference to a new initialized account.
func NewAccount(base auth.BaseAccount, code []byte, storage Storage) *Account {
return &Account{
BaseAccount: base,
Code: code,
Storage: storage,
}
}
// GetAccountDecoder returns the auth.AccountDecoder function for the custom
// Account type.
func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder {
return func(accBytes []byte) (auth.Account, error) {
if len(accBytes) == 0 {
return nil, sdk.ErrTxDecode("account bytes are empty")
}
acc := new(Account)
err := cdc.UnmarshalBinaryBare(accBytes, &acc)
if err != nil {
return nil, sdk.ErrTxDecode("failed to decode account bytes")
}
return acc, err
}
}

View File

@ -1,3 +1,4 @@
// nolint
package types package types
import ( import (
@ -7,119 +8,113 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
stake "github.com/cosmos/cosmos-sdk/x/stake/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
) )
var ( var (
TestChainID = sdk.NewInt(3) TestSDKAddr = GenerateEthAddress()
TestChainID = big.NewInt(3)
TestPrivKey1, _ = ethcrypto.GenerateKey() TestPrivKey1, _ = ethcrypto.GenerateKey()
TestPrivKey2, _ = ethcrypto.GenerateKey() TestPrivKey2, _ = ethcrypto.GenerateKey()
TestAddr1 = PrivKeyToEthAddress(TestPrivKey1) TestAddr1 = PrivKeyToEthAddress(TestPrivKey1)
TestAddr2 = PrivKeyToEthAddress(TestPrivKey2) TestAddr2 = PrivKeyToEthAddress(TestPrivKey2)
TestSDKAddress = GenerateEthAddress()
) )
func NewTestCodec() *wire.Codec { func NewTestCodec() *wire.Codec {
codec := wire.NewCodec() codec := wire.NewCodec()
RegisterWire(codec) RegisterWire(codec)
auth.RegisterWire(codec)
wire.RegisterCrypto(codec)
codec.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil) codec.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil)
// Register any desired SDK msgs to be embedded
stake.RegisterWire(codec)
return codec return codec
} }
func NewStdFee() auth.StdFee { func NewTestStdFee() auth.StdFee {
return auth.NewStdFee(5000, sdk.NewCoin("photon", 150)) return auth.NewStdFee(5000, sdk.NewCoin("photon", sdk.NewInt(150)))
} }
func NewTestEmbeddedTx( func NewTestStdTx(
chainID sdk.Int, msgs []sdk.Msg, pKeys []*ecdsa.PrivateKey, chainID *big.Int, msgs []sdk.Msg, accNums []int64, seqs []int64, pKeys []*ecdsa.PrivateKey, fee auth.StdFee,
accNums []int64, seqs []int64, fee auth.StdFee,
) sdk.Tx { ) sdk.Tx {
sigs := make([][]byte, len(pKeys)) sigs := make([]auth.StdSignature, len(pKeys))
for i, priv := range pKeys { for i, priv := range pKeys {
signEtx := EmbeddedTxSign{chainID.String(), accNums[i], seqs[i], msgs, fee} signBytes := GetStdTxSignBytes(chainID.String(), accNums[i], seqs[i], NewTestStdFee(), msgs, "")
signBytes, err := signEtx.Bytes()
if err != nil {
panic(err)
}
sig, err := ethcrypto.Sign(signBytes, priv) sig, err := ethcrypto.Sign(signBytes, priv)
if err != nil { if err != nil {
panic(err) panic(err)
} }
sigs[i] = sig sigs[i] = auth.StdSignature{Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
} }
return EmbeddedTx{msgs, fee, sigs} return auth.NewStdTx(msgs, fee, sigs, "")
} }
func NewTestGethTxs(chainID sdk.Int, pKeys []*ecdsa.PrivateKey, addrs []ethcmn.Address) []ethtypes.Transaction { func NewTestGethTxs(
txs := make([]ethtypes.Transaction, len(pKeys)) chainID *big.Int, seqs []int64, addrs []ethcmn.Address, pKeys []*ecdsa.PrivateKey,
) []*ethtypes.Transaction {
for i, priv := range pKeys { txs := make([]*ethtypes.Transaction, len(pKeys))
for i, privKey := range pKeys {
ethTx := ethtypes.NewTransaction( ethTx := ethtypes.NewTransaction(
uint64(i), addrs[i], big.NewInt(10), 100, big.NewInt(100), nil, uint64(seqs[i]), addrs[i], big.NewInt(10), 1000, big.NewInt(100), []byte{},
) )
signer := ethtypes.NewEIP155Signer(chainID.BigInt()) signer := ethtypes.NewEIP155Signer(chainID)
ethTx, _ = ethtypes.SignTx(ethTx, signer, priv)
txs[i] = *ethTx ethTx, err := ethtypes.SignTx(ethTx, signer, privKey)
if err != nil {
panic(err)
}
txs[i] = ethTx
} }
return txs return txs
} }
func NewTestEthTxs(chainID sdk.Int, pKeys []*ecdsa.PrivateKey, addrs []ethcmn.Address) []Transaction { func NewTestEthTxs(
txs := make([]Transaction, len(pKeys)) chainID *big.Int, seqs []int64, addrs []ethcmn.Address, pKeys []*ecdsa.PrivateKey,
) []*Transaction {
for i, priv := range pKeys { txs := make([]*Transaction, len(pKeys))
emintTx := NewTransaction(
uint64(i), addrs[i], sdk.NewInt(10), 1000, sdk.NewInt(100), nil, for i, privKey := range pKeys {
ethTx := NewTransaction(
uint64(seqs[i]), addrs[i], big.NewInt(10), 1000, big.NewInt(100), []byte{},
) )
emintTx.Sign(chainID, priv) ethTx.Sign(chainID, privKey)
txs[i] = ethTx
txs[i] = emintTx
} }
return txs return txs
} }
func NewTestSDKTxs( func NewTestSDKTxs(
codec *wire.Codec, chainID sdk.Int, msgs []sdk.Msg, pKeys []*ecdsa.PrivateKey, codec *wire.Codec, chainID *big.Int, to ethcmn.Address, msgs []sdk.Msg,
accNums []int64, seqs []int64, fee auth.StdFee, accNums []int64, seqs []int64, pKeys []*ecdsa.PrivateKey, fee auth.StdFee,
) []Transaction { ) []*Transaction {
txs := make([]Transaction, len(pKeys)) txs := make([]*Transaction, len(pKeys))
etx := NewTestEmbeddedTx(chainID, msgs, pKeys, accNums, seqs, fee) stdTx := NewTestStdTx(chainID, msgs, accNums, seqs, pKeys, fee)
payload := codec.MustMarshalBinary(stdTx)
for i, priv := range pKeys { for i, privKey := range pKeys {
payload := codec.MustMarshalBinary(etx) ethTx := NewTransaction(uint64(seqs[i]), to, big.NewInt(10), 1000, big.NewInt(100), payload)
emintTx := NewTransaction( ethTx.Sign(chainID, privKey)
uint64(i), TestSDKAddress, sdk.NewInt(10), 1000, txs[i] = ethTx
sdk.NewInt(100), payload,
)
emintTx.Sign(TestChainID, priv)
txs[i] = emintTx
} }
return txs return txs

View File

@ -3,21 +3,18 @@ package types
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/sha256"
"encoding/json"
"fmt" "fmt"
"io"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto"
ethsha "github.com/ethereum/go-ethereum/crypto/sha3" ethsha "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -30,16 +27,18 @@ const (
// Ethereum transaction // Ethereum transaction
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
var _ sdk.Tx = (*Transaction)(nil)
type ( type (
// Transaction implements the Ethereum transaction structure as an exact // Transaction implements the Ethereum transaction structure as an exact
// copy. It implements the Cosmos sdk.Tx interface. Due to the private // replica. It implements the Cosmos sdk.Tx interface. Due to the private
// fields, it must be replicated here and cannot be embedded or used // fields, it must be replicated here and cannot be embedded or used
// directly. // directly.
// //
// Note: The transaction also implements the sdk.Msg interface to perform // Note: The transaction also implements the sdk.Msg interface to perform
// basic validation that is done in the BaseApp. // basic validation that is done in the BaseApp.
Transaction struct { Transaction struct {
Data TxData data TxData
// caches // caches
hash atomic.Value hash atomic.Value
@ -52,100 +51,131 @@ type (
// except for the payload field which may embed a Cosmos SDK transaction. // except for the payload field which may embed a Cosmos SDK transaction.
TxData struct { TxData struct {
AccountNonce uint64 `json:"nonce"` AccountNonce uint64 `json:"nonce"`
Price sdk.Int `json:"gasPrice"` Price *big.Int `json:"gasPrice"`
GasLimit uint64 `json:"gas"` GasLimit uint64 `json:"gas"`
Recipient *ethcmn.Address `json:"to"` // nil means contract creation Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation
Amount sdk.Int `json:"value"` Amount *big.Int `json:"value"`
Payload []byte `json:"input"` Payload []byte `json:"input"`
Signature *EthSignature `json:"signature"`
// signature values
V *big.Int `json:"v"`
R *big.Int `json:"r"`
S *big.Int `json:"s"`
// hash is only used when marshaling to JSON // hash is only used when marshaling to JSON
Hash *ethcmn.Hash `json:"hash"` Hash *ethcmn.Hash `json:"hash" rlp:"-"`
} }
// EthSignature reflects an Ethereum signature. We wrap this in a structure // sigCache is used to cache the derived sender and contains the signer used
// to support Amino serialization of transactions. // to derive it.
EthSignature struct {
v, r, s *big.Int
}
// sigCache is used to cache the derived sender and contains
// the signer used to derive it.
sigCache struct { sigCache struct {
signer ethtypes.Signer signer ethtypes.Signer
from ethcmn.Address from ethcmn.Address
} }
) )
// NewEthSignature returns a new instantiated Ethereum signature. // NewTransaction returns a reference to a new Ethereum transaction.
func NewEthSignature(v, r, s *big.Int) *EthSignature {
return &EthSignature{v, r, s}
}
func (es *EthSignature) sanitize() {
if es.v == nil {
es.v = new(big.Int)
}
if es.r == nil {
es.r = new(big.Int)
}
if es.s == nil {
es.s = new(big.Int)
}
}
// MarshalAmino defines a custom encoding scheme for a EthSignature.
func (es EthSignature) MarshalAmino() ([3]string, error) {
es.sanitize()
return ethSigMarshalAmino(es)
}
// UnmarshalAmino defines a custom decoding scheme for a EthSignature.
func (es *EthSignature) UnmarshalAmino(raw [3]string) error {
es.sanitize()
return ethSigUnmarshalAmino(es, raw)
}
// NewTransaction mimics ethereum's NewTransaction function. It returns a
// reference to a new Ethereum Transaction.
func NewTransaction( func NewTransaction(
nonce uint64, to ethcmn.Address, amount sdk.Int, nonce uint64, to ethcmn.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte,
gasLimit uint64, gasPrice sdk.Int, payload []byte, ) *Transaction {
) Transaction {
return newTransaction(nonce, &to, amount, gasLimit, gasPrice, payload)
}
// NewContractCreation returns a reference to a new Ethereum transaction
// designated for contract creation.
func NewContractCreation(
nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte,
) *Transaction {
return newTransaction(nonce, nil, amount, gasLimit, gasPrice, payload)
}
func newTransaction(
nonce uint64, to *ethcmn.Address, amount *big.Int,
gasLimit uint64, gasPrice *big.Int, payload []byte,
) *Transaction {
if len(payload) > 0 { if len(payload) > 0 {
payload = ethcmn.CopyBytes(payload) payload = ethcmn.CopyBytes(payload)
} }
txData := TxData{ txData := TxData{
Recipient: &to,
AccountNonce: nonce, AccountNonce: nonce,
Recipient: to,
Payload: payload, Payload: payload,
GasLimit: gasLimit, GasLimit: gasLimit,
Amount: amount, Amount: new(big.Int),
Price: gasPrice, Price: new(big.Int),
Signature: NewEthSignature(new(big.Int), new(big.Int), new(big.Int)), V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
} }
return Transaction{Data: txData} if amount != nil {
txData.Amount.Set(amount)
}
if gasPrice != nil {
txData.Price.Set(gasPrice)
}
return &Transaction{data: txData}
}
// Data returns the Transaction's data.
func (tx Transaction) Data() TxData {
return tx.data
}
// EncodeRLP implements the rlp.Encoder interface.
func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data)
}
// DecodeRLP implements the rlp.Decoder interface.
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
_, size, _ := s.Kind()
err := s.Decode(&tx.data)
if err == nil {
tx.size.Store(ethcmn.StorageSize(rlp.ListSize(size)))
}
return err
}
// Hash hashes the RLP encoding of a transaction.
func (tx *Transaction) Hash() ethcmn.Hash {
if hash := tx.hash.Load(); hash != nil {
return hash.(ethcmn.Hash)
}
v := rlpHash(tx)
tx.hash.Store(v)
return v
}
// SigHash returns the RLP hash of a transaction with a given chainID used for
// signing.
func (tx Transaction) SigHash(chainID *big.Int) ethcmn.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
chainID, uint(0), uint(0),
})
} }
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It // Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
// takes a private key and chainID to sign an Ethereum transaction according to // takes a private key and chainID to sign an Ethereum transaction according to
// EIP155 standard. It mutates the transaction as it populates the V, R, S // EIP155 standard. It mutates the transaction as it populates the V, R, S
// fields of the Transaction's Signature. // fields of the Transaction's Signature.
func (tx *Transaction) Sign(chainID sdk.Int, priv *ecdsa.PrivateKey) { func (tx *Transaction) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
h := rlpHash([]interface{}{ txHash := tx.SigHash(chainID)
tx.Data.AccountNonce,
tx.Data.Price.BigInt(),
tx.Data.GasLimit,
tx.Data.Recipient,
tx.Data.Amount.BigInt(),
tx.Data.Payload,
chainID.BigInt(), uint(0), uint(0),
})
sig, err := ethcrypto.Sign(h[:], priv) sig, err := ethcrypto.Sign(txHash[:], priv)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -162,17 +192,20 @@ func (tx *Transaction) Sign(chainID sdk.Int, priv *ecdsa.PrivateKey) {
v = new(big.Int).SetBytes([]byte{sig[64] + 27}) v = new(big.Int).SetBytes([]byte{sig[64] + 27})
} else { } else {
v = big.NewInt(int64(sig[64] + 35)) v = big.NewInt(int64(sig[64] + 35))
chainIDMul := new(big.Int).Mul(chainID.BigInt(), big.NewInt(2)) chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2))
v.Add(v, chainIDMul) v.Add(v, chainIDMul)
} }
tx.Data.Signature.v = v tx.data.V = v
tx.Data.Signature.r = r tx.data.R = r
tx.Data.Signature.s = s tx.data.S = s
} }
// VerifySig attempts to verify a Transaction's signature for a given chainID.
// A derived address is returned upon success or an error if recovery fails.
func (tx Transaction) VerifySig(chainID *big.Int) (ethcmn.Address, error) { func (tx Transaction) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
signer := ethtypes.NewEIP155Signer(chainID) signer := ethtypes.NewEIP155Signer(chainID)
if sc := tx.from.Load(); sc != nil { if sc := tx.from.Load(); sc != nil {
sigCache := sc.(sigCache) sigCache := sc.(sigCache)
// If the signer used to derive from in a previous // If the signer used to derive from in a previous
@ -183,24 +216,15 @@ func (tx Transaction) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
} }
} }
// Do not allow unprotected chainID // do not allow recovery for transactions with an unprotected chainID
if chainID.Sign() == 0 { if chainID.Sign() == 0 {
return ethcmn.Address{}, errors.New("Cannot have 0 as ChainID") return ethcmn.Address{}, errors.New("invalid chainID")
} }
signBytes := rlpHash([]interface{}{ txHash := tx.SigHash(chainID)
tx.Data.AccountNonce, sig := recoverEthSig(tx.data.R, tx.data.S, tx.data.V, chainID)
tx.Data.Price.BigInt(),
tx.Data.GasLimit,
tx.Data.Recipient,
tx.Data.Amount.BigInt(),
tx.Data.Payload,
chainID, uint(0), uint(0),
})
sig := recoverEthSig(tx.Data.Signature, chainID) pub, err := ethcrypto.Ecrecover(txHash[:], sig)
pub, err := ethcrypto.Ecrecover(signBytes[:], sig)
if err != nil { if err != nil {
return ethcmn.Address{}, err return ethcmn.Address{}, err
} }
@ -221,11 +245,11 @@ func (tx Transaction) Type() string {
// ValidateBasic implements the sdk.Msg interface. It performs basic validation // ValidateBasic implements the sdk.Msg interface. It performs basic validation
// checks of a Transaction. If returns an sdk.Error if validation fails. // checks of a Transaction. If returns an sdk.Error if validation fails.
func (tx Transaction) ValidateBasic() sdk.Error { func (tx Transaction) ValidateBasic() sdk.Error {
if tx.Data.Price.Sign() != 1 { if tx.data.Price.Sign() != 1 {
return ErrInvalidValue(DefaultCodespace, "price must be positive") return ErrInvalidValue(DefaultCodespace, "price must be positive")
} }
if tx.Data.Amount.Sign() != 1 { if tx.data.Amount.Sign() != 1 {
return ErrInvalidValue(DefaultCodespace, "amount must be positive") return ErrInvalidValue(DefaultCodespace, "amount must be positive")
} }
@ -248,10 +272,10 @@ func (tx Transaction) GetMsgs() []sdk.Msg {
return []sdk.Msg{tx} return []sdk.Msg{tx}
} }
// HasEmbeddedTx returns a boolean reflecting if the transaction contains an // hasEmbeddedTx returns a boolean reflecting if the transaction contains an
// SDK transaction or not based on the recipient address. // SDK transaction or not based on the recipient address.
func (tx Transaction) HasEmbeddedTx(addr ethcmn.Address) bool { func (tx Transaction) hasEmbeddedTx(addr ethcmn.Address) bool {
return bytes.Equal(tx.Data.Recipient.Bytes(), addr.Bytes()) return bytes.Equal(tx.data.Recipient.Bytes(), addr.Bytes())
} }
// GetEmbeddedTx returns the embedded SDK transaction from an Ethereum // GetEmbeddedTx returns the embedded SDK transaction from an Ethereum
@ -259,147 +283,40 @@ func (tx Transaction) HasEmbeddedTx(addr ethcmn.Address) bool {
// //
// CONTRACT: The payload field of an Ethereum transaction must contain a valid // CONTRACT: The payload field of an Ethereum transaction must contain a valid
// encoded SDK transaction. // encoded SDK transaction.
func (tx Transaction) GetEmbeddedTx(codec *wire.Codec) (EmbeddedTx, sdk.Error) { func (tx Transaction) GetEmbeddedTx(codec *wire.Codec) (sdk.Tx, sdk.Error) {
etx := EmbeddedTx{} var etx sdk.Tx
err := codec.UnmarshalBinary(tx.Data.Payload, &etx) err := codec.UnmarshalBinary(tx.data.Payload, &etx)
if err != nil { if err != nil {
return EmbeddedTx{}, sdk.ErrTxDecode("failed to encode embedded tx") return etx, sdk.ErrTxDecode("failed to decode embedded transaction")
} }
return etx, nil return etx, nil
} }
// Copies Ethereum tx's Protected function
func (tx Transaction) protected() bool {
if tx.Data.Signature.v.BitLen() <= 8 {
v := tx.Data.Signature.v.Uint64()
return v != 27 && v != 28
}
return true
}
// ----------------------------------------------------------------------------
// embedded SDK transaction
// ----------------------------------------------------------------------------
type (
// EmbeddedTx implements an SDK transaction. It is to be encoded into the
// payload field of an Ethereum transaction in order to route and handle SDK
// transactions.
EmbeddedTx struct {
Messages []sdk.Msg `json:"messages"`
Fee auth.StdFee `json:"fee"`
Signatures [][]byte `json:"signatures"`
}
// embeddedSignDoc implements a simple SignDoc for a EmbeddedTx signer to
// sign over.
embeddedSignDoc struct {
ChainID string `json:"chainID"`
AccountNumber int64 `json:"accountNumber"`
Sequence int64 `json:"sequence"`
Messages []json.RawMessage `json:"messages"`
Fee json.RawMessage `json:"fee"`
}
// EmbeddedTxSign implements a structure for containing the information
// necessary for building and signing an EmbeddedTx.
EmbeddedTxSign struct {
ChainID string
AccountNumber int64
Sequence int64
Messages []sdk.Msg
Fee auth.StdFee
}
)
// GetMsgs implements the sdk.Tx interface. It returns all the SDK transaction
// messages.
func (etx EmbeddedTx) GetMsgs() []sdk.Msg {
return etx.Messages
}
// GetRequiredSigners returns all the required signers of an SDK transaction
// accumulated from messages. It returns them in a deterministic fashion given
// a list of messages.
func (etx EmbeddedTx) GetRequiredSigners() []sdk.AccAddress {
seen := map[string]bool{}
var signers []sdk.AccAddress
for _, msg := range etx.GetMsgs() {
for _, addr := range msg.GetSigners() {
if !seen[addr.String()] {
signers = append(signers, sdk.AccAddress(addr))
seen[addr.String()] = true
}
}
}
return signers
}
// Bytes returns the EmbeddedTxSign signature bytes for a signer to sign over.
func (ets EmbeddedTxSign) Bytes() ([]byte, error) {
sigBytes, err := EmbeddedSignBytes(ets.ChainID, ets.AccountNumber, ets.Sequence, ets.Messages, ets.Fee)
if err != nil {
return nil, err
}
hash := sha256.Sum256(sigBytes)
return hash[:], nil
}
// EmbeddedSignBytes creates signature bytes for a signer to sign an embedded
// transaction. The signature bytes require a chainID and an account number.
// The signature bytes are JSON encoded.
func EmbeddedSignBytes(chainID string, accnum, sequence int64, msgs []sdk.Msg, fee auth.StdFee) ([]byte, error) {
var msgsBytes []json.RawMessage
for _, msg := range msgs {
msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes()))
}
signDoc := embeddedSignDoc{
ChainID: chainID,
AccountNumber: accnum,
Sequence: sequence,
Messages: msgsBytes,
Fee: json.RawMessage(fee.Bytes()),
}
bz, err := typesCodec.MarshalJSON(signDoc)
if err != nil {
errors.Wrap(err, "failed to JSON encode EmbeddedSignDoc")
}
return bz, nil
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Utilities // Utilities
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// TxDecoder returns an sdk.TxDecoder that given raw transaction bytes, // TxDecoder returns an sdk.TxDecoder that given raw transaction bytes and an
// attempts to decode them into a Transaction or an EmbeddedTx or returning an // SDK address, attempts to decode them into a Transaction or an EmbeddedTx or
// error if decoding fails. // returning an error if decoding fails.
func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder { func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) { return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = Transaction{} var tx = Transaction{}
if len(txBytes) == 0 { if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty") return nil, sdk.ErrTxDecode("transaction bytes are empty")
} }
// The given codec should have all the appropriate message types err := rlp.DecodeBytes(txBytes, &tx)
// registered.
err := codec.UnmarshalBinary(txBytes, &tx)
if err != nil { if err != nil {
return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error()) return nil, sdk.ErrTxDecode("failed to decode transaction").TraceSDK(err.Error())
} }
// If the transaction is routed as an SDK transaction, decode and // If the transaction is routed as an SDK transaction, decode and return
// return the embedded transaction. // the embedded SDK transaction.
if tx.HasEmbeddedTx(sdkAddress) { if tx.hasEmbeddedTx(sdkAddress) {
etx, err := tx.GetEmbeddedTx(codec) etx, err := tx.GetEmbeddedTx(codec)
if err != nil { if err != nil {
return nil, err return nil, err
@ -413,20 +330,20 @@ func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder {
} }
// recoverEthSig recovers a signature according to the Ethereum specification. // recoverEthSig recovers a signature according to the Ethereum specification.
func recoverEthSig(es *EthSignature, chainID *big.Int) []byte { func recoverEthSig(R, S, Vb, chainID *big.Int) []byte {
var v byte var v byte
r, s := es.r.Bytes(), es.s.Bytes() r, s := R.Bytes(), S.Bytes()
sig := make([]byte, 65) sig := make([]byte, 65)
copy(sig[32-len(r):32], r) copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s) copy(sig[64-len(s):64], s)
if chainID.Sign() == 0 { if chainID.Sign() == 0 {
v = byte(es.v.Uint64() - 27) v = byte(Vb.Uint64() - 27)
} else { } else {
chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2)) chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2))
V := new(big.Int).Sub(es.v, chainIDMul) V := new(big.Int).Sub(Vb, chainIDMul)
v = byte(V.Uint64() - 35) v = byte(V.Uint64() - 35)
} }
@ -435,43 +352,11 @@ func recoverEthSig(es *EthSignature, chainID *big.Int) []byte {
return sig return sig
} }
func rlpHash(x interface{}) (h ethcmn.Hash) { func rlpHash(x interface{}) (hash ethcmn.Hash) {
hasher := ethsha.NewKeccak256() hasher := ethsha.NewKeccak256()
rlp.Encode(hasher, x) rlp.Encode(hasher, x)
hasher.Sum(h[:0]) hasher.Sum(hash[:0])
return h
}
func ethSigMarshalAmino(es EthSignature) (raw [3]string, err error) {
vb, err := es.v.MarshalText()
if err != nil {
return raw, err
}
rb, err := es.r.MarshalText()
if err != nil {
return raw, err
}
sb, err := es.s.MarshalText()
if err != nil {
return raw, err
}
raw[0], raw[1], raw[2] = string(vb), string(rb), string(sb)
return raw, err
}
func ethSigUnmarshalAmino(es *EthSignature, raw [3]string) (err error) {
if err = es.v.UnmarshalText([]byte(raw[0])); err != nil {
return
}
if err = es.r.UnmarshalText([]byte(raw[1])); err != nil {
return
}
if err = es.s.UnmarshalText([]byte(raw[2])); err != nil {
return
}
return return
} }

View File

@ -3,18 +3,57 @@ package types
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
"math/big"
"testing" "testing"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestTransactionRLPEncode(t *testing.T) {
txs := NewTestEthTxs(TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1})
gtxs := NewTestGethTxs(TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1})
txRLP, err := rlp.EncodeToBytes(txs[0])
require.NoError(t, err)
gtxRLP, err := rlp.EncodeToBytes(gtxs[0])
require.NoError(t, err)
require.Equal(t, gtxRLP, txRLP)
}
func TestTransactionRLPDecode(t *testing.T) {
txs := NewTestEthTxs(TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1})
gtxs := NewTestGethTxs(TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1})
txRLP, err := rlp.EncodeToBytes(txs[0])
require.NoError(t, err)
gtxRLP, err := rlp.EncodeToBytes(gtxs[0])
require.NoError(t, err)
var (
decodedTx Transaction
decodedGtx ethtypes.Transaction
)
err = rlp.DecodeBytes(txRLP, &decodedTx)
require.NoError(t, err)
err = rlp.DecodeBytes(gtxRLP, &decodedGtx)
require.NoError(t, err)
require.Equal(t, decodedGtx.Hash(), decodedTx.Hash())
}
func TestValidation(t *testing.T) { func TestValidation(t *testing.T) {
ethTxs := NewTestEthTxs( ethTxs := NewTestEthTxs(
TestChainID, TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1},
[]*ecdsa.PrivateKey{TestPrivKey1},
[]ethcmn.Address{TestAddr1},
) )
testCases := []struct { testCases := []struct {
@ -24,13 +63,13 @@ func TestValidation(t *testing.T) {
}{ }{
{ethTxs[0], func(msg sdk.Msg) sdk.Msg { return msg }, false}, {ethTxs[0], func(msg sdk.Msg) sdk.Msg { return msg }, false},
{ethTxs[0], func(msg sdk.Msg) sdk.Msg { {ethTxs[0], func(msg sdk.Msg) sdk.Msg {
tx := msg.(Transaction) tx := msg.(*Transaction)
tx.Data.Price = sdk.NewInt(-1) tx.data.Price = big.NewInt(-1)
return tx return tx
}, true}, }, true},
{ethTxs[0], func(msg sdk.Msg) sdk.Msg { {ethTxs[0], func(msg sdk.Msg) sdk.Msg {
tx := msg.(Transaction) tx := msg.(*Transaction)
tx.Data.Amount = sdk.NewInt(-1) tx.data.Amount = big.NewInt(-1)
return tx return tx
}, true}, }, true},
} }
@ -47,147 +86,57 @@ func TestValidation(t *testing.T) {
} }
} }
func TestHasEmbeddedTx(t *testing.T) { func TestTransactionVerifySig(t *testing.T) {
testCodec := NewTestCodec() txs := NewTestEthTxs(
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))} TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1},
sdkTxs := NewTestSDKTxs(
testCodec, TestChainID, msgs, []*ecdsa.PrivateKey{TestPrivKey1},
[]int64{0}, []int64{0}, NewStdFee(),
)
require.True(t, sdkTxs[0].HasEmbeddedTx(TestSDKAddress))
ethTxs := NewTestEthTxs(
TestChainID,
[]*ecdsa.PrivateKey{TestPrivKey1},
[]ethcmn.Address{TestAddr1},
)
require.False(t, ethTxs[0].HasEmbeddedTx(TestSDKAddress))
}
func TestGetEmbeddedTx(t *testing.T) {
testCodec := NewTestCodec()
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))}
ethTxs := NewTestEthTxs(
TestChainID,
[]*ecdsa.PrivateKey{TestPrivKey1},
[]ethcmn.Address{TestAddr1},
)
sdkTxs := NewTestSDKTxs(
testCodec, TestChainID, msgs, []*ecdsa.PrivateKey{TestPrivKey1},
[]int64{0}, []int64{0}, NewStdFee(),
) )
etx, err := sdkTxs[0].GetEmbeddedTx(testCodec) addr, err := txs[0].VerifySig(TestChainID)
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, etx.Messages) require.Equal(t, TestAddr1, addr)
etx, err = ethTxs[0].GetEmbeddedTx(testCodec) addr, err = txs[0].VerifySig(big.NewInt(100))
require.Error(t, err) require.Error(t, err)
require.Empty(t, etx.Messages) require.NotEqual(t, TestAddr1, addr)
}
func TestTransactionGetMsgs(t *testing.T) {
ethTxs := NewTestEthTxs(
TestChainID,
[]*ecdsa.PrivateKey{TestPrivKey1},
[]ethcmn.Address{TestAddr1},
)
msgs := ethTxs[0].GetMsgs()
require.Len(t, msgs, 1)
require.Equal(t, ethTxs[0], msgs[0])
expectedMsgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))}
etx := NewTestEmbeddedTx(
TestChainID, expectedMsgs, []*ecdsa.PrivateKey{TestPrivKey1},
[]int64{0}, []int64{0}, NewStdFee(),
)
msgs = etx.GetMsgs()
require.Len(t, msgs, len(expectedMsgs))
require.Equal(t, expectedMsgs, msgs)
}
func TestGetRequiredSigners(t *testing.T) {
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))}
etx := NewTestEmbeddedTx(
TestChainID, msgs, []*ecdsa.PrivateKey{TestPrivKey1},
[]int64{0}, []int64{0}, NewStdFee(),
)
signers := etx.(EmbeddedTx).GetRequiredSigners()
require.Equal(t, []sdk.AccAddress{sdk.AccAddress(TestAddr1.Bytes())}, signers)
}
func TestVerifySig(t *testing.T) {
ethTx := NewTestEthTxs(
TestChainID,
[]*ecdsa.PrivateKey{TestPrivKey1},
[]ethcmn.Address{TestAddr1},
)[0]
addr, err := ethTx.VerifySig(TestChainID.BigInt())
require.Nil(t, err, "Sig verification failed")
require.Equal(t, TestAddr1, addr, "Address is not the same")
} }
func TestTxDecoder(t *testing.T) { func TestTxDecoder(t *testing.T) {
testCodec := NewTestCodec() testCodec := NewTestCodec()
txDecoder := TxDecoder(testCodec, TestSDKAddress) txDecoder := TxDecoder(testCodec, TestSDKAddr)
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))} msgs := []sdk.Msg{sdk.NewTestMsg()}
// create a non-SDK Ethereum transaction // create a non-SDK Ethereum transaction
emintTx := NewTransaction( txs := NewTestEthTxs(
uint64(0), TestAddr1, sdk.NewInt(10), 100, sdk.NewInt(100), nil, TestChainID, []int64{0}, []ethcmn.Address{TestAddr1}, []*ecdsa.PrivateKey{TestPrivKey1},
) )
emintTx.Sign(TestChainID, TestPrivKey1)
txBytes, err := rlp.EncodeToBytes(txs[0])
require.NoError(t, err)
// require the transaction to properly decode into a Transaction // require the transaction to properly decode into a Transaction
txBytes := testCodec.MustMarshalBinary(emintTx) decodedTx, err := txDecoder(txBytes)
tx, err := txDecoder(txBytes)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, emintTx, tx) require.IsType(t, Transaction{}, decodedTx)
require.Equal(t, txs[0].data, (decodedTx.(Transaction)).data)
// create embedded transaction and encode // create a SDK (auth.StdTx) transaction and encode
etx := NewTestEmbeddedTx( txs = NewTestSDKTxs(
TestChainID, msgs, []*ecdsa.PrivateKey{TestPrivKey1}, testCodec, TestChainID, TestSDKAddr, msgs, []int64{0}, []int64{0},
[]int64{0}, []int64{0}, NewStdFee(), []*ecdsa.PrivateKey{TestPrivKey1}, NewTestStdFee(),
) )
payload := testCodec.MustMarshalBinary(etx) txBytes, err = rlp.EncodeToBytes(txs[0])
require.NoError(t, err)
expectedEtx := EmbeddedTx{}
testCodec.UnmarshalBinary(payload, &expectedEtx)
emintTx = NewTransaction(
uint64(0), TestSDKAddress, sdk.NewInt(10), 100,
sdk.NewInt(100), payload,
)
emintTx.Sign(TestChainID, TestPrivKey1)
// require the transaction to properly decode into a Transaction // require the transaction to properly decode into a Transaction
txBytes = testCodec.MustMarshalBinary(emintTx) stdTx := NewTestStdTx(TestChainID, msgs, []int64{0}, []int64{0}, []*ecdsa.PrivateKey{TestPrivKey1}, NewTestStdFee())
tx, err = txDecoder(txBytes) decodedTx, err = txDecoder(txBytes)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expectedEtx, tx) require.IsType(t, auth.StdTx{}, decodedTx)
require.Equal(t, stdTx, decodedTx)
// require the decoding to fail when no transaction bytes are given // require the decoding to fail when no transaction bytes are given
tx, err = txDecoder([]byte{}) decodedTx, err = txDecoder([]byte{})
require.Error(t, err) require.Error(t, err)
require.Nil(t, tx) require.Nil(t, decodedTx)
// create a non-SDK Ethereum transaction with an SDK address and garbage payload
emintTx = NewTransaction(
uint64(0), TestSDKAddress, sdk.NewInt(10), 100, sdk.NewInt(100), []byte("garbage"),
)
emintTx.Sign(TestChainID, TestPrivKey1)
// require the transaction to fail decoding as the payload is invalid
txBytes = testCodec.MustMarshalBinary(emintTx)
tx, err = txDecoder(txBytes)
require.Error(t, err)
require.Nil(t, tx)
} }

View File

@ -2,8 +2,11 @@ package types
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/sha256"
"fmt" "fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common"
ethcrypto "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto"
@ -32,10 +35,18 @@ func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error {
pk, err := ethcrypto.SigToPub(signBytes, sig) pk, err := ethcrypto.SigToPub(signBytes, sig)
if err != nil { if err != nil {
return errors.Wrap(err, "signature verification failed") return errors.Wrap(err, "failed to derive public key from signature")
} else if ethcrypto.PubkeyToAddress(*pk) != signer { } else if ethcrypto.PubkeyToAddress(*pk) != signer {
return fmt.Errorf("invalid signature for signer: %s", signer) return fmt.Errorf("invalid signature for signer: %s", signer)
} }
return nil return nil
} }
// GetStdTxSignBytes returns the signature bytes for an auth.StdTx transaction
// that is compatible with Ethereum's signature mechanism.
func GetStdTxSignBytes(chainID string, accNum int64, seq int64, fee auth.StdFee, msgs []sdk.Msg, memo string) []byte {
signBytes := auth.StdSignBytes(chainID, accNum, seq, fee, msgs, "")
hash := sha256.Sum256(signBytes)
return hash[:]
}

View File

@ -12,12 +12,8 @@ import (
func TestValidateSigner(t *testing.T) { func TestValidateSigner(t *testing.T) {
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))} msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(TestAddr1.Bytes()))}
// create message signing structure // create message signing structure and bytes
signEtx := EmbeddedTxSign{TestChainID.String(), 0, 0, msgs, NewStdFee()} signBytes := GetStdTxSignBytes(TestChainID.String(), 0, 0, NewTestStdFee(), msgs, "")
// create signing bytes and sign
signBytes, err := signEtx.Bytes()
require.NoError(t, err)
// require signing not to fail // require signing not to fail
sig, err := ethcrypto.Sign(signBytes, TestPrivKey1) sig, err := ethcrypto.Sign(signBytes, TestPrivKey1)
@ -33,4 +29,8 @@ func TestValidateSigner(t *testing.T) {
// require signature to be invalid // require signature to be invalid
err = ValidateSigner(signBytes, sig, TestAddr1) err = ValidateSigner(signBytes, sig, TestAddr1)
require.Error(t, err) require.Error(t, err)
// require invalid signature bytes return an error
err = ValidateSigner([]byte{}, sig, TestAddr2)
require.Error(t, err)
} }

View File

@ -3,7 +3,6 @@ package types
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
) )
var typesCodec = wire.NewCodec() var typesCodec = wire.NewCodec()
@ -16,10 +15,6 @@ func init() {
// codec. // codec.
func RegisterWire(codec *wire.Codec) { func RegisterWire(codec *wire.Codec) {
sdk.RegisterWire(codec) sdk.RegisterWire(codec)
wire.RegisterCrypto(codec) codec.RegisterConcrete(&Transaction{}, "types/Transaction", nil)
auth.RegisterWire(codec) codec.RegisterConcrete(&Account{}, "types/Account", nil)
codec.RegisterConcrete(&EthSignature{}, "types/EthSignature", nil)
codec.RegisterConcrete(TxData{}, "types/TxData", nil)
codec.RegisterConcrete(Transaction{}, "types/Transaction", nil)
codec.RegisterConcrete(EmbeddedTx{}, "types/EmbeddedTx", nil)
} }

View File

@ -1,10 +1,26 @@
package version package version
import (
"fmt"
"runtime"
)
// AppName represents the application name as the 'user agent' on the larger Ethereum network.
const AppName = "Ethermint"
// Version contains the application semantic version. // Version contains the application semantic version.
// //
// TODO: How do we want to version this being that an initial Ethermint has // TODO: How do we want to version this being that an initial Ethermint has
// been developed? // been developed?
const Version = "0.0.0" const Version = "0.0.0"
// ProtocolVersion is the supported Ethereum protocol version (e.g., Homestead, Olympic, etc.)
const ProtocolVersion = "63"
// GitCommit contains the git SHA1 short hash set by build flags. // GitCommit contains the git SHA1 short hash set by build flags.
var GitCommit = "" var GitCommit = ""
// ClientVersion returns the full version string for identification on the larger Ethereum network.
func ClientVersion() string {
return fmt.Sprintf("%s/%s+%s/%s/%s", AppName, Version, GitCommit, runtime.GOOS, runtime.Version())
}