This commit is contained in:
rhuairahrighairigh 2019-06-07 12:59:19 +01:00
parent 28eb2d6b31
commit 061c27bbc6
50 changed files with 1510 additions and 5399 deletions

View File

@ -1,8 +0,0 @@
vendor/
Dockerfile
*.sublime-project
*.sublime-workspace
.kvd/
.kvcli/
scratch/
testnets/

View File

@ -1,22 +0,0 @@
# Start with go container
FROM golang:alpine AS builder
WORKDIR /go/src/github.com/kava-labs/kava
# Install go package manager
#RUN curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - doesn't work as alpine has no curl
RUN apk add --no-cache git && go get -u github.com/golang/dep/cmd/dep
# Install go packages (without updating Gopkg, as there is no source code to update from)(also with -v for verbose)
ADD Gopkg.toml Gopkg.lock ./
RUN dep ensure --vendor-only -v
# Copy in app code and build
COPY . .
RUN go build ./cmd/kvd && go build ./cmd/kvcli
# Copy app binary over to small container.
# Using alpine instad of scratch to aid in debugging and avoid complicated compile
# note the home directory for alpine is /root/
FROM alpine
COPY --from=builder /go/src/github.com/kava-labs/kava/kvd /go/src/github.com/kava-labs/kava/kvcli /usr/bin/
CMD ["kvd", "start"]

730
Gopkg.lock generated
View File

@ -1,730 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0"
name = "github.com/bartekn/go-bip39"
packages = ["."]
pruneopts = "UT"
revision = "a05967ea095d81c8fe4833776774cfaff8e5036c"
[[projects]]
branch = "master"
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "UT"
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
digest = "1:1343a2963481a305ca4d051e84bc2abd16b601ee22ed324f8d605de1adb291b0"
name = "github.com/bgentry/speakeasy"
packages = ["."]
pruneopts = "UT"
revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd"
version = "v0.1.0"
[[projects]]
branch = "master"
digest = "1:70f6b224a59b2fa453debffa85c77f71063d8754b90c8c4fbad5794e2c382b0f"
name = "github.com/brejski/hid"
packages = ["."]
pruneopts = "UT"
revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc"
[[projects]]
branch = "master"
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "f899737d7f2764dc13e4d01ff00108ec58f766a9"
[[projects]]
digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2"
name = "github.com/btcsuite/btcutil"
packages = ["bech32"]
pruneopts = "UT"
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
branch = "master"
digest = "1:64eb47e3881f808c83f0a3015f626d21ccc4a4eafbc530a6454e97296ededbef"
name = "github.com/cosmos/cosmos-sdk"
packages = [
"baseapp",
"client",
"client/context",
"client/keys",
"client/rpc",
"client/tx",
"client/utils",
"crypto",
"crypto/keys",
"crypto/keys/bcrypt",
"crypto/keys/bip39",
"crypto/keys/hd",
"server",
"server/config",
"store",
"tests",
"types",
"version",
"wire",
"x/auth",
"x/auth/client/cli",
"x/auth/client/context",
"x/auth/client/rest",
"x/bank",
"x/bank/client",
"x/bank/client/cli",
"x/bank/client/rest",
"x/mock",
"x/params",
"x/slashing",
"x/slashing/client/cli",
"x/slashing/client/rest",
"x/stake",
"x/stake/client/cli",
"x/stake/client/rest",
"x/stake/keeper",
"x/stake/tags",
"x/stake/types",
]
pruneopts = "UT"
revision = "4a54b3ddbde412bea07250680a09fa6cbf1c3824"
source = "github.com/kava-labs/cosmos-sdk"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
name = "github.com/ebuchman/fail-test"
packages = ["."]
pruneopts = "UT"
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
pruneopts = "UT"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11"
name = "github.com/go-kit/kit"
packages = [
"log",
"log/level",
"log/term",
"metrics",
"metrics/discard",
"metrics/internal/lv",
"metrics/prometheus",
]
pruneopts = "UT"
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659"
name = "github.com/go-logfmt/logfmt"
packages = ["."]
pruneopts = "UT"
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406"
name = "github.com/go-stack/stack"
packages = ["."]
pruneopts = "UT"
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
"jsonpb",
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types",
]
pruneopts = "UT"
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
version = "v1.1.1"
[[projects]]
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp",
]
pruneopts = "UT"
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = "UT"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"
name = "github.com/gorilla/context"
packages = ["."]
pruneopts = "UT"
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
version = "v1.1.1"
[[projects]]
digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f"
name = "github.com/gorilla/mux"
packages = ["."]
pruneopts = "UT"
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]]
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
name = "github.com/gorilla/websocket"
packages = ["."]
pruneopts = "UT"
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token",
]
pruneopts = "UT"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = "UT"
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
branch = "master"
digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214"
name = "github.com/jmhodges/levigo"
packages = ["."]
pruneopts = "UT"
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]]
branch = "master"
digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72"
name = "github.com/kr/logfmt"
packages = ["."]
pruneopts = "UT"
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
name = "github.com/magiconair/properties"
packages = ["."]
pruneopts = "UT"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "UT"
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
branch = "master"
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0"
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/promhttp",
]
pruneopts = "UT"
revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632"
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "UT"
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
[[projects]]
branch = "master"
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model",
]
pruneopts = "UT"
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
[[projects]]
branch = "master"
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs",
]
pruneopts = "UT"
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
[[projects]]
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
pruneopts = "UT"
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "UT"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
[[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "UT"
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
digest = "1:645cabccbb4fa8aab25a956cbcbdf6a6845ca736b2c64e197ca7cbb9d210b939"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = "UT"
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
version = "v0.0.3"
[[projects]]
branch = "master"
digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2"
[[projects]]
digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
version = "v1.0.2"
[[projects]]
digest = "1:59e7dceb53b4a1e57eb1eb0bf9951ff0c25912df7660004a789b62b4e8cdca3b"
name = "github.com/spf13/viper"
packages = ["."]
pruneopts = "UT"
revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
version = "v1.0.2"
[[projects]]
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require",
]
pruneopts = "UT"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
"leveldb/cache",
"leveldb/comparer",
"leveldb/errors",
"leveldb/filter",
"leveldb/iterator",
"leveldb/journal",
"leveldb/memdb",
"leveldb/opt",
"leveldb/storage",
"leveldb/table",
"leveldb/util",
]
pruneopts = "UT"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
[[projects]]
branch = "master"
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519",
]
pruneopts = "UT"
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c"
version = "v0.12.0-rc0"
[[projects]]
digest = "1:d4a15d404afbf591e8be16fcda7f5ac87948d5c7531f9d909fd84cc730ab16e2"
name = "github.com/tendermint/iavl"
packages = ["."]
pruneopts = "UT"
revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9"
version = "v0.9.2"
[[projects]]
digest = "1:4f15e95fe3888cc75dd34f407d6394cbc7fd3ff24920851b92b295f6a8b556e6"
name = "github.com/tendermint/tendermint"
packages = [
"abci/client",
"abci/example/code",
"abci/example/kvstore",
"abci/server",
"abci/types",
"blockchain",
"cmd/tendermint/commands",
"config",
"consensus",
"consensus/types",
"crypto",
"crypto/armor",
"crypto/ed25519",
"crypto/encoding/amino",
"crypto/merkle",
"crypto/secp256k1",
"crypto/tmhash",
"crypto/xsalsa20symmetric",
"evidence",
"libs/autofile",
"libs/bech32",
"libs/cli",
"libs/cli/flags",
"libs/clist",
"libs/common",
"libs/db",
"libs/events",
"libs/flowrate",
"libs/log",
"libs/pubsub",
"libs/pubsub/query",
"lite",
"lite/client",
"lite/errors",
"lite/files",
"lite/proxy",
"mempool",
"node",
"p2p",
"p2p/conn",
"p2p/pex",
"p2p/upnp",
"privval",
"proxy",
"rpc/client",
"rpc/core",
"rpc/core/types",
"rpc/grpc",
"rpc/lib",
"rpc/lib/client",
"rpc/lib/server",
"rpc/lib/types",
"state",
"state/txindex",
"state/txindex/kv",
"state/txindex/null",
"types",
"version",
]
pruneopts = "UT"
revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e"
version = "v0.23.1-rc0"
[[projects]]
digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6"
name = "github.com/zondax/ledger-goclient"
packages = ["."]
pruneopts = "UT"
revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259"
[[projects]]
branch = "master"
digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5"
name = "golang.org/x/crypto"
packages = [
"blowfish",
"chacha20poly1305",
"curve25519",
"hkdf",
"internal/chacha20",
"internal/subtle",
"nacl/box",
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"pbkdf2",
"poly1305",
"ripemd160",
"salsa20/salsa",
]
pruneopts = "UT"
revision = "de0752318171da717af4ce24d0a2e8626afaeb11"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"netutil",
"trace",
]
pruneopts = "UT"
revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f"
[[projects]]
branch = "master"
digest = "1:4bd75b1a219bc590b05c976bbebf47f4e993314ebb5c7cbf2efe05a09a184d54"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "383e8b2c3b9e36c4076b235b32537292176bae20"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"balancer/base",
"balancer/roundrobin",
"codes",
"connectivity",
"credentials",
"encoding",
"encoding/proto",
"grpclog",
"internal",
"internal/backoff",
"internal/channelz",
"internal/grpcrand",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"resolver/dns",
"resolver/passthrough",
"stats",
"status",
"tap",
"transport",
]
pruneopts = "UT"
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
version = "v1.13.0"
[[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/cosmos/cosmos-sdk/baseapp",
"github.com/cosmos/cosmos-sdk/client",
"github.com/cosmos/cosmos-sdk/client/context",
"github.com/cosmos/cosmos-sdk/client/keys",
"github.com/cosmos/cosmos-sdk/client/rpc",
"github.com/cosmos/cosmos-sdk/client/tx",
"github.com/cosmos/cosmos-sdk/client/utils",
"github.com/cosmos/cosmos-sdk/crypto/keys",
"github.com/cosmos/cosmos-sdk/server",
"github.com/cosmos/cosmos-sdk/server/config",
"github.com/cosmos/cosmos-sdk/tests",
"github.com/cosmos/cosmos-sdk/types",
"github.com/cosmos/cosmos-sdk/version",
"github.com/cosmos/cosmos-sdk/wire",
"github.com/cosmos/cosmos-sdk/x/auth",
"github.com/cosmos/cosmos-sdk/x/auth/client/cli",
"github.com/cosmos/cosmos-sdk/x/auth/client/context",
"github.com/cosmos/cosmos-sdk/x/auth/client/rest",
"github.com/cosmos/cosmos-sdk/x/bank",
"github.com/cosmos/cosmos-sdk/x/bank/client/cli",
"github.com/cosmos/cosmos-sdk/x/bank/client/rest",
"github.com/cosmos/cosmos-sdk/x/mock",
"github.com/cosmos/cosmos-sdk/x/params",
"github.com/cosmos/cosmos-sdk/x/slashing",
"github.com/cosmos/cosmos-sdk/x/slashing/client/cli",
"github.com/cosmos/cosmos-sdk/x/slashing/client/rest",
"github.com/cosmos/cosmos-sdk/x/stake",
"github.com/cosmos/cosmos-sdk/x/stake/client/cli",
"github.com/cosmos/cosmos-sdk/x/stake/client/rest",
"github.com/gorilla/mux",
"github.com/pkg/errors",
"github.com/spf13/cobra",
"github.com/spf13/pflag",
"github.com/spf13/viper",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"github.com/tendermint/go-amino",
"github.com/tendermint/tendermint/abci/types",
"github.com/tendermint/tendermint/config",
"github.com/tendermint/tendermint/crypto",
"github.com/tendermint/tendermint/crypto/ed25519",
"github.com/tendermint/tendermint/libs/bech32",
"github.com/tendermint/tendermint/libs/cli",
"github.com/tendermint/tendermint/libs/common",
"github.com/tendermint/tendermint/libs/db",
"github.com/tendermint/tendermint/libs/log",
"github.com/tendermint/tendermint/node",
"github.com/tendermint/tendermint/p2p",
"github.com/tendermint/tendermint/privval",
"github.com/tendermint/tendermint/proxy",
"github.com/tendermint/tendermint/rpc/core/types",
"github.com/tendermint/tendermint/rpc/lib/server",
"github.com/tendermint/tendermint/types",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,60 +0,0 @@
[[constraint]]
name = "github.com/cosmos/cosmos-sdk"
branch="master"
source="github.com/kava-labs/cosmos-sdk"
# Copied from cosmos-sdk, constraints switched to overrides
[[override]]
name = "github.com/bgentry/speakeasy"
version = "~0.1.0"
[[override]]
name = "github.com/golang/protobuf"
version = "=1.1.0"
[[override]]
name = "github.com/mattn/go-isatty"
version = "~0.0.3"
[[override]]
name = "github.com/spf13/cobra"
version = "~0.0.1"
[[override]]
name = "github.com/spf13/viper"
version = "~1.0.0"
[[override]]
name = "github.com/pkg/errors"
version = "=0.8.0"
[[override]]
name = "github.com/stretchr/testify"
version = "=1.2.1"
[[override]]
name = "github.com/tendermint/go-amino"
version = "=v0.12.0-rc0"
[[override]]
name = "github.com/tendermint/iavl"
version = "=v0.9.2"
[[override]]
name = "github.com/tendermint/tendermint"
version = "=v0.23.1-rc0"
[[override]]
name = "github.com/bartekn/go-bip39"
revision = "a05967ea095d81c8fe4833776774cfaff8e5036c"
[[override]]
name = "github.com/zondax/ledger-goclient"
revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259"
[prune]
go-tests = true
unused-packages = true

View File

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Kava Labs
Copyright 2019 Kava Labs, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,34 +1 @@
<h1>
<img alt="Kava Blockchain" src="./kava-logo.svg" width="200">
</h1>
A decentralized fast-finality blockchain for interoperable payment channel networks.
Providing a base layer currency to settle interoperable payments at high throughput. Leveraging fast finality to allow short cross currency channel timeouts. Proof of stake to provide performance and effective light clients. Payment channels to enable high volume low value transfers at realistic throughput. Streaming credit payments for effective routing and interoperability across chains.
Proudly building on the work of [Cosmos](https://github.com/cosmos/cosmos-sdk) and [Interledger](https://github.com/interledger/rfcs).
# Project Status
[![Testnet](https://img.shields.io/badge/testnet-live-brightgreen.svg)](http://kava-test-3.node.connector.kava.io:17127/abci_info)
[![Go Report Card](https://goreportcard.com/badge/github.com/kava-labs/kava)](https://goreportcard.com/report/github.com/kava-labs/kava)
[![GitHub](https://img.shields.io/github/license/kava-labs/kava.svg)](https://github.com/Kava-Labs/kava/blob/master/LICENSE.md)
We're currently in a very early public testnet, with future features being implemented.
If you're interested in being a validator join the chat and checkout the setup instructions below.
## Community
- [Telegram](https://t.me/kavalabs)
- [Medium](https://medium.com/kava-labs)
- [Validator Chat](https://riot.im/app/#/room/#kava-validators:matrix.org)
# Find Out More
- [Installation and Setup](docs/setup.md)
- [Basic Usage](docs/usage.md)
- [Payment Channels](docs/paychans.md)
- [Running a Validator](docs/validators.md)
- [Upgrading to a New Testnet](docs/upgrade.md)
# _

254
app/app.go Normal file
View File

@ -0,0 +1,254 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2019 Kava Labs
package app
import (
"io"
"os"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/params"
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
)
const appName = "KavaApp"
var (
// default home directories for cli
DefaultCLIHome = os.ExpandEnv("$HOME/.kvcli")
// default home directories for daemon
DefaultNodeHome = os.ExpandEnv("$HOME/.kvd")
// The ModuleBasicManager is in charge of setting up basic,
// non-dependant module elements, such as codec registration
// and genesis verification.
ModuleBasics module.BasicManager
)
func init() {
ModuleBasics = module.NewBasicManager(
genaccounts.AppModuleBasic{},
genutil.AppModuleBasic{},
auth.AppModuleBasic{},
bank.AppModuleBasic{},
staking.AppModuleBasic{},
mint.AppModuleBasic{},
distr.AppModuleBasic{},
gov.NewAppModuleBasic(paramsclient.ProposalHandler, distrclient.ProposalHandler),
params.AppModuleBasic{},
crisis.AppModuleBasic{},
slashing.AppModuleBasic{},
)
}
// custom tx codec
func MakeCodec() *codec.Codec {
var cdc = codec.New()
ModuleBasics.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
return cdc
}
// SetAddressPrefixes sets the bech32 address prefixes on an sdk config.
func SetAddressPrefixes(config *sdk.Config) {
config.SetBech32PrefixForAccount("k", "k"+"pub")
config.SetBech32PrefixForValidator("k"+"val"+"oper", "k"+"val"+"oper"+"pub")
config.SetBech32PrefixForConsensusNode("k"+"val"+"cons", "k"+"val"+"cons"+"pub")
}
// Extended ABCI application
type App struct {
*bam.BaseApp
cdc *codec.Codec
invCheckPeriod uint
// keys to access the substores
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyStaking *sdk.KVStoreKey
tkeyStaking *sdk.TransientStoreKey
keySlashing *sdk.KVStoreKey
keyMint *sdk.KVStoreKey
keyDistr *sdk.KVStoreKey
tkeyDistr *sdk.TransientStoreKey
keyGov *sdk.KVStoreKey
keyFeeCollection *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
tkeyParams *sdk.TransientStoreKey
// keepers
accountKeeper auth.AccountKeeper
feeCollectionKeeper auth.FeeCollectionKeeper
bankKeeper bank.Keeper
stakingKeeper staking.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
crisisKeeper crisis.Keeper
paramsKeeper params.Keeper
// the module manager
mm *module.Manager
}
// NewApp returns a reference to an initialized App.
func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp)) *App {
cdc := MakeCodec()
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
bApp.SetAppVersion(version.Version)
var app = &App{
BaseApp: bApp,
cdc: cdc,
invCheckPeriod: invCheckPeriod,
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
keyMint: sdk.NewKVStoreKey(mint.StoreKey),
keyDistr: sdk.NewKVStoreKey(distr.StoreKey),
tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey),
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
keyGov: sdk.NewKVStoreKey(gov.StoreKey),
keyFeeCollection: sdk.NewKVStoreKey(auth.FeeStoreKey),
keyParams: sdk.NewKVStoreKey(params.StoreKey),
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
}
// init params keeper and subspaces
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams, params.DefaultCodespace)
authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace)
bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace)
stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace)
mintSubspace := app.paramsKeeper.Subspace(mint.DefaultParamspace)
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace)
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
// add keepers
app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.keyAccount, authSubspace, auth.ProtoBaseAccount)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace)
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
stakingKeeper := staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking, app.bankKeeper,
stakingSubspace, staking.DefaultCodespace)
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, mintSubspace, &stakingKeeper, app.feeCollectionKeeper)
app.distrKeeper = distr.NewKeeper(app.cdc, app.keyDistr, distrSubspace, app.bankKeeper, &stakingKeeper,
app.feeCollectionKeeper, distr.DefaultCodespace)
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, &stakingKeeper,
slashingSubspace, slashing.DefaultCodespace)
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.distrKeeper,
app.bankKeeper, app.feeCollectionKeeper)
// register the proposal types
govRouter := gov.NewRouter()
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper, govSubspace,
app.bankKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
app.stakingKeeper = *stakingKeeper.SetHooks(
staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()))
app.mm = module.NewManager(
genaccounts.NewAppModule(app.accountKeeper),
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.accountKeeper, app.feeCollectionKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
crisis.NewAppModule(app.crisisKeeper, app.Logger()),
distr.NewAppModule(app.distrKeeper),
gov.NewAppModule(app.govKeeper),
mint.NewAppModule(app.mintKeeper),
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
staking.NewAppModule(app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
)
// During begin block slashing happens after distr.BeginBlocker so that
// there is nothing left over in the validator fee pool, so as to keep the
// CanWithdrawInvariant invariant.
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName)
app.mm.SetOrderEndBlockers(gov.ModuleName, staking.ModuleName)
// genutils must occur after staking so that pools are properly
// initialized with tokens from genesis accounts.
app.mm.SetOrderInitGenesis(genaccounts.ModuleName, distr.ModuleName,
staking.ModuleName, auth.ModuleName, bank.ModuleName, slashing.ModuleName,
gov.ModuleName, mint.ModuleName, crisis.ModuleName, genutil.ModuleName)
app.mm.RegisterInvariants(&app.crisisKeeper)
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
// initialize stores
app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keyMint,
app.keyDistr, app.keySlashing, app.keyGov, app.keyFeeCollection,
app.keyParams, app.tkeyParams, app.tkeyStaking, app.tkeyDistr)
// initialize BaseApp
app.SetInitChainer(app.InitChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper, auth.DefaultSigVerificationGasConsumer))
app.SetEndBlocker(app.EndBlocker)
if loadLatest {
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
}
}
return app
}
// application updates every begin block
func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
return app.mm.BeginBlock(ctx, req)
}
// application updates every end block
func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
return app.mm.EndBlock(ctx, req)
}
// application update at chain initialization
func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var genesisState GenesisState
app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState)
return app.mm.InitGenesis(ctx, genesisState)
}
// load a particular height
func (app *App) LoadHeight(height int64) error {
return app.LoadVersion(height, app.keyMain)
}

