mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 23:35:19 +00:00
update
This commit is contained in:
parent
28eb2d6b31
commit
061c27bbc6
@ -1,8 +0,0 @@
|
||||
vendor/
|
||||
Dockerfile
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.kvd/
|
||||
.kvcli/
|
||||
scratch/
|
||||
testnets/
|
22
Dockerfile
22
Dockerfile
@ -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
730
Gopkg.lock
generated
@ -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
|
60
Gopkg.toml
60
Gopkg.toml
@ -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
|
||||
|
@ -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.
|
||||
|
35
README.md
35
README.md
@ -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
254
app/app.go
Normal 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
168
app/export.go
Normal 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
22
app/genesis.go
Normal 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()
|
||||
}
|
@ -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))
|
||||
}
|
||||
|
209
cmd/kvd/main.go
209
cmd/kvd/main.go
@ -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 exportAppStateAndTMValidators(
|
||||
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
|
||||
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
|
||||
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
|
||||
if height != -1 {
|
||||
appStruct := app.NewApp(logger, db, traceStore, false, uint(1))
|
||||
err := appStruct.LoadHeight(height)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}()
|
||||
|
||||
// copy file contents
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return err
|
||||
return appStruct.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||
}
|
||||
// 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
|
||||
appStruct := app.NewApp(logger, db, traceStore, true, uint(1))
|
||||
return appStruct.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||
}
|
||||
|
193
cmd/kvd/replay.go
Normal file
193
cmd/kvd/replay.go
Normal 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
378
cmd/kvd/testnet.go
Normal 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
|
||||
}
|
@ -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
|
@ -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.
|
110
docs/setup.md
110
docs/setup.md
@ -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
|
@ -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.
|
@ -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
|
||||
|
||||
|
@ -1,53 +0,0 @@
|
||||
# Validator Introduction
|
||||
|
||||
Kava is building a decentralized and secure fast finality currency designed to accelerate upcoming interoperability solutions.
|
||||
|
||||
We’re 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
35
go.mod
Normal 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
271
go.sum
Normal 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=
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
*/
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
*/
|
@ -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
|
||||
}
|
@ -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
|
@ -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
|
||||
*/
|
@ -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 doesn’t throw json parsing error on invalid json
|
||||
- can’t 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
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
@ -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
|
||||
}
|
@ -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}
|
||||
}
|
@ -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())
|
||||
})
|
||||
}
|
@ -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)
|
||||
}
|
@ -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 |
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user