168
app/export.go Normal file
View File

@ -0,0 +1,168 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2019 Kava Labs
package app
import (
"encoding/json"
"log"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/staking"
)
// export the state of the app for a genesis file
func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string,
) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
// as if they could withdraw from the start of the next block
ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()})
if forZeroHeight {
app.prepForZeroHeightGenesis(ctx, jailWhiteList)
}
genState := app.mm.ExportGenesis(ctx)
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
validators = staking.WriteValidators(ctx, app.stakingKeeper)
return appState, validators, nil
}
// prepare for fresh start at zero height
// NOTE zero height genesis is a temporary feature which will be deprecated
// in favour of export at a block height
func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) {
applyWhiteList := false
//Check if there is a whitelist
if len(jailWhiteList) > 0 {
applyWhiteList = true
}
whiteListMap := make(map[string]bool)
for _, addr := range jailWhiteList {
_, err := sdk.ValAddressFromBech32(addr)
if err != nil {
log.Fatal(err)
}
whiteListMap[addr] = true
}
/* Just to be safe, assert the invariants on current state. */
app.crisisKeeper.AssertInvariants(ctx, app.Logger())
/* Handle fee distribution state. */
// withdraw all validator commission
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) {
_, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
return false
})
// withdraw all delegator rewards
dels := app.stakingKeeper.GetAllDelegations(ctx)
for _, delegation := range dels {
_, _ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
}
// clear validator slash events
app.distrKeeper.DeleteAllValidatorSlashEvents(ctx)
// clear validator historical rewards
app.distrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
// set context height to zero
height := ctx.BlockHeight()
ctx = ctx.WithBlockHeight(0)
// reinitialize all validators
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) {
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
feePool := app.distrKeeper.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(scraps)
app.distrKeeper.SetFeePool(ctx, feePool)
app.distrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
return false
})
// reinitialize all delegations
for _, del := range dels {
app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
app.distrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
}
// reset context height
ctx = ctx.WithBlockHeight(height)
/* Handle staking state. */
// iterate through redelegations, reset creation height
app.stakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
for i := range red.Entries {
red.Entries[i].CreationHeight = 0
}
app.stakingKeeper.SetRedelegation(ctx, red)
return false
})
// iterate through unbonding delegations, reset creation height
app.stakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
for i := range ubd.Entries {
ubd.Entries[i].CreationHeight = 0
}
app.stakingKeeper.SetUnbondingDelegation(ctx, ubd)
return false
})
// Iterate through validators by power descending, reset bond heights, and
// update bond intra-tx counters.
store := ctx.KVStore(app.keyStaking)
iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey)
counter := int16(0)
var valConsAddrs []sdk.ConsAddress
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(iter.Key()[1:])
validator, found := app.stakingKeeper.GetValidator(ctx, addr)
if !found {
panic("expected validator, not found")
}
validator.UnbondingHeight = 0
valConsAddrs = append(valConsAddrs, validator.ConsAddress())
if applyWhiteList && !whiteListMap[addr.String()] {
validator.Jailed = true
}
app.stakingKeeper.SetValidator(ctx, validator)
counter++
}
iter.Close()
_ = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
/* Handle slashing state. */
// reset start height on signing infos
app.slashingKeeper.IterateValidatorSigningInfos(
ctx,
func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) {
info.StartHeight = 0
app.slashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
return false
},
)
}

22
app/genesis.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2019 Kava Labs
package app
import (
"encoding/json"
)
// The genesis state of the blockchain is represented here as a map of raw json
// messages key'd by a identifier string.
// The identifier is used to determine which module genesis information belongs
// to so it may be appropriately routed during init chain.
// Within this application default genesis information is retrieved from
// the ModuleBasicManager which populates json from each BasicModule
// object provided to it during init.
type GenesisState map[string]json.RawMessage
// NewDefaultGenesisState generates the default state for gaia.
func NewDefaultGenesisState() GenesisState {
return ModuleBasics.DefaultGenesis()
}

View File

@ -1,139 +1,170 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
// Modifications copyright 2019 Kava Labs
package main
import (
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/libs/cli"
"fmt"
"os"
"path"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/bank"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
paychancmd "github.com/kava-labs/kava/internal/x/paychan/client/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/kava-labs/kava/internal/app"
"github.com/kava-labs/kava/internal/lcd"
)
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/libs/cli"
var (
rootCmd = &cobra.Command{
Use: "kvcli",
Short: "Kava Light-Client",
}
"github.com/kava-labs/_/app"
)
func main() {
// Configure cobra to sort commands
cobra.EnableCommandSorting = false
// get the codec
cdc := app.CreateKavaAppCodec()
// Instantiate the codec for the command line application
cdc := app.MakeCodec()
// add standard rpc commands
rpc.AddCommands(rootCmd)
// Read in the configuration file for the sdk
config := sdk.GetConfig()
app.SetAddressPrefixes(config)
config.Seal()
// Add state commands
tendermintCmd := &cobra.Command{
Use: "tendermint",
Short: "Tendermint state querying subcommands",
}
tendermintCmd.AddCommand(
rpc.BlockCommand(),
rpc.ValidatorCommand(),
)
tx.AddCommands(tendermintCmd, cdc)
// TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do
// with the cdc
advancedCmd := &cobra.Command{
Use: "advanced",
Short: "Advanced subcommands",
rootCmd := &cobra.Command{
Use: "kvcli",
Short: "Command line interface for interacting with kvd",
}
advancedCmd.AddCommand(
tendermintCmd,
lcd.ServeCommand(cdc),
)
// Add --chain-id to persistent flags and mark it required
rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node")
rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error {
return initConfig(rootCmd)
}
// Construct Root Command
rootCmd.AddCommand(
advancedCmd,
rpc.StatusCommand(),
client.ConfigCmd(app.DefaultCLIHome),
queryCmd(cdc),
txCmd(cdc),
client.LineBreak,
lcd.ServeCommand(cdc, registerRoutes),
client.LineBreak,
)
// Add stake commands
stakeCmd := &cobra.Command{
Use: "stake",
Short: "Stake and validation subcommands",
}
stakeCmd.AddCommand(
client.GetCommands(
stakecmd.GetCmdQueryValidator("stake", cdc),
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc),
stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc),
stakecmd.GetCmdQueryRedelegation("stake", cdc),
stakecmd.GetCmdQueryRedelegations("stake", cdc),
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc),
)...)
stakeCmd.AddCommand(
client.PostCommands(
stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond("stake", cdc),
stakecmd.GetCmdRedelegate("stake", cdc),
slashingcmd.GetCmdUnrevoke(cdc),
)...)
rootCmd.AddCommand(
stakeCmd,
)
// Add auth and bank commands
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
)...)
rootCmd.AddCommand(
client.PostCommands( // this just wraps the input cmds with common flags
bankcmd.SendTxCmd(cdc),
)...)
// Add paychan commands
paychanCmd := &cobra.Command{
Use: "paychan",
Short: "Payment channel subcommand",
}
paychanCmd.AddCommand(
client.PostCommands(
paychancmd.CreateChannelCmd(cdc),
paychancmd.GetChannelCmd(cdc, "paychan"), // pass in storeKey
paychancmd.GeneratePaymentCmd(cdc),
paychancmd.VerifyPaymentCmd(cdc, "paychan"), // pass in storeKey
paychancmd.SubmitPaymentCmd(cdc),
)...)
rootCmd.AddCommand(
paychanCmd,
)
// add proxy, version and key info
rootCmd.AddCommand(
keys.Commands(),
client.LineBreak,
version.VersionCmd,
version.Cmd,
client.NewCompletionCmd(rootCmd, true),
)
// prepare and add flags
executor := cli.PrepareMainCmd(rootCmd, "KV", app.DefaultCLIHome)
// Add flags and prefix all env exposed with GA
executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome)
err := executor.Execute()
if err != nil {
panic(err)
fmt.Printf("Failed executing CLI command: %s, exiting...\n", err)
os.Exit(1)
}
}
func queryCmd(cdc *amino.Codec) *cobra.Command {
queryCmd := &cobra.Command{
Use: "query",
Aliases: []string{"q"},
Short: "Querying subcommands",
}
queryCmd.AddCommand(
authcmd.GetAccountCmd(cdc),
client.LineBreak,
rpc.ValidatorCommand(cdc),
rpc.BlockCommand(),
tx.SearchTxCmd(cdc),
tx.QueryTxCmd(cdc),
client.LineBreak,
)
// add modules' query commands
app.ModuleBasics.AddQueryCommands(queryCmd, cdc)
return queryCmd
}
func txCmd(cdc *amino.Codec) *cobra.Command {
txCmd := &cobra.Command{
Use: "tx",
Short: "Transactions subcommands",
}
txCmd.AddCommand(
bankcmd.SendTxCmd(cdc),
client.LineBreak,
authcmd.GetSignCommand(cdc),
authcmd.GetMultiSignCommand(cdc),
client.LineBreak,
tx.GetBroadcastCommand(cdc),
tx.GetEncodeCommand(cdc),
client.LineBreak,
)
// add modules' tx commands
app.ModuleBasics.AddTxCommands(txCmd, cdc)
// remove auth and bank commands as they're mounted under the root tx command
var cmdsToRemove []*cobra.Command
for _, cmd := range txCmd.Commands() {
if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName {
cmdsToRemove = append(cmdsToRemove, cmd)
}
}
txCmd.RemoveCommand(cmdsToRemove...)
return txCmd
}
// registerRoutes registers the routes from the different modules for the LCD.
// NOTE: details on the routes added for each module are in the module documentation
// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go
func registerRoutes(rs *lcd.RestServer) {
client.RegisterRoutes(rs.CliCtx, rs.Mux)
app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux)
}
func initConfig(cmd *cobra.Command) error {
home, err := cmd.PersistentFlags().GetString(cli.HomeFlag)
if err != nil {
return err
}
cfgFile := path.Join(home, "config", "config.toml")
if _, err := os.Stat(cfgFile); err == nil {
viper.SetConfigFile(cfgFile)
if err := viper.ReadInConfig(); err != nil {
return err
}
}
if err := viper.BindPFlag(client.FlagChainID, cmd.PersistentFlags().Lookup(client.FlagChainID)); err != nil {
return err
}
if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil {
return err
}
return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag))
}

View File

@ -1,61 +1,69 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
// Modifications copyright 2019 Kava Labs
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
genaccscli "github.com/cosmos/cosmos-sdk/x/auth/genaccounts/client/cli"
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
"github.com/cosmos/cosmos-sdk/x/staking"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/cli"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/server"
"github.com/kava-labs/kava/internal/app"
"github.com/kava-labs/_/app"
)
// gaiad custom flags
const flagInvCheckPeriod = "inv-check-period"
var invCheckPeriod uint
func main() {
// Create an app codec
cdc := app.CreateKavaAppCodec()
cdc := app.MakeCodec()
config := sdk.GetConfig()
app.SetAddressPrefixes(config)
config.Seal()
// Create a server context (a struct of a tendermint config and a logger)
ctx := server.NewDefaultContext()
// Create the root kvd command
cobra.EnableCommandSorting = false
rootCmd := &cobra.Command{
Use: "kvd",
Short: "Kava Daemon",
Short: "Kava Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
// Add server commands to kvd, passing in the app
appInit := app.KavaAppInit()
appCreator := server.ConstructAppCreator(newApp, "kava") // init db before calling newApp
appExporter := server.ConstructAppExporter(exportAppStateAndTMValidators, "kava")
rootCmd.AddCommand(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome))
rootCmd.AddCommand(genutilcli.CollectGenTxsCmd(ctx, cdc, genaccounts.AppModuleBasic{}, app.DefaultNodeHome))
rootCmd.AddCommand(genutilcli.GenTxCmd(ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{},
genaccounts.AppModuleBasic{}, app.DefaultNodeHome, app.DefaultCLIHome))
rootCmd.AddCommand(genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics))
rootCmd.AddCommand(genaccscli.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome))
rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true))
rootCmd.AddCommand(testnetCmd(ctx, cdc, app.ModuleBasics, genaccounts.AppModuleBasic{}))
rootCmd.AddCommand(replayCmd())
server.AddCommands(ctx, cdc, rootCmd, appInit, appCreator, appExporter)
server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)
// Add custom init command
rootCmd.AddCommand(initTestnetCmd())
// handle envs and add some flags and stuff
executor := cli.PrepareBaseCmd(rootCmd, "KV", app.DefaultNodeHome)
// Run kvd
// prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "KA", app.DefaultNodeHome)
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
0, "Assert registered invariants every N blocks")
err := executor.Execute()
if err != nil {
panic(err)
@ -63,137 +71,26 @@ func main() {
}
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
return app.NewKavaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")))
return app.NewApp(
logger, db, traceStore, true, invCheckPeriod,
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))),
)
}
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, traceStore io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) {
tempApp := app.NewKavaApp(logger, db, traceStore)
return tempApp.ExportAppStateAndValidators()
}
func initTestnetCmd() *cobra.Command {
flagChainID := server.FlagChainID
flagName := server.FlagName
cmd := &cobra.Command{
Use: "init-testnet",
Short: "Setup genesis and config to join testnet.",
Long: "Copy the genesis.json and config.toml files from the testnets folder into the default config directories. Also set the validator moniker.",
RunE: func(cmd *cobra.Command, args []string) error {
// This only works with default config locations
testnetVersion := viper.GetString(flagChainID)
genesisFileName := "genesis.json"
configFileName := "config.toml"
configPath := "config"
testnetsPath := os.ExpandEnv("$GOPATH/src/github.com/kava-labs/kava/testnets/")
// Copy genesis file from testnet folder to config directories
// Copied to .kvcli to enable automatic reading of chain-id
genesis := filepath.Join(testnetsPath, testnetVersion, genesisFileName)
err := copyFile(genesis, filepath.Join(app.DefaultNodeHome, configPath, genesisFileName))
if err != nil {
return err
}
err = copyFile(genesis, filepath.Join(app.DefaultCLIHome, configPath, genesisFileName))
if err != nil {
return err
}
// Copy config file from testnet folder to config directories
// Custom config file specifies seeds and altered ports
// Also add back validator moniker to config file
config := filepath.Join(testnetsPath, testnetVersion, configFileName)
monikerPattern, err := regexp.Compile("moniker = \"[^\n]*\"") // anything that's not a new line
if err != nil {
return err
}
monikerReplaceString := fmt.Sprintf("moniker = \"%v\"", viper.GetString(flagName))
err = copyFile(config, filepath.Join(app.DefaultNodeHome, configPath, configFileName))
if err != nil {
return err
}
err = replaceStringInFile(
filepath.Join(app.DefaultNodeHome, configPath, configFileName),
monikerPattern,
monikerReplaceString)
if err != nil {
return err
}
err = copyFile(config, filepath.Join(app.DefaultCLIHome, configPath, configFileName))
if err != nil {
return err
}
err = replaceStringInFile(
filepath.Join(app.DefaultCLIHome, configPath, configFileName),
monikerPattern,
monikerReplaceString)
if err != nil {
return err
}
return nil
},
}
cmd.Flags().String(flagChainID, "", "testnet chain-id, required")
cmd.Flags().String(flagName, "", "validator moniker, required")
return cmd
}
func copyFile(src string, dst string) error {
// read in source file
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
// create destination file (and any necessary directories)(overwriting if it exists already)
path := filepath.Dir(dst)
err = os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
out, err := os.Create(dst)
if err != nil {
return err
}
defer func() {
cerr := out.Close()
if err == nil {
err = cerr
}
}()
// copy file contents
if _, err = io.Copy(out, in); err != nil {
return err
}
// write to disk
err = out.Sync()
return err
}
// replaceStringInFile finds strings matching a regexp in a file and replaces them with a new string, before saving file.
func replaceStringInFile(filePath string, re *regexp.Regexp, replace string) error {
// get permissions of file
fileInfo, err := os.Stat(filePath)
if err != nil {
return err
}
// read in file contents
in, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
// replace string
newContents := re.ReplaceAll(in, []byte(replace))
// write file
err = ioutil.WriteFile(filePath, newContents, fileInfo.Mode())
if err != nil {
return err
}
return nil
func exportAppStateAndTMValidators(
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
if height != -1 {
appStruct := app.NewApp(logger, db, traceStore, false, uint(1))
err := appStruct.LoadHeight(height)
if err != nil {
return nil, nil, err
}
return appStruct.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
}
appStruct := app.NewApp(logger, db, traceStore, true, uint(1))
return appStruct.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
}

193
cmd/kvd/replay.go Normal file
View File

@ -0,0 +1,193 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2019 Kava Labs
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"time"
cpm "github.com/otiai10/copy"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/tendermint/abci/types"
bcm "github.com/tendermint/tendermint/blockchain"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/proxy"
tmsm "github.com/tendermint/tendermint/state"
tm "github.com/tendermint/tendermint/types"
"github.com/kava-labs/_/app"
)
func replayCmd() *cobra.Command {
return &cobra.Command{
Use: "replay <root-dir>",
Short: "Replay transactions",
RunE: func(_ *cobra.Command, args []string) error {
return replayTxs(args[0])
},
Args: cobra.ExactArgs(1),
}
}
func replayTxs(rootDir string) error {
if false {
// Copy the rootDir to a new directory, to preserve the old one.
fmt.Fprintln(os.Stderr, "Copying rootdir over")
oldRootDir := rootDir
rootDir = oldRootDir + "_replay"
if cmn.FileExists(rootDir) {
cmn.Exit(fmt.Sprintf("temporary copy dir %v already exists", rootDir))
}
if err := cpm.Copy(oldRootDir, rootDir); err != nil {
return err
}
}
configDir := filepath.Join(rootDir, "config")
dataDir := filepath.Join(rootDir, "data")
ctx := server.NewDefaultContext()
// App DB
// appDB := dbm.NewMemDB()
fmt.Fprintln(os.Stderr, "Opening app database")
appDB, err := sdk.NewLevelDB("application", dataDir)
if err != nil {
return err
}
// TM DB
// tmDB := dbm.NewMemDB()
fmt.Fprintln(os.Stderr, "Opening tendermint state database")
tmDB, err := sdk.NewLevelDB("state", dataDir)
if err != nil {
return err
}
// Blockchain DB
fmt.Fprintln(os.Stderr, "Opening blockstore database")
bcDB, err := sdk.NewLevelDB("blockstore", dataDir)
if err != nil {
return err
}
// TraceStore
var traceStoreWriter io.Writer
var traceStoreDir = filepath.Join(dataDir, "trace.log")
traceStoreWriter, err = os.OpenFile(
traceStoreDir,
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
0666,
)
if err != nil {
return err
}
// Application
fmt.Fprintln(os.Stderr, "Creating application")
myapp := app.NewApp(
ctx.Logger, appDB, traceStoreWriter, true, uint(1),
baseapp.SetPruning(store.PruneEverything), // nothing
)
// Genesis
var genDocPath = filepath.Join(configDir, "genesis.json")
genDoc, err := tm.GenesisDocFromFile(genDocPath)
if err != nil {
return err
}
genState, err := tmsm.MakeGenesisState(genDoc)
if err != nil {
return err
}
// tmsm.SaveState(tmDB, genState)
cc := proxy.NewLocalClientCreator(myapp)
proxyApp := proxy.NewAppConns(cc)
err = proxyApp.Start()
if err != nil {
return err
}
defer func() {
_ = proxyApp.Stop()
}()
state := tmsm.LoadState(tmDB)
if state.LastBlockHeight == 0 {
// Send InitChain msg
fmt.Fprintln(os.Stderr, "Sending InitChain msg")
validators := tm.TM2PB.ValidatorUpdates(genState.Validators)
csParams := tm.TM2PB.ConsensusParams(genDoc.ConsensusParams)
req := abci.RequestInitChain{
Time: genDoc.GenesisTime,
ChainId: genDoc.ChainID,
ConsensusParams: csParams,
Validators: validators,
AppStateBytes: genDoc.AppState,
}
res, err := proxyApp.Consensus().InitChainSync(req)
if err != nil {
return err
}
newValidatorz, err := tm.PB2TM.ValidatorUpdates(res.Validators)
if err != nil {
return err
}
newValidators := tm.NewValidatorSet(newValidatorz)
// Take the genesis state.
state = genState
state.Validators = newValidators
state.NextValidators = newValidators
}
// Create executor
fmt.Fprintln(os.Stderr, "Creating block executor")
blockExec := tmsm.NewBlockExecutor(tmDB, ctx.Logger, proxyApp.Consensus(),
tmsm.MockMempool{}, tmsm.MockEvidencePool{})
// Create block store
fmt.Fprintln(os.Stderr, "Creating block store")
blockStore := bcm.NewBlockStore(bcDB)
tz := []time.Duration{0, 0, 0}
for i := int(state.LastBlockHeight) + 1; ; i++ {
fmt.Fprintln(os.Stderr, "Running block ", i)
t1 := time.Now()
// Apply block
fmt.Printf("loading and applying block %d\n", i)
blockmeta := blockStore.LoadBlockMeta(int64(i))
if blockmeta == nil {
fmt.Printf("Couldn't find block meta %d... done?\n", i)
return nil
}
block := blockStore.LoadBlock(int64(i))
if block == nil {
return fmt.Errorf("couldn't find block %d", i)
}
t2 := time.Now()
state, err = blockExec.ApplyBlock(state, blockmeta.BlockID, block)
if err != nil {
return err
}
t3 := time.Now()
tz[0] += t2.Sub(t1)
tz[1] += t3.Sub(t2)
fmt.Fprintf(os.Stderr, "new app hash: %X\n", state.AppHash)
fmt.Fprintln(os.Stderr, tz)
}
}

378
cmd/kvd/testnet.go Normal file
View File

@ -0,0 +1,378 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2019 Kava Labs
package main
// DONTCOVER
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/viper"
tmconfig "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
"github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/staking"
)
var (
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "v"
flagOutputDir = "output-dir"
flagNodeDaemonHome = "node-daemon-home"
flagNodeCLIHome = "node-cli-home"
flagStartingIPAddress = "starting-ip-address"
)
// get cmd to initialize all files for tendermint testnet and application
func testnetCmd(ctx *server.Context, cdc *codec.Codec,
mbm module.BasicManager, genAccIterator genutil.GenesisAccountsIterator) *cobra.Command {
cmd := &cobra.Command{
Use: "testnet",
Short: "Initialize files for a kvd testnet",
Long: `testnet will create "v" number of directories and populate each with
necessary files (private validator, genesis, config, etc.).
Note, strict routability for addresses is turned off in the config file.
Example:
kvd testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
`,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
outputDir := viper.GetString(flagOutputDir)
chainID := viper.GetString(client.FlagChainID)
minGasPrices := viper.GetString(server.FlagMinGasPrices)
nodeDirPrefix := viper.GetString(flagNodeDirPrefix)
nodeDaemonHome := viper.GetString(flagNodeDaemonHome)
nodeCLIHome := viper.GetString(flagNodeCLIHome)
startingIPAddress := viper.GetString(flagStartingIPAddress)
numValidators := viper.GetInt(flagNumValidators)
return InitTestnet(config, cdc, mbm, genAccIterator, outputDir, chainID, minGasPrices,
nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, numValidators)
},
}
cmd.Flags().Int(flagNumValidators, 4,
"Number of validators to initialize the testnet with")
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
"Directory to store initialization data for the testnet")
cmd.Flags().String(flagNodeDirPrefix, "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)")
cmd.Flags().String(flagNodeDaemonHome, "kvd",
"Home directory of the node's daemon configuration")
cmd.Flags().String(flagNodeCLIHome, "kvcli",
"Home directory of the node's cli configuration")
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1",
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
cmd.Flags().String(
client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(
server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
"Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
return cmd
}
const nodeDirPerm = 0755
// Initialize the testnet
func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicManager,
genAccIterator genutil.GenesisAccountsIterator,
outputDir, chainID, minGasPrices, nodeDirPrefix, nodeDaemonHome,
nodeCLIHome, startingIPAddress string, numValidators int) error {
if chainID == "" {
chainID = "chain-" + cmn.RandStr(6)
}
monikers := make([]string, numValidators)
nodeIDs := make([]string, numValidators)
valPubKeys := make([]crypto.PubKey, numValidators)
appConfig := srvconfig.DefaultConfig()
appConfig.MinGasPrices = minGasPrices
var (
accs []genaccounts.GenesisAccount
genFiles []string
)
// generate private keys, node IDs, and initial transactions
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
clientDir := filepath.Join(outputDir, nodeDirName, nodeCLIHome)
gentxsDir := filepath.Join(outputDir, "gentxs")
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
err = os.MkdirAll(clientDir, nodeDirPerm)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
monikers = append(monikers, nodeDirName)
config.Moniker = nodeDirName
ip, err := getIP(i, startingIPAddress)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(config)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
genFiles = append(genFiles, config.GenesisFile())
buf := client.BufferStdin()
prompt := fmt.Sprintf(
"Password for account '%s' (default %s):", nodeDirName, client.DefaultKeyPass,
)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return err
}
if keyPass == "" {
keyPass = client.DefaultKeyPass
}
addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
info := map[string]string{"secret": secret}
cliPrint, err := json.Marshal(info)
if err != nil {
return err
}
// save private key seed words
err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint)
if err != nil {
return err
}
accTokens := sdk.TokensFromTendermintPower(1000)
accStakingTokens := sdk.TokensFromTendermintPower(500)
accs = append(accs, genaccounts.GenesisAccount{
Address: addr,
Coins: sdk.Coins{
sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens),
sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens),
},
})
valTokens := sdk.TokensFromTendermintPower(100)
msg := staking.NewMsgCreateValidator(
sdk.ValAddress(addr),
valPubKeys[i],
sdk.NewCoin(sdk.DefaultBondDenom, valTokens),
staking.NewDescription(nodeDirName, "", "", ""),
staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
sdk.OneInt(),
)
kb, err := keys.NewKeyBaseFromDir(clientDir)
if err != nil {
return err
}
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
txBldr := auth.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb)
signedTx, err := txBldr.SignStdTx(nodeDirName, client.DefaultKeyPass, tx, false)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
txBytes, err := cdc.MarshalJSON(signedTx)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
// gather gentxs folder
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
// TODO: Rename config file to server.toml as it's not particular to Gaia
// (REF: https://github.com/cosmos/cosmos-sdk/issues/4125).
appConfigFilePath := filepath.Join(nodeDir, "config/gaiad.toml")
srvconfig.WriteConfigFile(appConfigFilePath, appConfig)
}
if err := initGenFiles(cdc, mbm, chainID, accs, genFiles, numValidators); err != nil {
return err
}
err := collectGenFiles(
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
outputDir, nodeDirPrefix, nodeDaemonHome, genAccIterator,
)
if err != nil {
return err
}
fmt.Printf("Successfully initialized %d node directories\n", numValidators)
return nil
}
func initGenFiles(cdc *codec.Codec, mbm module.BasicManager, chainID string,
accs []genaccounts.GenesisAccount, genFiles []string, numValidators int) error {
appGenState := mbm.DefaultGenesis()
// set the accounts in the genesis state
appGenState = genaccounts.SetGenesisStateInAppState(cdc, appGenState, accs)
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
if err != nil {
return err
}
genDoc := types.GenesisDoc{
ChainID: chainID,
AppState: appGenStateJSON,
Validators: nil,
}
// generate empty genesis files for each validator and save
for i := 0; i < numValidators; i++ {
if err := genDoc.SaveAs(genFiles[i]); err != nil {
return err
}
}
return nil
}
func collectGenFiles(
cdc *codec.Codec, config *tmconfig.Config, chainID string,
monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string,
genAccIterator genutil.GenesisAccountsIterator) error {
var appState json.RawMessage
genTime := tmtime.Now()
for i := 0; i < numValidators; i++ {
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
gentxsDir := filepath.Join(outputDir, "gentxs")
moniker := monikers[i]
config.Moniker = nodeDirName
config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
initCfg := genutil.NewInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey)
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
if err != nil {
return err
}
nodeAppState, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genAccIterator)
if err != nil {
return err
}
if appState == nil {
// set the canonical application state (they should not differ)
appState = nodeAppState
}
genFile := config.GenesisFile()
// overwrite each validator's genesis file to have a canonical genesis time
err = genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime)
if err != nil {
return err
}
}
return nil
}
func getIP(i int, startingIPAddr string) (ip string, err error) {
if len(startingIPAddr) == 0 {
ip, err = server.ExternalIP()
if err != nil {
return "", err
}
return ip, nil
}
return calculateIP(startingIPAddr, i)
}
func calculateIP(ip string, i int) (string, error) {
ipv4 := net.ParseIP(ip).To4()
if ipv4 == nil {
return "", fmt.Errorf("%v: non ipv4 address", ip)
}
for j := 0; j < i; j++ {
ipv4[3]++
}
return ipv4.String(), nil
}
func writeFile(name string, dir string, contents []byte) error {
writePath := filepath.Join(dir)
file := filepath.Join(writePath, name)
err := cmn.EnsureDir(writePath, 0700)
if err != nil {
return err
}
err = cmn.WriteFile(file, contents, 0600)
if err != nil {
return err
}
return nil
}

View File

@ -1,21 +0,0 @@
version: '3'
services:
kvd:
image: kava/kava
command: ["kvd", "start"]
ports:
- 17127:17127
volumes:
- ~/.kvd:/root/.kvd
- ~/.kvcli:/root/.kvcli
# # Avoid printing out all the tendermint logs
# logging:
# driver: "none"
# lcd:
# image: kava/kava
# command: "kvcli rest-server --chain-id test-kava --node kvd:46657 --laddr tcp://0.0.0.0:1317"
# ports:
# - 1317:1317
# volumes:
# - ~/.kvd:/root/.kvd
# - ~/.kvcli:/root/.kvcli

View File

@ -1,39 +0,0 @@
# Payment Channels
Payment channels are designed to enable high speed and throughput for transactions while requiring no counter-party risk.
This initial implementation is for unidirectional channels. Channels can be opened by a sender and closed immediately by the receiver, or by the sender subject to a dispute period. There are no top-ups or partial withdrawals (yet).
# Usage
>The following commands require communication with a full node. By default they expect one to be running locally (accessible on localhost), but a remote can be provided with the `--node` flag.
## Create a channel
kvcli paychan create --from <your account name> --to <receivers address> --amount 100KVA
## Send an off-chain payment
Send a payment for 10 KVA.
kvcli paychan pay --from <your account name> --sen-amt 90KVA --rec-amt 10KVA --chan-id <ID of channel> --filename payment.json
Send the file `payment.json` to your receiver. Then they run the following to verify.
kvcli paychan verify --filename payment.json
## Close a channel
The receiver can close immediately at any time.
kvcli paychan submit --from <receiver's account name> --payment payment.json
The sender can submit a close request, causing the channel will close automatically after a dispute period. During this period a receiver can still close immediately, overruling the sender's request.
kvcli paychan submit --from <receiver's account name> --payment payment.json
>Note: The dispute period on the testnet is 30 seconds for ease of testing.
## Get info on a channel
kvcli get --chan-id <ID of channel>
This will print out a channel, if it exists, and any submitted close requests.

View File

@ -1,110 +0,0 @@
# Installation and Setup
#### Who this guide is for
The code currently consists of a full node daemon (`kvd`) and it's command line interface (`kvcli`).
Full nodes are fairly resource intensive and are designed to be run continuously on servers, primarily by people validating the network. While it is possible to run locally it is not recommended except for development purposes. In the future light clients will enable secure transactions for clients.
A **full node** syncs with the blockchain and processes transactions. A **validator** is a full node that has declared itself to be a "validator" on chain. This obligates it to participate in consensus by proposing and signing blocks and maintaining uptime. By not following the protocol, the validator's stake will be slashed.
Use these instructions to set up a full node, and to optionally declare yourself as a validator.
## Install
Requirements: go installed and set up (version 1.10+).
>If installing from a new Ubuntu server (16.04 or 18.04), here's how to setup go.
>
> sudo apt update
> sudo apt upgrade -y
> sudo apt install git gcc make wget -y
> wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
> sudo tar -xvf go1.10.3.linux-amd64.tar.gz
> sudo mv go /usr/local
>
> cat >> ~/.profile <<EOF
> export GOROOT=/usr/local/go
> export GOPATH=\$HOME/go
> export PATH=\$GOPATH/bin:\$GOROOT/bin:\$PATH
> EOF
>
> source ~/.profile
1. Get the code.
mkdir -p $GOPATH/src/github.com/kava-labs
cd $GOPATH/src/github.com/kava-labs
git clone https://github.com/kava-labs/kava
cd kava
2. Install the dependencies.
mkdir $GOPATH/bin
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure -vendor-only
3. Install the code.
go install ./cmd/kvd
go install ./cmd/kvcli
## Run a Full Node
kvd init --name <your-name>
Enter a new password for your validator key. This will generate generic config and private keys in `$HOME/.kvd` and `$HOME/.kvcli`.
> Note: Make sure `GOBIN` is set and added to your path if you want to be able to run installed go programs from any folder.
Setup the correct config for the current testnet
kvd init-testnet --name <your-name> --chain-id kava-test-3
Start your full node
kvd start
> Note: It might take a while to fully sync. Check the latest block height [here](http://kava-test-3.node.connector.kava.io:17127/abci_info).
### Running in the Background
It's often more convenient to start `kvd` in the background and capture output to a log file.
kvd start &> kvd.log &
To see the output of the log.
tail -f kvd.log
## Become a Validator
Join the [validator chat](https://riot.im/app/#/room/#kava-validators:matrix.org).
Follow the setup for a full node above.
Get your address with `kvcli keys list` and your validator pubkey with `kvd tendermint show_validator`.
Get some testnet coins from [the faucet](http://kava-test-3.faucet.connector.kava.io).
Then, with your full node running in the background or separate window, run:
kvcli stake create-validator \
--amount 900KVA \
--pubkey <you validator pubkey from above> \
--moniker "<your name>" \
--from <your name> \
--gas 1000000
> Note You'll need to type in your password you set earlier.
Now your full node should be participating in consensus and validating blocks!
Running a validator requires that you keep validating blocks. If you stop, your stake will be slashed.
In order to stop validating, first remove yourself as validator, then you can stop your node.
kvcli stake unbond begin \
--address-validator <your address> \
--shares-percent 1 \
--from <your name> \
--gas 1000000

View File

@ -1,27 +0,0 @@
# Validator Upgrade
These are some guidelines to upgrade to a new testnet if you where validating on a previous one.
1. Stop the current validator.
1. Remove the old config.
rm -r $HOME/.kvd
rm -r $HOME/.kvcli
1. Get the latest code.
cd $GOPATH/src/github.com/kava-labs/kava
git pull
1. Get the latest dependencies.
dep ensure -vendor-only
1. Install.
go install ./cmd/kvd
go install ./cmd/kvcli
1. Follow the installation instructions for running a full node and becoming a validator.

View File

@ -1,19 +0,0 @@
# Basic Usage
>The following commands require communication with a full node. By default they expect one to be running locally (accessible on localhost), but a remote can be provided with the `--node` flag.
## View Your Address
List locally stored account addresses and their names. The name is used in other commands to specify which address to use to sign the transaction.
kvcli keys list
## View Account Balances
kvcli account <address>
## Send Some KVA
kvcli send --from <your key name> \
--to <address> \
--amount 100KVA

View File

@ -1,53 +0,0 @@
# Validator Introduction
Kava is building a decentralized and secure fast finality currency designed to accelerate upcoming interoperability solutions.
Were focused on providing a fast and resilient base currency that counters the problems existing currencies face when combined with interoperability solutions. This will enable small light clients to connect securely and quickly create and tear down payment channels on demand. Designed to keep transaction volume within acceptable bounds and not limit the global throughput of the system.
A strong set of validators is essential to this vision. The Kava blockchain is built on tendermint using cosmos proof of stake. Unlike other consensus models this places higher security and uptime demands on the validators. Validators have the opportunity to collect fees and inflation payouts, but also must ensure the network stays live and functioning well. They are ultimately responsible for how well the network runs.
## What is a validator?
Validators collect transactions submitted to the network, process them into blocks, and sign off on those blocks to add them to the global blockchain. For this work they are rewarded in tokens.
To keep blocks fast, there are a limited number of validators. The validators are picked by the protocol according to how much tokens they lock up. These can come from their own supplies or from others through a process known as delegation - where others lock up their own tokens with a validator. Validators receive rewards based on the total amount of stake they have, and are free to choose to distribute a portion of these rewards back to delegators.
To keep validators' behavior in line with the protocol, their locked tokens can be deducted if certain behaviors are detected. Tokens are deducted evenly from both their personal tokens and delegates' tokens.
Validators want to be delegated to (as it increases their rewards), so should persuade delegators that they offer a good deal for doing so. This comes down to both distributing rewards back and by maintaining a reliable validator setup that will not result in delegator's stake being slashed.
## A Validation Setup
The correct operation of the network requires overcoming several challenges. These act as the baseline in deciding how validators should ideally be setup and operated. The top challenges are listed here with the recommended methods of mitigation.
#### Chain Halting
Tendermint will halt if enough validators go offline (by malicious or accidental means). Therefore a validator's stake will be slashed if they do not sign blocks.
Validators should maintain a high availability compute setup (ie with redundant failover, located on high availability infrastructure). They should also be resilient to DoS attacks. The recommended pattern is to use a [sentry node architecture](https://forum.cosmos.network/t/sentry-node-architecture-overview/454), where the real validator (and its IP address) is shielded from the open internet.
#### Double Signing
A validator should never sign more than one block for a given height. This is indicative of byzantine behavior and will be slashed harshly.
Using a key management system is suggested to make sure that failover nodes don't result in accidental signatures. A KMS is being developed for tendermint [here](https://github.com/tendermint/kms).
#### Private Key Storage
A validator's private key is used to sign blocks and must not be compromised. Signatures from two thirds of validators defines truth in the blockchain. The network must be secure against hostile take over of validators.
The industry recommendation for secure private key storage is to use a hardware security module; a dedicated hardware device for both storing keys and signing data. Dedicated server hardware is also recommended for running a validator, along with controlled physical access. A secure collocation facility is recommended.
#### Further Reading
For a security analysis, see the work kindly provided to the community by [Bubowerks](https://bubowerks.io):
- [Risk Assessment](https://bubowerks.io/blog/2018/08/03/risk-assessment-of-cosmos-tendermint-validators/)
- [Risk Treatment](https://bubowerks.io/blog/2018/08/27/risk-treatments-for-cosmos-hub-tendermint-validator-risks/)
Validators can also checkout the [cosmos documentation](https://cosmos.network/docs/validators/overview.html#introduction) and [cosmos forum](https://forum.cosmos.network) for additional details.
## Current Kava Testnet
The previous section covers what to expect as a long term validator. Right now Kava is in early testnet with the goal of establishing community coordination and communication channels. We're looking for validators to join using whatever compute they like (AWS is fine).
Our vision requires a wide and reliable set of validators to process blocks and keep the network running. To achieve this we are incentivizing the setup of this network to ensure it reaches the decentralization and security requirements a viable currency needs. An incentive program will be released in the near future.
We'll be running tests and ramping up validator security over the course of our testnets, with mainnet launching after a satisfactory level of validator resilience has been achieved.

35
go.mod Normal file
View File

@ -0,0 +1,35 @@
module github.com/kava-labs/_
go 1.12
require (
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c // indirect
github.com/cosmos/cosmos-sdk v0.28.2-0.20190606154315-3180e68c7b57
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect
github.com/google/gofuzz v1.0.0 // indirect
github.com/gorilla/mux v1.7.2 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/otiai10/copy v1.0.1
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776 // indirect
github.com/pelletier/go-toml v1.4.0 // indirect
github.com/prometheus/common v0.4.1 // indirect
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389 // indirect
github.com/rakyll/statik v0.1.6 // indirect
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.4
github.com/spf13/viper v1.4.0
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tendermint/go-amino v0.15.0
github.com/tendermint/tendermint v0.31.5
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect
golang.org/x/sys v0.0.0-20190527104216-9cd6430ef91e // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/appengine v1.4.0 // indirect
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 // indirect
)
replace golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5

271
go.sum Normal file
View File

@ -0,0 +1,271 @@
bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8=
github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c h1:aEbSeNALREWXk0G7UdNhR3ayBV7tZ4M2PNmnrCAph6Q=
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosmos/cosmos-sdk v0.28.2-0.20190606154315-3180e68c7b57 h1:bLViLq/BPtEMhxsYhE5NxxTM664Bt1Z+vM6SJflQXTU=
github.com/cosmos/cosmos-sdk v0.28.2-0.20190606154315-3180e68c7b57/go.mod h1:MvaJDmjgAK7X1rTnpk8+c6tUFfIZ++iuNCp2sUWzprM=
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/otiai10/copy v1.0.1 h1:gtBjD8aq4nychvRZ2CyJvFWAw0aja+VHazDdruZKGZA=
github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389 h1:F/k2nob1S9M6v5Xkq7KjSTQirOYaYQord0jR4TwyVmY=
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE=
github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U=
github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 h1:u8i49c+BxloX3XQ55cvzFNXplizZP/q00i+IlttUjAU=
github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
github.com/tendermint/go-amino v0.15.0 h1:TC4e66P59W7ML9+bxio17CPKnxW3nKIRAYskntMAoRk=
github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/tendermint/iavl v0.12.2 h1:Ls5p5VINCM1HRT9g5Vvs2zmDOCU/CCIvIHzd/pZ8P0E=
github.com/tendermint/iavl v0.12.2/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM=
github.com/tendermint/tendermint v0.31.5 h1:vTet8tCq3B9/J9Yo11dNZ8pOB7NtSy++bVSfkP4KzR4=
github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190527104216-9cd6430ef91e h1:Pzdi8HRppinixnWWzN6KSa0QkBM+GKsTJaWwwfJskNw=
golang.org/x/sys v0.0.0-20190527104216-9cd6430ef91e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 h1:4rNOqY4ULrKzS6twXa619uQgI7h9PaVd4ZhjFQ7C5zs=
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -1,214 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package app
import (
"encoding/json"
"io"
"os"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
bam "github.com/cosmos/cosmos-sdk/baseapp"
sdk "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/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/kava-labs/kava/internal/x/paychan"
)
const (
appName = "KavaApp"
)
// Set default directories for data
var (
DefaultCLIHome = os.ExpandEnv("$HOME/.kvcli")
DefaultNodeHome = os.ExpandEnv("$HOME/.kvd")
)
type KavaApp struct {
*bam.BaseApp
cdc *wire.Codec
// keys to access the substores
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
keySlashing *sdk.KVStoreKey
keyFeeCollection *sdk.KVStoreKey
keyParams *sdk.KVStoreKey
tkeyParams *sdk.TransientStoreKey
keyPaychan *sdk.KVStoreKey
// keepers
accountMapper auth.AccountMapper
feeCollectionKeeper auth.FeeCollectionKeeper
coinKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
paramsKeeper params.Keeper
paychanKeeper paychan.Keeper
}
// Creates a new KavaApp.
func NewKavaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptions ...func(*bam.BaseApp)) *KavaApp {
// Create a codec for use across the whole app
cdc := CreateKavaAppCodec()
// Create a new base app
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
// Create the kava app, extending baseApp
var app = &KavaApp{
BaseApp: bApp,
cdc: cdc,
keyMain: sdk.NewKVStoreKey("main"),
keyAccount: sdk.NewKVStoreKey("acc"),
keyStake: sdk.NewKVStoreKey("stake"),
keySlashing: sdk.NewKVStoreKey("slashing"),
keyFeeCollection: sdk.NewKVStoreKey("fee"),
keyParams: sdk.NewKVStoreKey("params"),
tkeyParams: sdk.NewTransientStoreKey("transient_params"),
keyPaychan: sdk.NewKVStoreKey("paychan"),
}
// Define the accountMapper and base account
app.accountMapper = auth.NewAccountMapper(
app.cdc,
app.keyAccount,
auth.ProtoBaseAccount,
)
// Create the keepers
app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
app.paychanKeeper = paychan.NewKeeper(app.cdc, app.keyPaychan, app.coinKeeper)
// Register the message handlers
app.Router().
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("stake", stake.NewHandler(app.stakeKeeper)).
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)).
AddRoute("paychan", paychan.NewHandler(app.paychanKeeper))
// Set func to initialize the chain from appState in genesis file
app.SetInitChainer(app.initChainer)
// Set functions that run before and after txs / blocks
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
// Mount stores
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keySlashing, app.keyFeeCollection, app.keyParams, app.keyPaychan)
app.MountStore(app.tkeyParams, sdk.StoreTypeTransient)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// Creates a codec for use across the whole app.
func CreateKavaAppCodec() *wire.Codec {
cdc := wire.NewCodec()
paychan.RegisterWire(cdc)
bank.RegisterWire(cdc)
stake.RegisterWire(cdc)
slashing.RegisterWire(cdc)
auth.RegisterWire(cdc)
sdk.RegisterWire(cdc)
wire.RegisterCrypto(cdc)
return cdc
}
// The function baseapp runs on receipt of a BeginBlock ABCI message
func (app *KavaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
}
// The function baseapp runs on receipt of a EndBlock ABCI message
func (app *KavaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
tags := paychan.EndBlocker(ctx, app.paychanKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
}
}
// Initializes the app db from the appState in the genesis file. Baseapp runs this on receipt of an InitChain ABCI message
func (app *KavaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
var genesisState GenesisState
err := app.cdc.UnmarshalJSON(stateJSON, &genesisState)
if err != nil {
panic(err)
}
// load the accounts
for _, gacc := range genesisState.Accounts {
acc := gacc.ToAccount()
acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx)
app.accountMapper.SetAccount(ctx, acc)
}
// load the initial stake information
validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
if err != nil {
panic(err)
}
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData)
return abci.ResponseInitChain{
Validators: validators,
}
}
//
func (app *KavaApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts
accounts := []GenesisAccount{}
appendAccount := func(acc auth.Account) (stop bool) {
account := NewGenesisAccountI(acc)
accounts = append(accounts, account)
return false
}
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := GenesisState{
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
}
appState, err = wire.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
validators = stake.WriteValidators(ctx, app.stakeKeeper)
return appState, validators, nil
}

View File

@ -1,79 +0,0 @@
package app
/*
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "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/stake"
"github.com/kava-labs/kava/internal/types"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
)
func setGenesis(bapp *KavaApp, accs ...auth.BaseAccount) error {
genaccs := make([]*types.GenesisAccount, len(accs))
for i, acc := range accs {
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, "foobart"})
}
genesisState := types.GenesisState{
Accounts: genaccs,
StakeData: stake.DefaultGenesisState(),
}
stateBytes, err := wire.MarshalJSONIndent(bapp.cdc, genesisState)
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
return nil
}
//_______________________________________________________________________
func TestGenesis(t *testing.T) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
bapp := NewKavaApp(logger, db)
// Construct some genesis bytes to reflect basecoin/types/AppAccount
pk := crypto.GenPrivKeyEd25519().PubKey()
addr := pk.Address()
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
require.Nil(t, err)
baseAcc := auth.BaseAccount{
Address: addr,
Coins: coins,
}
acc := &types.AppAccount{baseAcc, "foobart"}
err = setGenesis(bapp, baseAcc)
require.Nil(t, err)
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
// reload app and ensure the account is still there
bapp = NewKavaApp(logger, db)
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
}
*/

View File

@ -1,246 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package app
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/cosmos/cosmos-sdk/client"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/server/config"
sdk "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/stake"
)
const defaultPassword = "password"
var (
// Tokens given to genesis validators and accounts
numStartingTokensValidators = int64(1000)
numStartingTokensAccounts = int64(99000)
)
// Initial app state to be written to (and read from) genesis file
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
}
// A simplified version of a normal account. It doesn't have pubkey or sequence.
type GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
}
// TODO remove?
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
return GenesisAccount{
Address: acc.Address,
Coins: acc.Coins,
}
}
// TODO remove?
func NewGenesisAccountI(acc auth.Account) GenesisAccount {
return GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
}
}
// Converts a GenesisAccount to auth.BaseAccount TODO rename
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
return &auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}
}
// Create the appInit struct for server init command
func KavaAppInit() server.AppInit {
fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx.String(server.FlagName, "", "validator moniker, required")
fsAppGenTx.String(server.FlagClientHome, DefaultCLIHome,
"home directory for the client, used for key generation")
fsAppGenTx.Bool(server.FlagOWK, false, "overwrite the accounts created")
return server.AppInit{
FlagsAppGenState: fsAppGenState,
FlagsAppGenTx: fsAppGenTx,
AppGenTx: KavaAppGenTx,
AppGenState: KavaAppGenStateJSON,
}
}
// Define format for GenTx json
type KavaGenTx struct {
Name string `json:"name"`
Address sdk.AccAddress `json:"address"`
PubKey string `json:"pub_key"`
}
// Generate a genesis transaction
func KavaAppGenTx(cdc *wire.Codec, pk crypto.PubKey, genTxConfig config.GenTx) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
// check a name for the new validator has been specified
if genTxConfig.Name == "" {
return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)")
}
// get a new password for the new account
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account '%s' (default %s):", genTxConfig.Name, defaultPassword)
keyPass, err := client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return appGenTx, cliPrint, validator, err
}
if keyPass == "" {
keyPass = defaultPassword
}
// generate a new address and secret key
addr, secret, err := server.GenerateSaveCoinKey(
genTxConfig.CliRoot,
genTxConfig.Name,
defaultPassword,
genTxConfig.Overwrite,
)
if err != nil {
return
}
// Create message string to print out
mm := map[string]string{"secret": secret}
var bz []byte
bz, err = cdc.MarshalJSON(mm)
if err != nil {
return
}
cliPrint = json.RawMessage(bz)
// Create genTx and validator
appGenTx, _, validator, err = KavaAppGenTxNF(cdc, pk, addr, genTxConfig.Name)
return
}
// TODO combine with KavaAppGenTx
func KavaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.AccAddress, name string) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
// Create the gentx
var bz []byte
genTx := KavaGenTx{
Name: name,
Address: addr,
PubKey: sdk.MustBech32ifyAccPub(pk),
}
bz, err = wire.MarshalJSONIndent(cdc, genTx)
if err != nil {
return
}
appGenTx = json.RawMessage(bz)
// Create the validator
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: numStartingTokensValidators,
}
return
}
// Create the core parameters for genesis initialization
// note that the pubkey input is this machines pubkey
func KavaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
if len(appGenTxs) == 0 {
err = errors.New("must provide at least 1 genesis transaction")
return
}
// start with the default staking genesis state
stakeData := stake.DefaultGenesisState()
// change denom of staking coin
stakeData.Params.BondDenom = "KVA"
// drastically shorten unbonding time for test purposes
stakeData.Params.UnbondingTime = time.Second * 60 * 5 // 5 minutes
// get genesis flag account information
genaccs := make([]GenesisAccount, len(appGenTxs))
for i, appGenTx := range appGenTxs {
var genTx KavaGenTx
err = cdc.UnmarshalJSON(appGenTx, &genTx)
if err != nil {
return
}
// create the genesis account
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
accAuth.Coins = sdk.Coins{
{"KVA", sdk.NewInt(numStartingTokensAccounts)},
}
acc := NewGenesisAccount(&accAuth)
genaccs[i] = acc
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(numStartingTokensAccounts)) // increase the supply
// add the validator
if len(genTx.Name) > 0 {
desc := stake.NewDescription(genTx.Name, "", "", "")
validator := stake.NewValidator(genTx.Address,
sdk.MustGetAccPubKeyBech32(genTx.PubKey), desc)
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(numStartingTokensValidators)) // increase the supply
// add some new shares to the validator
var issuedDelShares sdk.Rat
validator, stakeData.Pool, issuedDelShares = validator.AddTokensFromDel(stakeData.Pool, numStartingTokensValidators)
stakeData.Validators = append(stakeData.Validators, validator)
// create the self-delegation from the issuedDelShares
delegation := stake.Delegation{
DelegatorAddr: validator.Owner,
ValidatorAddr: validator.Owner,
Shares: issuedDelShares,
Height: 0,
}
stakeData.Bonds = append(stakeData.Bonds, delegation)
}
}
// create the final app state
genesisState = GenesisState{
Accounts: genaccs,
StakeData: stakeData,
}
return
}
// Run KavaAppGenState then convert to JSON
func KavaAppGenStateJSON(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
// create the final app state
genesisState, err := KavaAppGenState(cdc, appGenTxs)
if err != nil {
return nil, err
}
appState, err = wire.MarshalJSONIndent(cdc, genesisState)
return
}

View File

@ -1,57 +0,0 @@
package lcd
import (
"encoding/base64"
"encoding/json"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)
type txBody struct {
TxBase64 string `json:"txbase64"`
}
// Decode a tx from base64 into json
func DecodeTxRequestHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// get the input base64 string
var m txBody
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
// convert from base64 string to bytes
txBytes, err := base64.StdEncoding.DecodeString(m.TxBase64)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
// convert bytes to Tx struct
var tx auth.StdTx
err = cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
// convert Tx struct to json (bytes) and return
output, err := cdc.MarshalJSON(tx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -1,695 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package lcd
import (
"encoding/hex"
"fmt"
"net/http"
"regexp"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
p2p "github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
client "github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
tests "github.com/cosmos/cosmos-sdk/tests"
sdk "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/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/x/stake/client/rest"
)
func init() {
cryptoKeys.BcryptSecurityParameter = 1
}
func TestKeys(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// get seed
// TODO Do we really need this endpoint?
res, body := Request(t, port, "GET", "/keys/seed", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
reg, err := regexp.Compile(`([a-z]+ ){12}`)
require.Nil(t, err)
match := reg.MatchString(seed)
require.True(t, match, "Returned seed has wrong format", seed)
newName := "test_newname"
newPassword := "0987654321"
// add key
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed))
res, body = Request(t, port, "POST", "/keys", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var resp keys.KeyOutput
err = wire.Cdc.UnmarshalJSON([]byte(body), &resp)
require.Nil(t, err, body)
addr2Bech32 := resp.Address.String()
_, err = sdk.AccAddressFromBech32(addr2Bech32)
require.NoError(t, err, "Failed to return a correct bech32 address")
// test if created account is the correct account
expectedInfo, _ := GetKeyBase(t).CreateKey(newName, seed, newPassword)
expectedAccount := sdk.AccAddress(expectedInfo.GetPubKey().Address().Bytes())
assert.Equal(t, expectedAccount.String(), addr2Bech32)
// existing keys
res, body = Request(t, port, "GET", "/keys", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var m [2]keys.KeyOutput
err = cdc.UnmarshalJSON([]byte(body), &m)
require.Nil(t, err)
addrBech32 := addr.String()
require.Equal(t, name, m[0].Name, "Did not serve keys name correctly")
require.Equal(t, addrBech32, m[0].Address.String(), "Did not serve keys Address correctly")
require.Equal(t, newName, m[1].Name, "Did not serve keys name correctly")
require.Equal(t, addr2Bech32, m[1].Address.String(), "Did not serve keys Address correctly")
// select key
keyEndpoint := fmt.Sprintf("/keys/%s", newName)
res, body = Request(t, port, "GET", keyEndpoint, nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var m2 keys.KeyOutput
err = cdc.UnmarshalJSON([]byte(body), &m2)
require.Nil(t, err)
require.Equal(t, newName, m2.Name, "Did not serve keys name correctly")
require.Equal(t, addr2Bech32, m2.Address.String(), "Did not serve keys Address correctly")
// update key
jsonStr = []byte(fmt.Sprintf(`{
"old_password":"%s",
"new_password":"12345678901"
}`, newPassword))
res, body = Request(t, port, "PUT", keyEndpoint, jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
// here it should say unauthorized as we changed the password before
res, body = Request(t, port, "PUT", keyEndpoint, jsonStr)
require.Equal(t, http.StatusUnauthorized, res.StatusCode, body)
// delete key
jsonStr = []byte(`{"password":"12345678901"}`)
res, body = Request(t, port, "DELETE", keyEndpoint, jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
}
func TestVersion(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
// node info
res, body := Request(t, port, "GET", "/version", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
reg, err := regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
require.Nil(t, err)
match := reg.MatchString(body)
require.True(t, match, body)
// node info
res, body = Request(t, port, "GET", "/node_version", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
reg, err = regexp.Compile(`\d+\.\d+\.\d+(-dev)?`)
require.Nil(t, err)
match = reg.MatchString(body)
require.True(t, match, body)
}
func TestNodeStatus(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
// node info
res, body := Request(t, port, "GET", "/node_info", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var nodeInfo p2p.NodeInfo
err := cdc.UnmarshalJSON([]byte(body), &nodeInfo)
require.Nil(t, err, "Couldn't parse node info")
require.NotEqual(t, p2p.NodeInfo{}, nodeInfo, "res: %v", res)
// syncing
res, body = Request(t, port, "GET", "/syncing", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
// we expect that there is no other node running so the syncing state is "false"
require.Equal(t, "false", body)
}
func TestBlock(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
var resultBlock ctypes.ResultBlock
res, body := Request(t, port, "GET", "/blocks/latest", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err := cdc.UnmarshalJSON([]byte(body), &resultBlock)
require.Nil(t, err, "Couldn't parse block")
require.NotEqual(t, ctypes.ResultBlock{}, resultBlock)
// --
res, body = Request(t, port, "GET", "/blocks/1", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = wire.Cdc.UnmarshalJSON([]byte(body), &resultBlock)
require.Nil(t, err, "Couldn't parse block")
require.NotEqual(t, ctypes.ResultBlock{}, resultBlock)
// --
res, body = Request(t, port, "GET", "/blocks/1000000000", nil)
require.Equal(t, http.StatusNotFound, res.StatusCode, body)
}
func TestValidators(t *testing.T) {
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
var resultVals rpc.ResultValidatorsOutput
res, body := Request(t, port, "GET", "/validatorsets/latest", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err := cdc.UnmarshalJSON([]byte(body), &resultVals)
require.Nil(t, err, "Couldn't parse validatorset")
require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
require.Contains(t, resultVals.Validators[0].Address.String(), "kvaladdr")
require.Contains(t, resultVals.Validators[0].PubKey, "kvalpub")
// --
res, body = Request(t, port, "GET", "/validatorsets/1", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = cdc.UnmarshalJSON([]byte(body), &resultVals)
require.Nil(t, err, "Couldn't parse validatorset")
require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
// --
res, body = Request(t, port, "GET", "/validatorsets/1000000000", nil)
require.Equal(t, http.StatusNotFound, res.StatusCode, body)
}
func TestCoinSend(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
require.NoError(t, err)
someFakeAddr := sdk.AccAddress(bz)
// query empty
res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", someFakeAddr), nil)
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
acc := getAccount(t, port, addr)
initialBalance := acc.GetCoins()
// create TX
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was committed
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query sender
acc = getAccount(t, port, addr)
coins := acc.GetCoins()
mycoins := coins[0]
require.Equal(t, "KVA", mycoins.Denom)
require.Equal(t, initialBalance[0].Amount.SubRaw(1), mycoins.Amount)
// query receiver
acc = getAccount(t, port, receiveAddr)
coins = acc.GetCoins()
mycoins = coins[0]
require.Equal(t, "KVA", mycoins.Denom)
require.Equal(t, int64(1), mycoins.Amount.Int64())
}
func TestTxs(t *testing.T) {
name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// query wrong
res, body := Request(t, port, "GET", "/txs", nil)
require.Equal(t, http.StatusBadRequest, res.StatusCode, body)
// query empty
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32='%s'", "kaccaddr1z2my792ekdthxfkwyks0pwl69jucf9wjq3txwj"), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.Equal(t, "[]", body)
// create TX
receiveAddr, resultTx := doSend(t, port, seed, name, password, addr)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx is findable
res, body = Request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var indexedTxs []tx.Info
// check if tx is queryable
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=tx.hash='%s'", resultTx.Hash), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
require.NotEqual(t, "[]", body)
err := cdc.UnmarshalJSON([]byte(body), &indexedTxs)
require.NoError(t, err)
require.Equal(t, 1, len(indexedTxs))
// XXX should this move into some other testfile for txs in general?
// test if created TX hash is the correct hash
require.Equal(t, resultTx.Hash, indexedTxs[0].Hash)
// query sender
// also tests url decoding
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=sender_bech32=%%27%s%%27", addr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = cdc.UnmarshalJSON([]byte(body), &indexedTxs)
require.NoError(t, err)
require.Equal(t, 1, len(indexedTxs), "%v", indexedTxs) // there are 2 txs created with doSend
require.Equal(t, resultTx.Height, indexedTxs[0].Height)
// query recipient
res, body = Request(t, port, "GET", fmt.Sprintf("/txs?tag=recipient_bech32='%s'", receiveAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = cdc.UnmarshalJSON([]byte(body), &indexedTxs)
require.NoError(t, err)
require.Equal(t, 1, len(indexedTxs))
require.Equal(t, resultTx.Height, indexedTxs[0].Height)
}
func TestValidatorsQuery(t *testing.T) {
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
require.Equal(t, 1, len(pks))
validators := getValidators(t, port)
require.Equal(t, len(validators), 1)
// make sure all the validators were found (order unknown because sorted by owner addr)
foundVal := false
pkBech := sdk.MustBech32ifyValPub(pks[0])
if validators[0].PubKey == pkBech {
foundVal = true
}
require.True(t, foundVal, "pkBech %v, owner %v", pkBech, validators[0].Owner)
}
func TestValidatorQuery(t *testing.T) {
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{})
defer cleanup()
require.Equal(t, 1, len(pks))
validator1Owner := sdk.AccAddress(pks[0].Address())
validator := getValidator(t, port, validator1Owner)
bech32ValAddress, err := sdk.Bech32ifyValPub(pks[0])
require.NoError(t, err)
assert.Equal(t, validator.PubKey, bech32ValAddress, "The returned validator does not hold the correct data")
}
func TestBonding(t *testing.T) {
name, password, denom := "test", "1234567890", "KVA"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
validator1Owner := sdk.AccAddress(pks[0].Address())
// create bond TX
resultTx := doDelegate(t, port, seed, name, password, addr, validator1Owner)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was committed
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query sender
acc := getAccount(t, port, addr)
coins := acc.GetCoins()
require.Equal(t, int64(40), coins.AmountOf(denom).Int64())
// query validator
bond := getDelegation(t, port, addr, validator1Owner)
require.Equal(t, "60.0000000000", bond.Shares)
//////////////////////
// testing unbonding
// create unbond TX
resultTx = doBeginUnbonding(t, port, seed, name, password, addr, validator1Owner)
tests.WaitForHeight(resultTx.Height+1, port)
// query validator
bond = getDelegation(t, port, addr, validator1Owner)
require.Equal(t, "30.0000000000", bond.Shares)
// check if tx was committed
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// should the sender should have not received any coins as the unbonding has only just begun
// query sender
acc = getAccount(t, port, addr)
coins = acc.GetCoins()
require.Equal(t, int64(40), coins.AmountOf("KVA").Int64())
// query unbonding delegation
validatorAddr := sdk.AccAddress(pks[0].Address())
unbondings := getUndelegations(t, port, addr, validatorAddr)
assert.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations")
assert.Equal(t, "30", unbondings[0].Balance.Amount.String())
// query summary
summary := getDelegationSummary(t, port, addr)
assert.Len(t, summary.Delegations, 1, "Delegation summary holds all delegations")
assert.Equal(t, "30.0000000000", summary.Delegations[0].Shares)
assert.Len(t, summary.UnbondingDelegations, 1, "Delegation summary holds all unbonding-delegations")
assert.Equal(t, "30", summary.UnbondingDelegations[0].Balance.Amount.String())
// TODO add redelegation, need more complex capabilities such to mock context and
// TODO check summary for redelegation
// assert.Len(t, summary.Redelegations, 1, "Delegation summary holds all redelegations")
// query txs
txs := getBondingTxs(t, port, addr, "")
assert.Len(t, txs, 2, "All Txs found")
txs = getBondingTxs(t, port, addr, "bond")
assert.Len(t, txs, 1, "All bonding txs found")
txs = getBondingTxs(t, port, addr, "unbond")
assert.Len(t, txs, 1, "All unbonding txs found")
}
func TestUnrevoke(t *testing.T) {
_, password := "test", "1234567890"
addr, _ := CreateAddr(t, "test", password, GetKeyBase(t))
cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
defer cleanup()
// XXX: any less than this and it fails
tests.WaitForHeight(3, port)
pkString, _ := sdk.Bech32ifyValPub(pks[0])
signingInfo := getSigningInfo(t, port, pkString)
tests.WaitForHeight(4, port)
require.Equal(t, true, signingInfo.IndexOffset > 0)
require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil)
require.Equal(t, true, signingInfo.SignedBlocksCounter > 0)
}
//_____________________________________________________________________________
// get the account to get the sequence
func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", addr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var acc auth.Account
err := cdc.UnmarshalJSON([]byte(body), &acc)
require.Nil(t, err)
return acc
}
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
// create receive address
kb := client.MockKeyBase()
receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, "1234567890", cryptoKeys.SigningAlgo("secp256k1"))
require.Nil(t, err)
receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address())
acc := getAccount(t, port, addr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
// send
coinbz, err := cdc.MarshalJSON(sdk.NewInt64Coin("KVA", 1))
if err != nil {
panic(err)
}
jsonStr := []byte(fmt.Sprintf(`{
"name":"%s",
"password":"%s",
"account_number":"%d",
"sequence":"%d",
"gas": "10000",
"amount":[%s],
"chain_id":"%s"
}`, name, password, accnum, sequence, coinbz, chainID))
res, body := Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
require.Nil(t, err)
return receiveAddr, resultTx
}
func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo {
res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/signing_info/%s", validatorPubKey), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var signingInfo slashing.ValidatorSigningInfo
err := cdc.UnmarshalJSON([]byte(body), &signingInfo)
require.Nil(t, err)
return signingInfo
}
// ============= Stake Module ================
func getDelegation(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) rest.DelegationWithoutRat {
// get the account to get the sequence
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var bond rest.DelegationWithoutRat
err := cdc.UnmarshalJSON([]byte(body), &bond)
require.Nil(t, err)
return bond
}
func getUndelegations(t *testing.T, port string, delegatorAddr, validatorAddr sdk.AccAddress) []stake.UnbondingDelegation {
// get the account to get the sequence
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var unbondings []stake.UnbondingDelegation
err := cdc.UnmarshalJSON([]byte(body), &unbondings)
require.Nil(t, err)
return unbondings
}
func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary {
// get the account to get the sequence
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var summary rest.DelegationSummary
err := cdc.UnmarshalJSON([]byte(body), &summary)
require.Nil(t, err)
return summary
}
func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info {
// get the account to get the sequence
var res *http.Response
var body string
if len(query) > 0 {
res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs?type=%s", delegatorAddr, query), nil)
} else {
res, body = Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/txs", delegatorAddr), nil)
}
require.Equal(t, http.StatusOK, res.StatusCode, body)
var txs []tx.Info
err := cdc.UnmarshalJSON([]byte(body), &txs)
require.Nil(t, err)
return txs
}
func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": "%d",
"sequence": "%d",
"gas": "10000",
"chain_id": "%s",
"delegations": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"delegation": { "denom": "%s", "amount": "60" }
}
],
"begin_unbondings": [],
"complete_unbondings": [],
"begin_redelegates": [],
"complete_redelegates": []
}`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr, "KVA"))
res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
func doBeginUnbonding(t *testing.T, port, seed, name, password string,
delegatorAddr, validatorAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": "%d",
"sequence": "%d",
"gas": "20000",
"chain_id": "%s",
"delegations": [],
"begin_unbondings": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"shares": "30"
}
],
"complete_unbondings": [],
"begin_redelegates": [],
"complete_redelegates": []
}`, name, password, accnum, sequence, chainID, delegatorAddr, validatorAddr))
res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
func doBeginRedelegation(t *testing.T, port, seed, name, password string,
delegatorAddr, validatorSrcAddr, validatorDstAddr sdk.AccAddress) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"account_number": "%d",
"sequence": "%d",
"gas": "10000",
"chain_id": "%s",
"delegations": [],
"begin_unbondings": [],
"complete_unbondings": [],
"begin_redelegates": [
{
"delegator_addr": "%s",
"validator_src_addr": "%s",
"validator_dst_addr": "%s",
"shares": "30"
}
],
"complete_redelegates": []
}`, name, password, accnum, sequence, chainID, delegatorAddr, validatorSrcAddr, validatorDstAddr))
res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delegatorAddr), jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
func getValidators(t *testing.T, port string) []stake.BechValidator {
// get the account to get the sequence
res, body := Request(t, port, "GET", "/stake/validators", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var validators []stake.BechValidator
err := cdc.UnmarshalJSON([]byte(body), &validators)
require.Nil(t, err)
return validators
}
func getValidator(t *testing.T, port string, validatorAddr sdk.AccAddress) stake.BechValidator {
// get the account to get the sequence
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var validator stake.BechValidator
err := cdc.UnmarshalJSON([]byte(body), &validator)
require.Nil(t, err)
return validator
}

View File

@ -1,97 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package lcd
import (
"net/http"
"os"
client "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
keys "github.com/cosmos/cosmos-sdk/client/keys"
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
tx "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
tmserver "github.com/tendermint/tendermint/rpc/lib/server"
)
// ServeCommand will generate a long-running rest server
// (aka Light Client Daemon) that exposes functionality similar
// to the cli, but over rest
func ServeCommand(cdc *wire.Codec) *cobra.Command {
flagListenAddr := "laddr"
flagCORS := "cors"
flagMaxOpenConnections := "max-open"
cmd := &cobra.Command{
Use: "rest-server",
Short: "Start LCD (light-client daemon), a local REST server",
RunE: func(cmd *cobra.Command, args []string) error {
listenAddr := viper.GetString(flagListenAddr)
handler := createHandler(cdc)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
maxOpen := viper.GetInt(flagMaxOpenConnections)
listener, err := tmserver.StartHTTPServer(
listenAddr, handler, logger,
tmserver.Config{MaxOpenConnections: maxOpen},
)
if err != nil {
return err
}
logger.Info("REST server started")
// wait forever and cleanup
cmn.TrapSignal(func() {
err := listener.Close()
logger.Error("error closing listener", "err", err)
})
return nil
},
}
cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on")
cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)")
cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to")
cmd.Flags().String(client.FlagNode, "tcp://localhost:17127", "Address of the node to connect to")
cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
return cmd
}
func createHandler(cdc *wire.Codec) http.Handler {
r := mux.NewRouter()
kb, err := keys.GetKeyBase() //XXX
if err != nil {
panic(err)
}
cliCtx := context.NewCLIContext().WithCodec(cdc).WithLogger(os.Stdout)
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET")
r.HandleFunc("/decode_tx", DecodeTxRequestHandlerFn(cliCtx, cdc)).Methods("POST")
keys.RegisterRoutes(r)
rpc.RegisterRoutes(cliCtx, r)
tx.RegisterRoutes(cliCtx, r, cdc)
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
bank.RegisterRoutes(cliCtx, r, cdc, kb)
stake.RegisterRoutes(cliCtx, r, cdc, kb)
slashing.RegisterRoutes(cliCtx, r, cdc, kb)
return r
}

View File

@ -1,275 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package lcd
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
kapp "github.com/kava-labs/kava/internal/app"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/libs/cli"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
nm "github.com/tendermint/tendermint/node"
pvm "github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
tmtypes "github.com/tendermint/tendermint/types"
)
// makePathname creates a unique pathname for each test. It will panic if it
// cannot get the current working directory.
func makePathname() string {
p, err := os.Getwd()
if err != nil {
panic(err)
}
sep := string(filepath.Separator)
return strings.Replace(p, sep, "_", -1)
}
// GetConfig returns a Tendermint config for the test cases.
func GetConfig() *tmcfg.Config {
pathname := makePathname()
config := tmcfg.ResetTestRoot(pathname)
tmAddr, _, err := server.FreeTCPAddr()
if err != nil {
panic(err)
}
rcpAddr, _, err := server.FreeTCPAddr()
if err != nil {
panic(err)
}
grpcAddr, _, err := server.FreeTCPAddr()
if err != nil {
panic(err)
}
config.P2P.ListenAddress = tmAddr
config.RPC.ListenAddress = rcpAddr
config.RPC.GRPCListenAddress = grpcAddr
return config
}
// GetKeyBase returns the LCD test keybase. It also requires that a directory
// could be made and a keybase could be fetched.
//
// NOTE: memDB cannot be used because the request is expecting to interact with
// the default location.
func GetKeyBase(t *testing.T) crkeys.Keybase {
dir, err := ioutil.TempDir("", "lcd_test")
require.NoError(t, err)
viper.Set(cli.HomeFlag, dir)
keybase, err := keys.GetKeyBase()
require.NoError(t, err)
return keybase
}
// CreateAddr adds an address to the key store and returns an address and seed.
// It also requires that the key could be created.
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) {
var (
err error
info crkeys.Info
seed string
)
info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1)
require.NoError(t, err)
return sdk.AccAddress(info.GetPubKey().Address()), seed
}
// InitializeTestLCD starts Tendermint and the LCD in process, listening on
// their respective sockets where nValidators is the total number of validators
// and initAddrs are the accounts to initialize with some KVA tokens. It
// returns a cleanup function, a set of validator public keys, and a port.
func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress) (func(), []crypto.PubKey, string) {
config := GetConfig()
config.Consensus.TimeoutCommit = 100
config.Consensus.SkipTimeoutCommit = false
config.TxIndex.IndexAllTags = true
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
logger = log.NewFilter(logger, log.AllowError())
privValidatorFile := config.PrivValidatorFile()
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
privVal.Reset()
db := dbm.NewMemDB()
app := kapp.NewKavaApp(logger, db, nil)
cdc = kapp.CreateKavaAppCodec()
genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
require.NoError(t, err)
if nValidators < 1 {
panic("InitializeTestLCD must use at least one validator")
}
for i := 1; i < nValidators; i++ {
genDoc.Validators = append(genDoc.Validators,
tmtypes.GenesisValidator{
PubKey: ed25519.GenPrivKey().PubKey(),
Power: 1,
Name: "val",
},
)
}
var validatorsPKs []crypto.PubKey
// NOTE: It's bad practice to reuse public key address for the owner
// address but doing in the test for simplicity.
var appGenTxs []json.RawMessage
for _, gdValidator := range genDoc.Validators {
pk := gdValidator.PubKey
validatorsPKs = append(validatorsPKs, pk)
appGenTx, _, _, err := kapp.KavaAppGenTxNF(cdc, pk, sdk.AccAddress(pk.Address()), "test_val1")
require.NoError(t, err)
appGenTxs = append(appGenTxs, appGenTx)
}
genesisState, err := kapp.KavaAppGenState(cdc, appGenTxs[:])
require.NoError(t, err)
// add some tokens to init accounts
for _, addr := range initAddrs {
accAuth := auth.NewBaseAccountWithAddress(addr)
accAuth.Coins = sdk.Coins{sdk.NewInt64Coin("KVA", 100)}
acc := kapp.NewGenesisAccount(&accAuth)
genesisState.Accounts = append(genesisState.Accounts, acc)
genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewRat(100))
}
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
require.NoError(t, err)
genDoc.AppState = appState
listenAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
// XXX: Need to set this so LCD knows the tendermint node address!
viper.Set(client.FlagNode, config.RPC.ListenAddress)
viper.Set(client.FlagChainID, genDoc.ChainID)
node, err := startTM(config, logger, genDoc, privVal, app)
require.NoError(t, err)
lcd, err := startLCD(logger, listenAddr, cdc)
require.NoError(t, err)
tests.WaitForLCDStart(port)
tests.WaitForHeight(1, port)
cleanup := func() {
logger.Debug("cleaning up LCD initialization")
node.Stop()
node.Wait()
lcd.Close()
}
return cleanup, validatorsPKs, port
}
// startTM creates and starts an in-process Tendermint node with memDB and
// in-process ABCI application. It returns the new node or any error that
// occurred.
//
// TODO: Clean up the WAL dir or enable it to be not persistent!
func startTM(
tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc,
privVal tmtypes.PrivValidator, app abci.Application,
) (*nm.Node, error) {
genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil }
dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil }
node, err := nm.NewNode(
tmcfg,
privVal,
proxy.NewLocalClientCreator(app),
genDocProvider,
dbProvider,
nm.DefaultMetricsProvider(tmcfg.Instrumentation),
logger.With("module", "node"),
)
if err != nil {
return nil, err
}
err = node.Start()
if err != nil {
return nil, err
}
tests.WaitForRPC(tmcfg.RPC.ListenAddress)
logger.Info("Tendermint running!")
return node, err
}
// startLCD starts the LCD.
//
// NOTE: This causes the thread to block.
func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{})
}
// Request makes a test LCD test request. It returns a response object and a
// stringified response body.
func Request(t *testing.T, port, method, path string, payload []byte) (*http.Response, string) {
var (
err error
res *http.Response
)
url := fmt.Sprintf("http://localhost:%v%v", port, path)
fmt.Println("REQUEST " + method + " " + url)
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
require.Nil(t, err)
res, err = http.DefaultClient.Do(req)
require.Nil(t, err)
output, err := ioutil.ReadAll(res.Body)
res.Body.Close()
require.Nil(t, err)
return res, string(output)
}

View File

@ -1,32 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package lcd
import (
"fmt"
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/version"
)
// cli version REST handler endpoint
func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) {
v := version.GetVersion()
w.Write([]byte(v))
}
// connected node version REST handler endpoint
func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
version, err := cliCtx.Query("/app/version")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error())))
return
}
w.Write(version)
}
}

View File

@ -1,15 +0,0 @@
// Copyright 2016 All in Bits, inc
// Modifications copyright 2018 Kava Labs
package lcd
import (
amino "github.com/tendermint/go-amino"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
var cdc = amino.NewCodec()
func init() {
ctypes.RegisterAmino(cdc)
}

View File

@ -1,81 +0,0 @@
package types
// DEPRECATED
/*
import (
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
//"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
//"github.com/cosmos/cosmos-sdk/x/stake"
)
/*
var _ auth.Account = (*AppAccount)(nil)
// Custom extensions for this application. This is just an example of
// extending auth.BaseAccount with custom fields.
//
// This is compatible with the stock auth.AccountStore, since
// auth.AccountStore uses the flexible go-amino library.
type AppAccount struct {
auth.BaseAccount
Name string `json:"name"`
}
// nolint
func (acc AppAccount) GetName() string { return acc.Name }
func (acc *AppAccount) SetName(name string) { acc.Name = name }
// Get the AccountDecoder function for the custom AppAccount
func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder {
return func(accBytes []byte) (res auth.Account, err error) {
if len(accBytes) == 0 {
return nil, sdk.ErrTxDecode("accBytes are empty")
}
acct := new(AppAccount)
err = cdc.UnmarshalBinaryBare(accBytes, &acct)
if err != nil {
panic(err)
}
return acct, err
}
}
*/
//___________________________________________________________________________________
/*
type GenTx struct {
Address sdk.Address `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
}
// State to Unmarshal
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
//StakeData stake.GenesisState `json:"stake"`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
//Name string `json:"name"`
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
func NewGenesisAccount(aa *auth.BaseAccount) GenesisAccount {
return GenesisAccount{
//Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins.Sort(),
}
}
// convert GenesisAccount to AppAccount
func (ga *GenesisAccount) ToAppAccount() (acc *auth.BaseAccount, err error) {
return &auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}, nil
}
*/

View File

@ -1,325 +0,0 @@
package cli
import (
"fmt"
"io/ioutil"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/utils"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context"
"github.com/kava-labs/kava/internal/x/paychan"
)
// list of functions that return pointers to cobra commands
// No local storage needed for cli acting as a sender
func CreateChannelCmd(cdc *wire.Codec) *cobra.Command {
flagTo := "to"
flagCoins := "amount"
cmd := &cobra.Command{
Use: "create",
Short: "Create a new payment channel",
Long: "Create a new unidirectional payment channel from a local address to a remote address, funded with some amount of coins. These coins are removed from the sender account and put into the channel.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
// Create a tx and cli "contexts": structs populated with info from common flags.
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithLogger(os.Stdout).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
// Get sender address
sender, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
// Get receiver address
toStr := viper.GetString(flagTo)
receiver, err := sdk.AccAddressFromBech32(toStr)
if err != nil {
return err
}
// Get channel funding amount
coinsString := viper.GetString(flagCoins)
coins, err := sdk.ParseCoins(coinsString)
if err != nil {
return err
}
// Create the create channel msg to send
msg := paychan.MsgCreate{
Participants: [2]sdk.AccAddress{sender, receiver},
Coins: coins,
}
err = msg.ValidateBasic()
if err != nil {
return err
}
// Build and sign the transaction, then broadcast to the blockchain
return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagTo, "", "Recipient address of the payment channel.")
cmd.Flags().String(flagCoins, "", "Amount of coins to fund the payment channel with.")
return cmd
}
func GeneratePaymentCmd(cdc *wire.Codec) *cobra.Command {
flagId := "chan-id"
flagReceiverAmount := "rec-amt" // amount the receiver should received on closing the channel
flagSenderAmount := "sen-amt"
flagPaymentFile := "filename"
cmd := &cobra.Command{
Use: "pay",
Short: "Generate a new payment.", // TODO descriptions
Long: "Generate a payment file (json) to send to the receiver as a payment.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
// Create a cli "context": struct populated with info from common flags.
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithLogger(os.Stdout).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
// Get the paychan id
id := paychan.ChannelID(viper.GetInt64(flagId)) // TODO make this default to pulling id from chain
// Get channel receiver amount
senderCoins, err := sdk.ParseCoins(viper.GetString(flagSenderAmount))
if err != nil {
return err
}
// Get channel receiver amount
receiverCoins, err := sdk.ParseCoins(viper.GetString(flagReceiverAmount))
if err != nil {
return err
}
// create close paychan msg
update := paychan.Update{
ChannelID: id,
Payout: paychan.Payout{senderCoins, receiverCoins},
// empty sigs
}
// Sign the update as the sender
keybase, err := keys.GetKeyBase()
if err != nil {
return err
}
name := cliCtx.FromAddressName
passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
if err != nil {
return err
}
bz := update.GetSignBytes()
sig, pubKey, err := keybase.Sign(name, passphrase, bz)
if err != nil {
return err
}
update.Sigs = [1]paychan.UpdateSignature{{
PubKey: pubKey,
CryptoSignature: sig,
}}
// Write out the update
jsonUpdate, err := wire.MarshalJSONIndent(cdc, update)
if err != nil {
return err
}
paymentFile := viper.GetString(flagPaymentFile)
err = ioutil.WriteFile(paymentFile, jsonUpdate, 0644)
if err != nil {
return err
}
fmt.Printf("Written payment out to %v.\n", paymentFile)
return nil
},
}
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
cmd.Flags().String(flagSenderAmount, "", "Total coins to payout to sender on channel close.")
cmd.Flags().String(flagReceiverAmount, "", "Total coins to payout to sender on channel close.")
cmd.Flags().String(flagPaymentFile, "payment.json", "File name to write the payment into.")
return cmd
}
func VerifyPaymentCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
flagPaymentFile := "payment"
cmd := &cobra.Command{
Use: "verify",
Short: "Verify a payment file.",
Long: "Verify that a received payment can be used to close a channel.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
// Create a cli "context": struct populated with info from common flags.
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithLogger(os.Stdout).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
// read in update
bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile))
if err != nil {
// TODO add nice message about how to feed in stdin
return err
}
// decode json
var update paychan.Update
cdc.UnmarshalJSON(bz, &update)
// get the channel from the node
res, err := cliCtx.QueryStore(paychan.GetChannelKey(update.ChannelID), paychanStoreName)
if len(res) == 0 || err != nil {
return errors.Errorf("channel with ID '%d' does not exist", update.ChannelID)
}
var channel paychan.Channel
cdc.MustUnmarshalBinary(res, &channel)
//verify
verificationError := paychan.VerifyUpdate(channel, update)
// print result
if verificationError == nil {
fmt.Printf("Payment is valid for channel '%d'.\n", update.ChannelID)
} else {
fmt.Printf("Payment is NOT valid for channel '%d'.\n", update.ChannelID)
fmt.Println(verificationError)
}
return nil
},
}
cmd.Flags().String(flagPaymentFile, "payment.json", "File name to read the payment from.")
return cmd
}
func SubmitPaymentCmd(cdc *wire.Codec) *cobra.Command {
flagPaymentFile := "payment"
cmd := &cobra.Command{
Use: "submit",
Short: "Submit a payment to the blockchain to close the channel.",
Long: fmt.Sprintf("Submit a payment to the blockchain to either close a channel immediately (if you are the receiver) or after a dispute period of %d blocks (if you are the sender).", paychan.ChannelDisputeTime),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
// Create a tx and cli "contexts": structs populated with info from common flags.
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithLogger(os.Stdout).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
// Get sender address
submitter, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
// read in update
bz, err := ioutil.ReadFile(viper.GetString(flagPaymentFile))
if err != nil {
return err
}
// decode json
var update paychan.Update
cdc.UnmarshalJSON(bz, &update)
// Create the create channel msg to send
msg := paychan.MsgSubmitUpdate{
Update: update,
Submitter: submitter,
}
err = msg.ValidateBasic()
if err != nil {
return err
}
// Build and sign the transaction, then broadcast to the blockchain
return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagPaymentFile, "payment.json", "File to read the payment from.")
return cmd
}
func GetChannelCmd(cdc *wire.Codec, paychanStoreName string) *cobra.Command {
flagId := "chan-id"
cmd := &cobra.Command{
Use: "get",
Short: "Get info on a channel.",
Long: "Get the details of a non closed channel plus any submitted update waiting to be executed.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
// Create a cli "context": struct populated with info from common flags.
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithLogger(os.Stdout).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
// Get channel ID
id := paychan.ChannelID(viper.GetInt64(flagId))
// Get the channel from the node
res, err := cliCtx.QueryStore(paychan.GetChannelKey(id), paychanStoreName)
if len(res) == 0 || err != nil {
return errors.Errorf("channel with ID '%d' does not exist", id)
}
var channel paychan.Channel
cdc.MustUnmarshalBinary(res, &channel)
// Convert the channel to a json object for pretty printing
jsonChannel, err := wire.MarshalJSONIndent(cdc, channel)
if err != nil {
return err
}
// print out json channel
fmt.Println(string(jsonChannel))
// Get any submitted updates from the node
res, err = cliCtx.QueryStore(paychan.GetSubmittedUpdateKey(id), paychanStoreName)
if err != nil {
return err
}
// Print out the submitted update if it exists
if len(res) != 0 {
var submittedUpdate paychan.SubmittedUpdate
cdc.MustUnmarshalBinary(res, &submittedUpdate)
// Convert the submitted update to a json object for pretty printing
jsonSU, err := wire.MarshalJSONIndent(cdc, submittedUpdate)
if err != nil {
return err
}
// print out json submitted update
fmt.Println(string(jsonSU))
}
return nil
},
}
cmd.Flags().Int(flagId, 0, "ID of the payment channel.")
return cmd
}

View File

@ -1,7 +0,0 @@
package lcd
import ()
// implement a thing to poll blockchain and handles paychan disputes
// needs plugged into LCD - add a "background processes" slot in the LCD run function?
// eventually LCD could evolve into paychan (network) daemon

View File

@ -1,24 +0,0 @@
package rest
import (
//"github.com/gorilla/mux"
//"github.com/tendermint/go-crypto/keys"
//"github.com/cosmos/cosmos-sdk/client/context"
//"github.com/cosmos/cosmos-sdk/wire"
)
/*
// RegisterRoutes registers paychan-related REST handlers to a router
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
//r.HandleFunc("/accounts/{address}/send", SendRequestHandlerFn(cdc, kb, ctx)).Methods("POST")
}
// handler functions ...
// create paychan
// close paychan
// get paychan(s)
// send paychan payment
// get balance from receiver
// get balance from local storage
// handle incoming payment
*/

View File

@ -1,32 +0,0 @@
/*
Package paychan provides unidirectional payment channels.
This module implements simple but feature complete unidirectional payment channels. Channels can be opened by a sender and closed immediately by the receiver, or by the sender subject to a dispute period. There are no top-ups or partial withdrawals (yet). Channels support multiple currencies.
>Note: This module is still a bit rough around the edges. More feature planned. More test cases needed.
TODO Explain how the payment channels are implemented.
# TODOs
- in code TODOs
- Tidy up - method descriptions, heading comments, remove uneccessary comments, README/docs
- Find a better name for Queue - clarify distinction between int slice and abstract queue concept
- write some sort of integration test
- possible bug in submitting same update repeatedly
- find nicer name for payout
- add Gas usage
- add tags (return channel id on creation)
- refactor cmds to be able to test them, then test them
- verify doesnt throw json parsing error on invalid json
- cant submit an update from an unitialised account
- pay without a --from returns confusing error
- use custom errors instead of using sdk.ErrInternal
- split off signatures from update as with txs/msgs - testing easier, code easier to use, doesn't store sigs unecessarily on chain
- consider removing pubKey from UpdateSignature - instead let channel module access accountMapper
- refactor queue into one object
- remove printout during tests caused by mock app initialisation
*/
package paychan

View File

@ -1,35 +0,0 @@
package paychan
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
func EndBlocker(ctx sdk.Context, k Keeper) sdk.Tags {
var err sdk.Error
var channelTags sdk.Tags
tags := sdk.EmptyTags()
// Iterate through submittedUpdatesQueue
// TODO optimise so it doesn't pull every channel update from DB every block
q := k.getSubmittedUpdatesQueue(ctx)
var sUpdate SubmittedUpdate
var found bool
for _, id := range q {
// close the channel if the update has reached its execution time.
// Using >= in case some are somehow missed.
sUpdate, found = k.getSubmittedUpdate(ctx, id)
if !found {
panic("can't find element in queue that should exist")
}
if ctx.BlockHeight() >= sUpdate.ExecutionTime {
k.removeFromSubmittedUpdatesQueue(ctx, sUpdate.ChannelID)
channelTags, err = k.closeChannel(ctx, sUpdate.Update)
if err != nil {
panic(err)
}
tags.AppendTags(channelTags)
}
}
return tags
}

View File

@ -1,59 +0,0 @@
package paychan
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
)
func TestEndBlocker(t *testing.T) {
// TODO test that endBlocker doesn't close channels before the execution time
// SETUP
accountSeeds := []string{"senderSeed", "receiverSeed"}
ctx, _, channelKeeper, addrs, _, _, _ := createMockApp(accountSeeds)
sender := addrs[0]
receiver := addrs[1]
coins := sdk.Coins{sdk.NewInt64Coin("KVA", 10)}
// create new channel
channelID := ChannelID(0) // should be 0 as first channel
channel := Channel{
ID: channelID,
Participants: [2]sdk.AccAddress{sender, receiver},
Coins: coins,
}
channelKeeper.setChannel(ctx, channel)
// create closing update and submittedUpdate
payout := Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}
update := Update{
ChannelID: channelID,
Payout: payout,
}
sUpdate := SubmittedUpdate{
Update: update,
ExecutionTime: 0, // current blocktime
}
// Set empty submittedUpdatesQueue TODO work out proper genesis initialisation
channelKeeper.setSubmittedUpdatesQueue(ctx, SubmittedUpdatesQueue{})
// flag channel for closure
channelKeeper.addToSubmittedUpdatesQueue(ctx, sUpdate)
// ACTION
EndBlocker(ctx, channelKeeper)
// CHECK RESULTS
// ideally just check if keeper.channelClose was called, but can't
// writing endBlocker to accept an interface of which keeper is implementation would make this possible
// check channel is gone
_, found := channelKeeper.getChannel(ctx, channelID)
assert.False(t, found)
// check queue is empty, NOTE: due to encoding, an empty queue (underneath just an int slice) will be decoded as nil slice rather than an empty slice
suq := channelKeeper.getSubmittedUpdatesQueue(ctx)
assert.Equal(t, SubmittedUpdatesQueue(nil), suq)
// check submittedUpdate is gone
_, found = channelKeeper.getSubmittedUpdate(ctx, channelID)
assert.False(t, found)
}

View File

@ -1,62 +0,0 @@
package paychan
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"reflect"
)
// NewHandler returns a handler for "paychan" type messages.
// Called when adding routes to a newly created app.
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case MsgCreate:
return handleMsgCreate(ctx, k, msg)
case MsgSubmitUpdate:
return handleMsgSubmitUpdate(ctx, k, msg)
default:
errMsg := "Unrecognized paychan Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
// Handle MsgCreate
// Leaves validation to the keeper methods.
func handleMsgCreate(ctx sdk.Context, k Keeper, msg MsgCreate) sdk.Result {
tags, err := k.CreateChannel(ctx, msg.Participants[0], msg.Participants[len(msg.Participants)-1], msg.Coins)
if err != nil {
return err.Result()
}
// TODO any other information that should be returned in Result?
return sdk.Result{
Tags: tags,
}
}
// Handle MsgSubmitUpdate
// Leaves validation to the keeper methods.
func handleMsgSubmitUpdate(ctx sdk.Context, k Keeper, msg MsgSubmitUpdate) sdk.Result {
var err sdk.Error
tags := sdk.EmptyTags()
// TODO refactor signer detection - move to keeper or find nicer setup
channel, _ := k.getChannel(ctx, msg.Update.ChannelID)
participants := channel.Participants
// if only sender signed
if reflect.DeepEqual(msg.Submitter, participants[0]) {
tags, err = k.InitCloseChannelBySender(ctx, msg.Update)
// else if receiver signed
} else if reflect.DeepEqual(msg.Submitter, participants[len(participants)-1]) {
tags, err = k.CloseChannelByReceiver(ctx, msg.Update)
}
if err != nil {
return err.Result()
}
// These tags can be used by clients to subscribe to channel close attempts
return sdk.Result{
Tags: tags,
}
}

View File

@ -1,375 +0,0 @@
package paychan
import (
"bytes"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
)
// Keeper of the paychan store
// Handles validation internally. Does not rely on calling code to do validation.
// Aim to keep public methods safe, private ones not necessaily.
// Keepers contain main business logic of the module.
type Keeper struct {
storeKey sdk.StoreKey
cdc *wire.Codec // needed to serialize objects before putting them in the store
coinKeeper bank.Keeper
//codespace sdk.CodespaceType TODO custom errors
}
// Called when creating new app.
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
coinKeeper: ck,
//codespace: codespace,
}
return keeper
}
// Create a new payment channel and lock up sender funds.
func (k Keeper) CreateChannel(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.AccAddress, coins sdk.Coins) (sdk.Tags, sdk.Error) {
// Check addresses valid (Technicaly don't need to check sender address is valid as SubtractCoins checks)
if len(sender) == 0 {
return nil, sdk.ErrInvalidAddress(sender.String())
}
if len(receiver) == 0 {
return nil, sdk.ErrInvalidAddress(receiver.String())
}
// check coins are sorted and positive (disallow channels with zero balance)
if !coins.IsValid() {
return nil, sdk.ErrInvalidCoins(coins.String())
}
if !coins.IsPositive() {
return nil, sdk.ErrInvalidCoins(coins.String())
}
// subtract coins from sender
_, tags, err := k.coinKeeper.SubtractCoins(ctx, sender, coins)
if err != nil {
return nil, err
}
// Calculate next id
id := k.getNewChannelID(ctx)
// create new Paychan struct
channel := Channel{
ID: id,
Participants: [2]sdk.AccAddress{sender, receiver},
Coins: coins,
}
// save to db
k.setChannel(ctx, channel)
// TODO add to tags
return tags, err
}
// Initiate the close of a payment channel, subject to dispute period.
func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
// This is roughly the default path for non unidirectional channels
// get the channel
channel, found := k.getChannel(ctx, update.ChannelID)
if !found {
return nil, sdk.ErrInternal("Channel doesn't exist")
}
err := VerifyUpdate(channel, update)
if err != nil {
return nil, err
}
q := k.getSubmittedUpdatesQueue(ctx)
if q.Contains(update.ChannelID) {
// Someone has previously tried to update channel
// In bidirectional channels the new update is compared against existing and replaces it if it has a higher sequence number.
// existingSUpdate, found := k.getSubmittedUpdate(ctx, update.ChannelID)
// if !found {
// panic("can't find element in queue that should exist")
// }
// k.addToSubmittedUpdatesQueue(ctx, k.applyNewUpdate(existingSUpdate, update))
// However in unidirectional case, only the sender can close a channel this way. No clear need for them to be able to submit an update replacing a previous one they sent, so don't allow it.
// TODO tags
// TODO custom errors
sdk.ErrInternal("Sender can't submit an update for channel if one has already been submitted.")
} else {
// No one has tried to update channel
submittedUpdate := SubmittedUpdate{
Update: update,
ExecutionTime: ctx.BlockHeight() + ChannelDisputeTime, //TODO check what exactly BlockHeight refers to
}
k.addToSubmittedUpdatesQueue(ctx, submittedUpdate)
}
tags := sdk.EmptyTags() // TODO tags
return tags, nil
}
// Immediately close a channel.
func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
// get the channel
channel, found := k.getChannel(ctx, update.ChannelID)
if !found {
return nil, sdk.ErrInternal("Channel doesn't exist")
}
err := VerifyUpdate(channel, update)
if err != nil {
return nil, err
}
// Check if there is an update in the queue already
q := k.getSubmittedUpdatesQueue(ctx)
if q.Contains(update.ChannelID) {
// Someone has previously tried to update channel but receiver has final say
k.removeFromSubmittedUpdatesQueue(ctx, update.ChannelID)
}
tags, err := k.closeChannel(ctx, update)
return tags, err
}
// Main function that compare updates against each other.
// Pure function, Not needed in unidirectional case.
// func (k Keeper) applyNewUpdate(existingSUpdate SubmittedUpdate, proposedUpdate Update) SubmittedUpdate {
// var returnUpdate SubmittedUpdate
// if existingSUpdate.Sequence > proposedUpdate.Sequence {
// // update accepted
// returnUpdate = SubmittedUpdate{
// Update: proposedUpdate,
// ExecutionTime: existingSUpdate.ExecutionTime, // FIXME any new update proposal should be subject to full dispute period from submission
// }
// } else {
// // update rejected
// returnUpdate = existingSUpdate
// }
// return returnUpdate
// }
func VerifyUpdate(channel Channel, update Update) sdk.Error {
// Check the num of payout participants match channel participants
if len(update.Payout) != len(channel.Participants) {
return sdk.ErrInternal("Payout doesn't match number of channel participants")
}
// Check each coins are valid
for _, coins := range update.Payout {
if !coins.IsValid() {
return sdk.ErrInternal("Payout coins aren't formatted correctly")
}
}
// Check payout coins are each not negative (can be zero though)
if !update.Payout.IsNotNegative() {
return sdk.ErrInternal("Payout cannot be negative")
}
// Check payout sums to match channel.Coins
if !channel.Coins.IsEqual(update.Payout.Sum()) {
return sdk.ErrInternal("Payout amount doesn't match channel amount")
}
// Check sender signature is OK
if !verifySignatures(channel, update) {
return sdk.ErrInternal("Signature on update not valid")
}
return nil
}
// unsafe close channel - doesn't check if update matches existing channel
// TODO make safer?
func (k Keeper) closeChannel(ctx sdk.Context, update Update) (sdk.Tags, sdk.Error) {
var err sdk.Error
var tags sdk.Tags
channel, _ := k.getChannel(ctx, update.ChannelID)
// TODO check channel exists and participants matches update payout length
// Add coins to sender and receiver
// TODO check for possible errors first to avoid coins being half paid out?
for i, coins := range update.Payout {
_, tags, err = k.coinKeeper.AddCoins(ctx, channel.Participants[i], coins)
if err != nil {
panic(err)
}
}
k.deleteChannel(ctx, update.ChannelID)
return tags, nil
}
func verifySignatures(channel Channel, update Update) bool {
// In non unidirectional channels there will be more than one signature to check
signBytes := update.GetSignBytes()
address := channel.Participants[0]
pubKey := update.Sigs[0].PubKey
cryptoSig := update.Sigs[0].CryptoSignature
// Check public key submitted with update signature matches the account address
valid := bytes.Equal(pubKey.Address(), address) &&
// Check the signature is correct
pubKey.VerifyBytes(signBytes, cryptoSig)
return valid
}
// =========================================== SUBMITTED UPDATES QUEUE
func (k Keeper) addToSubmittedUpdatesQueue(ctx sdk.Context, sUpdate SubmittedUpdate) {
// always overwrite prexisting values - leave paychan logic to higher levels
// get current queue
q := k.getSubmittedUpdatesQueue(ctx)
// append ID to queue
if !q.Contains(sUpdate.ChannelID) {
q = append(q, sUpdate.ChannelID)
}
// set queue
k.setSubmittedUpdatesQueue(ctx, q)
// store submittedUpdate
k.setSubmittedUpdate(ctx, sUpdate)
}
func (k Keeper) removeFromSubmittedUpdatesQueue(ctx sdk.Context, channelID ChannelID) {
// get current queue
q := k.getSubmittedUpdatesQueue(ctx)
// remove id
q.RemoveMatchingElements(channelID)
// set queue
k.setSubmittedUpdatesQueue(ctx, q)
// delete submittedUpdate
k.deleteSubmittedUpdate(ctx, channelID)
}
func (k Keeper) getSubmittedUpdatesQueue(ctx sdk.Context) SubmittedUpdatesQueue {
// load from DB
store := ctx.KVStore(k.storeKey)
bz := store.Get(k.getSubmittedUpdatesQueueKey())
var suq SubmittedUpdatesQueue // if the submittedUpdatesQueue not found then return an empty one
if bz != nil {
// unmarshal
k.cdc.MustUnmarshalBinary(bz, &suq)
}
return suq
}
func (k Keeper) setSubmittedUpdatesQueue(ctx sdk.Context, suq SubmittedUpdatesQueue) {
store := ctx.KVStore(k.storeKey)
// marshal
bz := k.cdc.MustMarshalBinary(suq)
// write to db
key := k.getSubmittedUpdatesQueueKey()
store.Set(key, bz)
}
func (k Keeper) getSubmittedUpdatesQueueKey() []byte {
return []byte("submittedUpdatesQueue")
}
// ============= SUBMITTED UPDATES
// These are keyed by the IDs of their associated Channels
// This section deals with only setting and getting
func (k Keeper) getSubmittedUpdate(ctx sdk.Context, channelID ChannelID) (SubmittedUpdate, bool) {
// load from DB
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetSubmittedUpdateKey(channelID))
var sUpdate SubmittedUpdate
if bz == nil {
return sUpdate, false
}
// unmarshal
k.cdc.MustUnmarshalBinary(bz, &sUpdate)
// return
return sUpdate, true
}
// Store payment channel struct in blockchain store.
func (k Keeper) setSubmittedUpdate(ctx sdk.Context, sUpdate SubmittedUpdate) {
store := ctx.KVStore(k.storeKey)
// marshal
bz := k.cdc.MustMarshalBinary(sUpdate) // panics if something goes wrong
// write to db
key := GetSubmittedUpdateKey(sUpdate.ChannelID)
store.Set(key, bz) // panics if something goes wrong
}
func (k Keeper) deleteSubmittedUpdate(ctx sdk.Context, channelID ChannelID) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetSubmittedUpdateKey(channelID))
// TODO does this have return values? What happens when key doesn't exist?
}
func GetSubmittedUpdateKey(channelID ChannelID) []byte {
return []byte(fmt.Sprintf("submittedUpdate:%d", channelID))
}
// ========================================== CHANNELS
// Reteive a payment channel struct from the blockchain store.
func (k Keeper) getChannel(ctx sdk.Context, channelID ChannelID) (Channel, bool) {
// load from DB
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetChannelKey(channelID))
var channel Channel
if bz == nil {
return channel, false
}
// unmarshal
k.cdc.MustUnmarshalBinary(bz, &channel)
// return
return channel, true
}
// Store payment channel struct in blockchain store.
func (k Keeper) setChannel(ctx sdk.Context, channel Channel) {
store := ctx.KVStore(k.storeKey)
// marshal
bz := k.cdc.MustMarshalBinary(channel) // panics if something goes wrong
// write to db
key := GetChannelKey(channel.ID)
store.Set(key, bz) // panics if something goes wrong
}
func (k Keeper) deleteChannel(ctx sdk.Context, channelID ChannelID) {
store := ctx.KVStore(k.storeKey)
store.Delete(GetChannelKey(channelID))
// TODO does this have return values? What happens when key doesn't exist?
}
func (k Keeper) getNewChannelID(ctx sdk.Context) ChannelID {
// get last channel ID
var lastID ChannelID
store := ctx.KVStore(k.storeKey)
bz := store.Get(getLastChannelIDKey())
if bz == nil {
lastID = -1 // TODO is just setting to zero if uninitialized ok?
} else {
k.cdc.MustUnmarshalBinary(bz, &lastID)
}
// increment to create new one
newID := lastID + 1
bz = k.cdc.MustMarshalBinary(newID)
// set last channel id again
store.Set(getLastChannelIDKey(), bz)
// return
return newID
}
func GetChannelKey(channelID ChannelID) []byte {
return []byte(fmt.Sprintf("channel:%d", channelID))
}
func getLastChannelIDKey() []byte {
return []byte("lastChannelID")
}

View File

@ -1,315 +0,0 @@
package paychan
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"testing"
)
func TestKeeper(t *testing.T) {
t.Run("CreateChannel", func(t *testing.T) {
// TODO test for receiver account not existing (OK) and sender not existing (not ok)
accountSeeds := []string{"senderSeed", "receiverSeed"}
const (
senderAccountIndex int = 0
receiverAccountIndex int = 1
)
_, addrs, _, _ := createTestGenAccounts(accountSeeds, sdk.Coins{}) // pure function
testCases := []struct {
name string
sender sdk.AccAddress
receiver sdk.AccAddress
coins sdk.Coins
shouldCreateChannel bool
shouldError bool
}{
{
"HappyPath",
addrs[senderAccountIndex],
addrs[receiverAccountIndex],
sdk.Coins{sdk.NewInt64Coin("KVA", 10)},
true,
false,
},
{
"NilAddress",
sdk.AccAddress{},
sdk.AccAddress{},
sdk.Coins{sdk.NewInt64Coin("KVA", 10)},
false,
true,
},
{
"NilCoins",
addrs[senderAccountIndex],
addrs[receiverAccountIndex],
sdk.Coins{},
false,
true,
},
{
"NegativeCoins",
addrs[senderAccountIndex],
addrs[receiverAccountIndex],
sdk.Coins{sdk.NewInt64Coin("KVA", -57)},
false,
true,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
////// SETUP
// create basic mock app
ctx, coinKeeper, channelKeeper, addrs, _, _, genAccFunding := createMockApp(accountSeeds)
//
////// ACTION
_, err := channelKeeper.CreateChannel(ctx, testCase.sender, testCase.receiver, testCase.coins)
//
////// CHECK RESULTS
// Check error
if testCase.shouldError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
// Check if channel exists and is correct
channelID := ChannelID(0) // should be 0 as first channel
createdChan, found := channelKeeper.getChannel(ctx, channelID)
if testCase.shouldCreateChannel {
expectedChan := Channel{
ID: channelID,
Participants: [2]sdk.AccAddress{testCase.sender, testCase.receiver},
Coins: testCase.coins,
}
// channel exists and correct
assert.True(t, found)
assert.Equal(t, expectedChan, createdChan)
// check coins deducted from sender
assert.Equal(t, genAccFunding.Minus(testCase.coins), coinKeeper.GetCoins(ctx, testCase.sender))
// check no coins deducted from receiver
assert.Equal(t, genAccFunding, coinKeeper.GetCoins(ctx, testCase.receiver))
// check next global channelID incremented
assert.Equal(t, ChannelID(1), channelKeeper.getNewChannelID(ctx))
} else {
// channel doesn't exist
assert.False(t, found)
assert.Equal(t, Channel{}, createdChan)
// check no coins deducted from sender
assert.Equal(t, genAccFunding, coinKeeper.GetCoins(ctx, addrs[senderAccountIndex]))
// check no coins deducted from receiver
assert.Equal(t, genAccFunding, coinKeeper.GetCoins(ctx, addrs[receiverAccountIndex]))
// check next global channelID not incremented
assert.Equal(t, ChannelID(0), channelKeeper.getNewChannelID(ctx))
}
})
}
})
t.Run("CloseChannelByReceiver", func(t *testing.T) {
// TODO convert to table driven and add more test cases
// channel exists or not (assume channels correct)
// various Updates
// submittedUpdates existing or not (assume they are valid)
// SETUP
accountSeeds := []string{"senderSeed", "receiverSeed"}
const (
senderAccountIndex int = 0
receiverAccountIndex int = 1
)
ctx, coinKeeper, channelKeeper, addrs, pubKeys, privKeys, genAccFunding := createMockApp(accountSeeds)
coins := sdk.Coins{sdk.NewInt64Coin("KVA", 10)}
// create new channel
channelID := ChannelID(0) // should be 0 as first channel
channel := Channel{
ID: channelID,
Participants: [2]sdk.AccAddress{addrs[senderAccountIndex], addrs[receiverAccountIndex]},
Coins: coins,
}
channelKeeper.setChannel(ctx, channel)
// create closing update
payout := Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}
update := Update{
ChannelID: channelID,
Payout: payout,
// empty sig
}
cryptoSig, _ := privKeys[senderAccountIndex].Sign(update.GetSignBytes())
update.Sigs = [1]UpdateSignature{UpdateSignature{
PubKey: pubKeys[senderAccountIndex],
CryptoSignature: cryptoSig,
}}
// Set empty submittedUpdatesQueue TODO work out proper genesis initialisation
channelKeeper.setSubmittedUpdatesQueue(ctx, SubmittedUpdatesQueue{})
// ACTION
_, err := channelKeeper.CloseChannelByReceiver(ctx, update)
// CHECK RESULTS
// no error
assert.NoError(t, err)
// coins paid out
senderPayout := payout[senderAccountIndex]
assert.Equal(t, genAccFunding.Plus(senderPayout), coinKeeper.GetCoins(ctx, addrs[senderAccountIndex]))
receiverPayout := payout[receiverAccountIndex]
assert.Equal(t, genAccFunding.Plus(receiverPayout), coinKeeper.GetCoins(ctx, addrs[receiverAccountIndex]))
// channel deleted
_, found := channelKeeper.getChannel(ctx, channelID)
assert.False(t, found)
})
t.Run("InitCloseChannelBySender", func(t *testing.T) {
// TODO do some documentation here
// Ideally this should mock calls to ctx.store.Get/Set - test the side effects without being dependent on implementatino details
// TODO test correct behaviour when a submittedUpdate already exists
accountSeeds := []string{"senderSeed", "receiverSeed", "notInChannelSeed"}
const (
senderAccountIndex int = 0
receiverAccountIndex int = 1
otherAccountIndex int = 2
)
chanID := ChannelID(0)
type testUpdate struct { // A parameterised version of an Update for use in specifying test cases.
channelID ChannelID // channelID of submitted update
payout Payout // payout of submitted update
pubKeyAccountIndex int // pubkey of signature of submitted update
sigAccountIndex int // crypto signature of signature of submitted update
}
testCases := []struct {
name string
setupChannel bool
updateToSubmit testUpdate
expectedSubmittedUpdate string // "empty" or "sameAsSubmitted"
shouldError bool
}{
{
"HappyPath",
true,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, senderAccountIndex},
"sameAsSubmited",
false,
},
{
"NoChannel",
false,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, senderAccountIndex},
"empty",
true,
},
{
"NoCoins",
true,
testUpdate{chanID, Payout{sdk.Coins{}}, senderAccountIndex, senderAccountIndex},
"empty",
true,
},
{
"NegativeCoins",
true,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", -5)}, sdk.Coins{sdk.NewInt64Coin("KVA", 15)}}, senderAccountIndex, senderAccountIndex},
"empty",
true,
},
{
"TooManyCoins",
true,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 100)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, senderAccountIndex},
"empty",
true,
},
{
"WrongSignature",
true,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, senderAccountIndex, otherAccountIndex},
"empty",
true,
},
{
"WrongPubKey",
true,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, otherAccountIndex, senderAccountIndex},
"empty",
true,
},
{
"ReceiverSigned",
true,
testUpdate{chanID, Payout{sdk.Coins{sdk.NewInt64Coin("KVA", 3)}, sdk.Coins{sdk.NewInt64Coin("KVA", 7)}}, receiverAccountIndex, receiverAccountIndex},
"empty",
true,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
// SETUP
ctx, _, channelKeeper, addrs, pubKeys, privKeys, _ := createMockApp(accountSeeds)
// Set empty submittedUpdatesQueue TODO work out proper genesis initialisation
channelKeeper.setSubmittedUpdatesQueue(ctx, SubmittedUpdatesQueue{})
// create new channel
if testCase.setupChannel {
channel := Channel{
ID: chanID, // should be 0 as first channel
Participants: [2]sdk.AccAddress{addrs[senderAccountIndex], addrs[receiverAccountIndex]},
Coins: sdk.Coins{sdk.NewInt64Coin("KVA", 10)},
}
channelKeeper.setChannel(ctx, channel)
}
// create update
// basic values
updateToSubmit := Update{
ChannelID: testCase.updateToSubmit.channelID,
Payout: testCase.updateToSubmit.payout,
// empty sig
}
// create update's signature
cryptoSig, _ := privKeys[testCase.updateToSubmit.sigAccountIndex].Sign(updateToSubmit.GetSignBytes())
updateToSubmit.Sigs = [1]UpdateSignature{UpdateSignature{
PubKey: pubKeys[testCase.updateToSubmit.pubKeyAccountIndex],
CryptoSignature: cryptoSig,
}}
// ACTION
_, err := channelKeeper.InitCloseChannelBySender(ctx, updateToSubmit)
// CHECK RESULTS
// Check error
if testCase.shouldError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
// Check submittedUpdate
su, found := channelKeeper.getSubmittedUpdate(ctx, chanID)
switch testCase.expectedSubmittedUpdate {
case "empty":
assert.False(t, found)
assert.Zero(t, su)
case "sameAsSubmitted":
assert.True(t, found)
expectedSU := SubmittedUpdate{updateToSubmit, ChannelDisputeTime}
assert.Equal(t, expectedSU, su)
}
})
}
})
}

View File

@ -1,61 +0,0 @@
package paychan
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/mock"
//"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
)
// Setup an example app with an in memory DB and the required keepers
// Also create two accounts with 1000KVA
// Could do with refactoring
func createMockApp(accountSeeds []string) (sdk.Context, bank.Keeper, Keeper, []sdk.AccAddress, []crypto.PubKey, []crypto.PrivKey, sdk.Coins) {
mApp := mock.NewApp() // creates a half complete app
coinKeeper := bank.NewKeeper(mApp.AccountMapper)
// create channel keeper
keyChannel := sdk.NewKVStoreKey("channel")
channelKeeper := NewKeeper(mApp.Cdc, keyChannel, coinKeeper)
// could add router for msg tests
//mapp.Router().AddRoute("channel", NewHandler(channelKeeper))
mApp.CompleteSetup([]*sdk.KVStoreKey{keyChannel})
// create some accounts
genAccFunding := sdk.Coins{sdk.NewInt64Coin("KVA", 1000)}
genAccs, addrs, pubKeys, privKeys := createTestGenAccounts(accountSeeds, genAccFunding)
// initialize the app with these accounts
mock.SetGenesis(mApp, genAccs)
mApp.BeginBlock(abci.RequestBeginBlock{}) // going off other module tests
ctx := mApp.BaseApp.NewContext(false, abci.Header{})
return ctx, coinKeeper, channelKeeper, addrs, pubKeys, privKeys, genAccFunding
}
// CreateTestGenAccounts deterministically generates genesis accounts loaded with coins, and returns
// their addresses, pubkeys, and privkeys.
func createTestGenAccounts(accountSeeds []string, genCoins sdk.Coins) (genAccs []auth.Account, addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) {
for _, seed := range accountSeeds {
privKey := ed25519.GenPrivKeyFromSecret([]byte(seed))
pubKey := privKey.PubKey()
addr := sdk.AccAddress(pubKey.Address())
genAcc := &auth.BaseAccount{
Address: addr,
Coins: genCoins,
}
genAccs = append(genAccs, genAcc)
privKeys = append(privKeys, privKey)
pubKeys = append(pubKeys, pubKey)
addrs = append(addrs, addr)
}
return
}

View File

@ -1,220 +0,0 @@
package paychan
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/crypto"
)
/* CHANNEL TYPES */
// Used to represent a channel in the keeper module.
// Participants is limited to two as currently these are unidirectional channels.
// Last participant is designated as receiver.
type Channel struct {
ID ChannelID
Participants [2]sdk.AccAddress // [senderAddr, receiverAddr]
Coins sdk.Coins
}
const ChannelDisputeTime = int64(6) // measured in blocks TODO pick reasonable time, add to channel or genesis
type ChannelID int64 // TODO should this be positive only?
// The data that is passed between participants as payments, and submitted to the blockchain to close a channel.
type Update struct {
ChannelID ChannelID
Payout Payout
//Sequence int64 Not needed for unidirectional channels
Sigs [1]UpdateSignature // only sender needs to sign in unidirectional
}
func (u Update) GetSignBytes() []byte {
bz, err := msgCdc.MarshalJSON(struct {
ChannelID ChannelID
Payout Payout
}{
ChannelID: u.ChannelID,
Payout: u.Payout})
if err != nil {
panic(err)
}
return sdk.MustSortJSON(bz)
}
type Payout [2]sdk.Coins // a list of coins to be paid to each of Channel.Participants
func (p Payout) IsNotNegative() bool {
result := true
for _, coins := range p {
result = result && coins.IsNotNegative()
}
return result
}
func (p Payout) Sum() sdk.Coins {
var total sdk.Coins
for _, coins := range p {
total = total.Plus(coins.Sort())
total = total.Sort()
}
return total
}
type UpdateSignature struct {
PubKey crypto.PubKey
CryptoSignature []byte
}
// An update that has been submitted to the blockchain, but not yet acted on.
type SubmittedUpdate struct {
Update
ExecutionTime int64 // BlockHeight
}
type SubmittedUpdatesQueue []ChannelID // not technically a queue
// Check if value is in queue
func (suq SubmittedUpdatesQueue) Contains(channelID ChannelID) bool {
found := false
for _, id := range suq {
if id == channelID {
found = true
break
}
}
return found
}
// Remove all values from queue that match argument
func (suq *SubmittedUpdatesQueue) RemoveMatchingElements(channelID ChannelID) {
newSUQ := SubmittedUpdatesQueue{}
for _, id := range *suq {
if id != channelID {
newSUQ = append(newSUQ, id)
}
}
*suq = newSUQ
}
/* MESSAGE TYPES */
/*
Message implement the sdk.Msg interface:
type Msg interface {
// Return the message type.
// Must be alphanumeric or empty.
Type() string
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
// ValidateBasic does a simple validation check that
// doesn't require access to any other information.
ValidateBasic() Error
// Signers returns the addrs of signers that must sign.
// CONTRACT: All signatures must be present to be valid.
// CONTRACT: Returns addrs in some deterministic order.
GetSigners() []AccAddress
}
*/
// A message to create a payment channel.
type MsgCreate struct {
Participants [2]sdk.AccAddress
Coins sdk.Coins
}
func (msg MsgCreate) Type() string { return "paychan" }
func (msg MsgCreate) GetSignBytes() []byte {
bz, err := msgCdc.MarshalJSON(msg)
if err != nil {
panic(err)
}
return sdk.MustSortJSON(bz)
}
func (msg MsgCreate) ValidateBasic() sdk.Error {
// Validate msg as an optimisation to avoid all validation going to keeper. It's run before the sigs are checked by the auth module.
// Validate without external information (such as account balance)
//TODO implement
/* old logic
// check if all fields present / not 0 valued
if len(msg.Sender) == 0 {
return sdk.ErrInvalidAddress(msg.Sender.String())
}
if len(msg.Receiver) == 0 {
return sdk.ErrInvalidAddress(msg.Receiver.String())
}
if len(msg.Amount) == 0 {
return sdk.ErrInvalidCoins(msg.Amount.String())
}
// Check if coins are sorted, non zero, non negative
if !msg.Amount.IsValid() {
return sdk.ErrInvalidCoins(msg.Amount.String())
}
if !msg.Amount.IsPositive() {
return sdk.ErrInvalidCoins(msg.Amount.String())
}
// TODO check if Address valid?
*/
return nil
}
func (msg MsgCreate) GetSigners() []sdk.AccAddress {
// Only sender must sign to create a paychan
return []sdk.AccAddress{msg.Participants[0]} // select sender address
}
// A message to close a payment channel.
type MsgSubmitUpdate struct {
Update
Submitter sdk.AccAddress
}
func (msg MsgSubmitUpdate) Type() string { return "paychan" }
func (msg MsgSubmitUpdate) GetSignBytes() []byte {
bz, err := msgCdc.MarshalJSON(msg)
if err != nil {
panic(err)
}
return sdk.MustSortJSON(bz)
}
func (msg MsgSubmitUpdate) ValidateBasic() sdk.Error {
// TODO implement
/* old logic
// check if all fields present / not 0 valued
if len(msg.Sender) == 0 {
return sdk.ErrInvalidAddress(msg.Sender.String())
}
if len(msg.Receiver) == 0 {
return sdk.ErrInvalidAddress(msg.Receiver.String())
}
if len(msg.ReceiverAmount) == 0 {
return sdk.ErrInvalidCoins(msg.ReceiverAmount.String())
}
// check id ≥ 0
if msg.Id < 0 {
return sdk.ErrInvalidAddress(strconv.Itoa(int(msg.Id))) // TODO implement custom errors
}
// Check if coins are sorted, non zero, non negative
if !msg.ReceiverAmount.IsValid() {
return sdk.ErrInvalidCoins(msg.ReceiverAmount.String())
}
if !msg.ReceiverAmount.IsPositive() {
return sdk.ErrInvalidCoins(msg.ReceiverAmount.String())
}
// TODO check if Address valid?
*/
return nil
}
func (msg MsgSubmitUpdate) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Submitter}
}

View File

@ -1,45 +0,0 @@
package paychan
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSubmittedUpdatesQueue(t *testing.T) {
t.Run("RemoveMatchingElements", func(t *testing.T) {
// SETUP
q := SubmittedUpdatesQueue{4, 8, 23, 0, 5645657}
// ACTION
q.RemoveMatchingElements(23)
// CHECK RESULTS
expectedQ := SubmittedUpdatesQueue{4, 8, 0, 5645657}
assert.Equal(t, expectedQ, q)
// SETUP
q = SubmittedUpdatesQueue{0}
// ACTION
q.RemoveMatchingElements(0)
// CHECK RESULTS
expectedQ = SubmittedUpdatesQueue{}
assert.Equal(t, expectedQ, q)
})
}
func TestPayout(t *testing.T) {
t.Run("IsNotNegative", func(t *testing.T) {
p := Payout{sdk.Coins{sdk.NewInt64Coin("USD", 4), sdk.NewInt64Coin("GBP", 0)}, sdk.Coins{sdk.NewInt64Coin("USD", 129879234), sdk.NewInt64Coin("GBP", 1)}}
assert.True(t, p.IsNotNegative())
p = Payout{sdk.Coins{sdk.NewInt64Coin("USD", -4), sdk.NewInt64Coin("GBP", 0)}, sdk.Coins{sdk.NewInt64Coin("USD", 129879234), sdk.NewInt64Coin("GBP", 1)}}
assert.False(t, p.IsNotNegative())
})
t.Run("Sum", func(t *testing.T) {
p := Payout{
sdk.Coins{sdk.NewInt64Coin("EUR", 1), sdk.NewInt64Coin("USD", -5)},
sdk.Coins{sdk.NewInt64Coin("EUR", 1), sdk.NewInt64Coin("USD", 100), sdk.NewInt64Coin("GBP", 1)},
}
expected := sdk.Coins{sdk.NewInt64Coin("EUR", 2), sdk.NewInt64Coin("GBP", 1), sdk.NewInt64Coin("USD", 95)}
assert.Equal(t, expected, p.Sum())
})
}

View File

@ -1,18 +0,0 @@
package paychan
import (
"github.com/cosmos/cosmos-sdk/wire"
)
func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgCreate{}, "paychan/MsgCreate", nil)
cdc.RegisterConcrete(MsgSubmitUpdate{}, "paychan/MsgSubmitUpdate", nil)
}
// TODO move this to near the msg definitions?
var msgCdc = wire.NewCodec()
func init() {
wire.RegisterCrypto(msgCdc)
RegisterWire(msgCdc)
}

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="725px" height="213px" viewBox="0 0 725 213" enable-background="new 0 0 725 213" xml:space="preserve">
<g>
<g opacity="0.2">
<polygon fill="#FF564F" points="30,213 111.625,106.851 30,0 20.246,38.336 72.809,106.851 22.664,171.296 "/>
</g>
<g>
<polygon fill="#FF564F" points="154.344,213 72.211,106.851 154.125,0 193,0 111.027,106.851 193.625,213 "/>
</g>
<g>
<rect fill="#FF564F" width="30" height="213"/>
</g>
<g>
<path fill="#FF564F" d="M288.04,39.144c-12.1,0-23.001,1.318-32.403,3.917c-9.313,2.58-18.633,6.056-27.701,10.332l-2.473,1.167
l9.252,25.495l2.977-1.405c7.314-3.451,14.881-6.231,22.489-8.262c7.549-2.01,16.08-3.028,25.355-3.028
c14.401,0,25.424,3.462,33.168,10.29C326.336,84.381,330,94.554,330,107.887v1.038c-6-1.637-12.337-2.99-18.681-4.032
c-7.742-1.272-16.909-1.918-27.45-1.918c-10.432,0-20.067,1.193-28.741,3.548c-8.765,2.384-16.429,5.904-22.829,10.463
c-6.528,4.646-11.531,10.538-15.07,17.512c-3.538,6.964-5.23,15.158-5.23,24.357v0.626c0,8.815,1.772,16.671,5.445,23.348
c3.632,6.604,8.441,12.239,14.38,16.745c5.909,4.489,12.749,7.867,20.369,10.039c7.543,2.152,15.284,3.244,23.028,3.244
c14.647,0,26.893-2.927,36.813-8.698c7.052-4.105,12.965-8.749,17.965-13.859V213h30V107.539c0-21.171-5.885-37.887-17.68-49.682
C329.902,45.439,311.703,39.144,288.04,39.144z M280.081,187.145c-5.101,0-10.005-0.691-14.581-2.057
c-4.535-1.347-8.585-3.293-12.042-5.786c-3.356-2.419-5.818-5.483-7.772-9.111c-1.935-3.584-2.686-7.661-2.686-12.118v-0.626
c0-8.815,3.391-15.708,10.839-21.071c7.658-5.517,18.654-8.313,32.907-8.313c9.735,0,18.548,0.716,26.031,2.128
c6.664,1.263,12.223,2.613,18.223,4.023v12.649c0,5.912-1.437,11.356-3.999,16.185c-2.594,4.894-6.277,9.184-10.81,12.751
c-4.585,3.604-10.025,6.423-16.101,8.377C293.978,186.146,287.223,187.145,280.081,187.145z"/>
<polygon fill="#FF564F" points="473.281,171.271 420.85,42 387.678,42 461.364,213 484.532,213 558.564,42 526.015,42 "/>
<path fill="#FF564F" d="M707.237,57.857c-12.418-12.418-30.707-18.714-54.369-18.714c-12.1,0-22.999,1.318-32.4,3.917
c-9.313,2.58-18.631,6.056-27.7,10.332l-2.472,1.167l9.252,25.495l2.978-1.405c7.314-3.451,14.881-6.231,22.489-8.262
c7.549-2.01,16.08-3.028,25.355-3.028c14.401,0,25.507,3.462,33.251,10.29C691.253,84.381,695,94.554,695,107.887v1.038
c-6-1.637-12.42-2.99-18.764-4.032c-7.742-1.272-16.951-1.918-27.491-1.918c-10.432,0-20.088,1.193-28.762,3.548
c-8.765,2.384-16.439,5.904-22.839,10.463c-6.528,4.646-11.453,10.538-14.992,17.512c-3.538,6.964-5.152,15.158-5.152,24.357
v0.626c0,8.815,1.689,16.671,5.362,23.348c3.632,6.604,8.399,12.239,14.339,16.745c5.909,4.489,12.728,7.867,20.348,10.039
c7.543,2.152,15.273,3.244,23.017,3.244c14.647,0,26.971-2.927,36.891-8.698c7.052-4.105,13.043-8.749,18.043-13.859V213h30
V107.539C725,86.368,719.032,69.652,707.237,57.857z M644.915,187.145c-5.101,0-10.005-0.691-14.581-2.057
c-4.535-1.347-8.585-3.293-12.042-5.786c-3.356-2.419-6.235-5.483-8.189-9.111c-1.935-3.584-3.103-7.661-3.103-12.118v-0.626
c0-8.815,3.808-15.708,11.256-21.071c7.658-5.517,18.862-8.313,33.116-8.313c9.735,0,18.735,0.716,26.218,2.128
c6.664,1.263,12.41,2.613,18.41,4.023v12.649c0,5.912-1.52,11.356-4.082,16.185c-2.594,4.894-6.318,9.184-10.852,12.751
c-4.585,3.604-10.046,6.423-16.122,8.377C658.833,186.146,652.057,187.145,644.915,187.145z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,99 +0,0 @@
{
"genesis_time": "2018-08-16T20:19:19.700191483Z",
"chain_id": "kava-test-0",
"consensus_params": {
"block_size_params": {
"max_bytes": "22020096",
"max_txs": "10000",
"max_gas": "-1"
},
"tx_size_params": {
"max_bytes": "10240",
"max_gas": "-1"
},
"block_gossip_params": {
"block_part_size_bytes": "65536"
},
"evidence_params": {
"max_age": "100000"
}
},
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "dnl/bkhWxCMuq5ia4AWwU4r2HRl+MJjU4bSS0qsmI/0="
},
"power": "500000",
"name": ""
}
],
"app_hash": "",
"app_state": {
"accounts": [
{
"address": "cosmosaccaddr10fa3g29glhkekp6nhsqq4njwrpv69kl9zt4a7q",
"coins": [
{
"denom": "KVA",
"amount": "500000"
}
]
}
],
"stake": {
"pool": {
"loose_tokens": "1000000",
"bonded_tokens": "0",
"inflation_last_time": "0",
"inflation": "7/100",
"date_last_commission_reset": "0",
"prev_bonded_shares": "0"
},
"params": {
"inflation_rate_change": "13/100",
"inflation_max": "1/5",
"inflation_min": "7/100",
"goal_bonded": "67/100",
"unbonding_time": "259200",
"max_validators": 100,
"bond_denom": "KVA"
},
"validators": [
{
"owner": "cosmosaccaddr10fa3g29glhkekp6nhsqq4njwrpv69kl9zt4a7q",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "dnl/bkhWxCMuq5ia4AWwU4r2HRl+MJjU4bSS0qsmI/0="
},
"revoked": false,
"status": 0,
"tokens": "500000",
"delegator_shares": "500000",
"description": {
"moniker": "kava-validator",
"identity": "",
"website": "",
"details": ""
},
"bond_height": "0",
"bond_intra_tx_counter": 0,
"proposer_reward_pool": [],
"commission": "0",
"commission_max": "0",
"commission_change_rate": "0",
"commission_change_today": "0",
"prev_bonded_tokens": "0"
}
],
"bonds": [
{
"delegator_addr": "cosmosaccaddr10fa3g29glhkekp6nhsqq4njwrpv69kl9zt4a7q",
"validator_addr": "cosmosaccaddr10fa3g29glhkekp6nhsqq4njwrpv69kl9zt4a7q",
"shares": "500000",
"height": "0"
}
]
}
}
}

View File

@ -1,99 +0,0 @@
{
"genesis_time": "2018-08-17T20:36:46.515426797Z",
"chain_id": "kava-test-1",
"consensus_params": {
"block_size_params": {
"max_bytes": "22020096",
"max_txs": "10000",
"max_gas": "-1"
},
"tx_size_params": {
"max_bytes": "10240",
"max_gas": "-1"
},
"block_gossip_params": {
"block_part_size_bytes": "65536"
},
"evidence_params": {
"max_age": "100000"
}
},
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "2XxN3kL5CWNyI9x4o3+iG//R+BVyTwmhx0ZVNGQOAjo="
},
"power": "1000",
"name": ""
}
],
"app_hash": "",
"app_state": {
"accounts": [
{
"address": "cosmosaccaddr1fr2eescsy22uw93x9df2dernrd6llyar66e4gy",
"coins": [
{
"denom": "KVA",
"amount": "99000"
}
]
}
],
"stake": {
"pool": {
"loose_tokens": "100000",
"bonded_tokens": "0",
"inflation_last_time": "0",
"inflation": "7/100",
"date_last_commission_reset": "0",
"prev_bonded_shares": "0"
},
"params": {
"inflation_rate_change": "13/100",
"inflation_max": "1/5",
"inflation_min": "7/100",
"goal_bonded": "67/100",
"unbonding_time": "259200",
"max_validators": 100,
"bond_denom": "KVA"
},
"validators": [
{
"owner": "cosmosaccaddr1fr2eescsy22uw93x9df2dernrd6llyar66e4gy",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "2XxN3kL5CWNyI9x4o3+iG//R+BVyTwmhx0ZVNGQOAjo="
},
"revoked": false,
"status": 0,
"tokens": "1000",
"delegator_shares": "1000",
"description": {
"moniker": "kava-validator",
"identity": "",
"website": "",
"details": ""
},
"bond_height": "0",
"bond_intra_tx_counter": 0,
"proposer_reward_pool": [],
"commission": "0",
"commission_max": "0",
"commission_change_rate": "0",
"commission_change_today": "0",
"prev_bonded_tokens": "0"
}
],
"bonds": [
{
"delegator_addr": "cosmosaccaddr1fr2eescsy22uw93x9df2dernrd6llyar66e4gy",
"validator_addr": "cosmosaccaddr1fr2eescsy22uw93x9df2dernrd6llyar66e4gy",
"shares": "1000",
"height": "0"
}
]
}
}
}

View File

@ -1,99 +0,0 @@
{
"genesis_time": "2018-09-04T01:25:04.497806902Z",
"chain_id": "kava-test-2",
"consensus_params": {
"block_size_params": {
"max_bytes": "22020096",
"max_txs": "10000",
"max_gas": "-1"
},
"tx_size_params": {
"max_bytes": "10240",
"max_gas": "-1"
},
"block_gossip_params": {
"block_part_size_bytes": "65536"
},
"evidence_params": {
"max_age": "100000"
}
},
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "kcaCf2NcjO5I+jYTOnTsPMxXvO5m/c6HEUBKwYlLKFo="
},
"power": "1000",
"name": ""
}
],
"app_hash": "",
"app_state": {
"accounts": [
{
"address": "cosmosaccaddr1f6jrk3rgt9e647mjkeqqvq0yhl7wnntm5r6tyk",
"coins": [
{
"denom": "KVA",
"amount": "99000"
}
]
}
],
"stake": {
"pool": {
"loose_tokens": "100000",
"bonded_tokens": "0",
"inflation_last_time": "0",
"inflation": "7/100",
"date_last_commission_reset": "0",
"prev_bonded_shares": "0"
},
"params": {
"inflation_rate_change": "13/100",
"inflation_max": "1/5",
"inflation_min": "7/100",
"goal_bonded": "67/100",
"unbonding_time": "259200",
"max_validators": 100,
"bond_denom": "KVA"
},
"validators": [
{
"owner": "cosmosaccaddr1f6jrk3rgt9e647mjkeqqvq0yhl7wnntm5r6tyk",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "kcaCf2NcjO5I+jYTOnTsPMxXvO5m/c6HEUBKwYlLKFo="
},
"revoked": false,
"status": 0,
"tokens": "1000",
"delegator_shares": "1000",
"description": {
"moniker": "kava-validator",
"identity": "",
"website": "",
"details": ""
},
"bond_height": "0",
"bond_intra_tx_counter": 0,
"proposer_reward_pool": [],
"commission": "0",
"commission_max": "0",
"commission_change_rate": "0",
"commission_change_today": "0",
"prev_bonded_tokens": "0"
}
],
"bonds": [
{
"delegator_addr": "cosmosaccaddr1f6jrk3rgt9e647mjkeqqvq0yhl7wnntm5r6tyk",
"validator_addr": "cosmosaccaddr1f6jrk3rgt9e647mjkeqqvq0yhl7wnntm5r6tyk",
"shares": "1000",
"height": "0"
}
]
}
}
}

View File

@ -1,207 +0,0 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the Tendermint binary
proxy_app = "tcp://127.0.0.1:17128"
# A custom human readable name for this node
moniker = ""
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast_sync = true
# Database backend: leveldb | memdb
db_backend = "leveldb"
# Database directory
db_path = "data"
# Output level for logging, including package level options
log_level = "main:info,state:info,*:error"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "config/priv_validator.json"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# TCP or UNIX socket address for the profiling server to listen on
prof_laddr = "localhost:6060"
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
##### advanced configuration options #####
##### rpc server configuration options #####
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://0.0.0.0:17127"
# TCP or UNIX socket address for the gRPC server to listen on
# NOTE: This server only supports /broadcast_tx_commit
grpc_laddr = ""
# Maximum number of simultaneous connections.
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
# If you want to accept more significant number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
grpc_max_open_connections = 900
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
# Maximum number of simultaneous connections (including WebSocket).
# Does not include gRPC connections. See grpc_max_open_connections
# If you want to accept more significant number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
max_open_connections = 900
##### peer to peer configuration options #####
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:17126"
# Address to advertise to peers for them to dial
# If empty, will use the same port as the laddr,
# and will introspect on the listener or use UPnP
# to figure out the address.
external_address = ""
# Comma separated list of seed nodes to connect to
seeds = "6b1f509f5152ca08606c299c01ae284389671d8d@kava-test-3.node.connector.kava.io:17126"
# Comma separated list of nodes to keep persistent connections to
persistent_peers = ""
# UPNP port forwarding
upnp = false
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
addr_book_strict = true
# Time to wait before flushing messages out on the connection, in ms
flush_throttle_timeout = 100
# Maximum number of peers to connect to
max_num_peers = 50
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 5120000
# Rate at which packets can be received, in bytes/second
recv_rate = 5120000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
##### mempool configuration options #####
[mempool]
recheck = true
recheck_empty = true
broadcast = true
wal_dir = "data/mempool.wal"
# size of the mempool
size = 100000
# size of the cache (used to filter transactions we saw earlier)
cache_size = 100000
##### consensus configuration options #####
[consensus]
wal_file = "data/cs.wal/wal"
# All timeouts are in milliseconds
timeout_propose = 3000
timeout_propose_delta = 500
timeout_prevote = 1000
timeout_prevote_delta = 500
timeout_precommit = 1000
timeout_precommit_delta = 500
timeout_commit = 5000
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
skip_timeout_commit = false
# EmptyBlocks mode and possible interval between empty blocks in seconds
create_empty_blocks = true
create_empty_blocks_interval = 0
# Reactor sleep duration parameters are in milliseconds
peer_gossip_sleep_duration = 100
peer_query_maj23_sleep_duration = 2000
##### transactions indexer configuration options #####
[tx_index]
# What indexer to use for transactions
#
# Options:
# 1) "null" (default)
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of tags to index (by default the only tag is tx hash)
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = ""
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
index_all_tags = true
##### instrumentation configuration options #####
[instrumentation]
# When true, Prometheus metrics are served under /metrics on
# PrometheusListenAddr.
# Check out the documentation for the list of available metrics.
prometheus = false
# Address to listen for Prometheus collector(s) connections
prometheus_listen_addr = ":26660"
# Maximum number of simultaneous connections.
# If you want to accept more significant number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
max_open_connections = 3

View File

@ -1,99 +0,0 @@
{
"genesis_time": "2018-10-03T18:25:12.233785395Z",
"chain_id": "kava-test-3",
"consensus_params": {
"block_size_params": {
"max_bytes": "22020096",
"max_txs": "10000",
"max_gas": "-1"
},
"tx_size_params": {
"max_bytes": "10240",
"max_gas": "-1"
},
"block_gossip_params": {
"block_part_size_bytes": "65536"
},
"evidence_params": {
"max_age": "100000"
}
},
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "n+XBgHtHEJOQa9ufM1sj7Sacxo/W8lB3dJFmzlmmSrU="
},
"power": "1000",
"name": ""
}
],
"app_hash": "",
"app_state": {
"accounts": [
{
"address": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j",
"coins": [
{
"denom": "KVA",
"amount": "99000"
}
]
}
],
"stake": {
"pool": {
"loose_tokens": "100000",
"bonded_tokens": "0",
"inflation_last_time": "1970-01-01T00:00:00Z",
"inflation": "7/100",
"date_last_commission_reset": "0",
"prev_bonded_shares": "0"
},
"params": {
"inflation_rate_change": "13/100",
"inflation_max": "1/5",
"inflation_min": "7/100",
"goal_bonded": "67/100",
"unbonding_time": "300000000000",
"max_validators": 100,
"bond_denom": "KVA"
},
"validators": [
{
"owner": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "n+XBgHtHEJOQa9ufM1sj7Sacxo/W8lB3dJFmzlmmSrU="
},
"revoked": false,
"status": 0,
"tokens": "1000",
"delegator_shares": "1000",
"description": {
"moniker": "kava-validator",
"identity": "",
"website": "",
"details": ""
},
"bond_height": "0",
"bond_intra_tx_counter": 0,
"proposer_reward_pool": [],
"commission": "0",
"commission_max": "0",
"commission_change_rate": "0",
"commission_change_today": "0",
"prev_bonded_tokens": "0"
}
],
"bonds": [
{
"delegator_addr": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j",
"validator_addr": "kaccaddr125e8mv0zyf0jflw4jfrz85skt7ren5terqpe8j",
"shares": "1000",
"height": "0"
}
]
}
}
}