diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dff5632..2496fca45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.6.0 +*Oct 30th, 2018* + +BREAKING CHANGES + +- [core] Set validators limit to 100 for testnet +- [core] SetCandidateOff transaction now applies immediately +- [tendermint] Update to [v0.26.0](https://github.com/tendermint/tendermint/blob/master/CHANGELOG.md#v0260) + +IMPROVEMENT + +- [config] Add keep_state_history option +- [api] Limit API requests + ## 0.5.1 *Oct 22th, 2018* diff --git a/Gopkg.lock b/Gopkg.lock index 4f8318170..dc7da5928 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,40 +3,53 @@ [[projects]] branch = "master" + digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "UT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] branch = "master" + digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7" name = "github.com/btcsuite/btcd" packages = ["btcec"] - revision = "f673a4b563b57b9a95832545c878669a7fa801d9" + pruneopts = "UT" + revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" [[projects]] branch = "master" + digest = "1:0c4cb117e3229040d89bb2c5aae0f438fcabf3a85a48a667576c549bce0fc9db" name = "github.com/danil-lashin/iavl" packages = ["."] - revision = "2c61cc5c1598b5e7a989e21ca000f0d7d009c193" + pruneopts = "UT" + revision = "451c063b0b834b418c84fb98e1c0b9ae6b664dc9" [[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" name = "github.com/davecgh/go-spew" packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" + pruneopts = "UT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" [[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", @@ -45,30 +58,38 @@ "metrics", "metrics/discard", "metrics/internal/lv", - "metrics/prometheus" + "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:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" name = "github.com/go-stack/stack" packages = ["."] - revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" - version = "v1.7.0" + pruneopts = "UT" + revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" + version = "v1.8.0" [[projects]] + digest = "1:234bd3c0b701cdb49dbda54821eecf2fc76431618751ee69e95248e358b26fdb" name = "github.com/gobuffalo/packr" packages = ["."] + pruneopts = "UT" revision = "bd47f2894846e32edcf9aa37290fef76c327883f" version = "v1.11.1" [[projects]] + digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -76,48 +97,60 @@ "proto", "protoc-gen-gogo/descriptor", "sortkeys", - "types" + "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" + "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]] + digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -129,146 +162,188 @@ "hcl/token", "json/parser", "json/scanner", - "json/token" + "json/token", ] + pruneopts = "UT" revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" version = "v1.0.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:ca955a9cd5b50b0f43d2cc3aeb35c951473eeca41b34eb67507f1dbcc0542394" name = "github.com/kr/pretty" packages = ["."] + pruneopts = "UT" revision = "73f6ac0b30a98e433b289500d779f50c1a6f0712" version = "v0.1.0" [[projects]] + digest = "1:15b5cc79aad436d47019f814fde81a10221c740dc8ddf769221a65097fb6c2e9" name = "github.com/kr/text" packages = ["."] + pruneopts = "UT" revision = "e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f" version = "v0.1.0" [[projects]] + digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" name = "github.com/magiconair/properties" packages = ["."] + pruneopts = "UT" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" [[projects]] + digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "UT" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] + digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" name = "github.com/mitchellh/mapstructure" packages = ["."] - revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8" - version = "v1.0.0" + pruneopts = "UT" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" [[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:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp" + "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:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model" + "model", ] - revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" + pruneopts = "UT" + revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" [[projects]] branch = "master" + digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] - revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a" + pruneopts = "UT" + revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" [[projects]] + digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" name = "github.com/rcrowley/go-metrics" packages = ["."] + pruneopts = "UT" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] + digest = "1:9695b472826ca23e521d650849fefac3102dddbf841c9a4b35fb9d3ed5d17011" name = "github.com/rs/cors" packages = ["."] + pruneopts = "UT" revision = "ca016a06a5753f8ba03029c0aa5e54afb1bf713f" version = "v1.4.0" [[projects]] + digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd" name = "github.com/spf13/afero" packages = [ ".", - "mem" + "mem", ] - revision = "787d034dfe70e44075ccc060d346146ef53270ad" - version = "v1.1.1" + pruneopts = "UT" + revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" + version = "v1.1.2" [[projects]] + digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" name = "github.com/spf13/cast" packages = ["."] + pruneopts = "UT" revision = "8965335b8c7107321228e3e3702cab9832751bac" version = "v1.2.0" [[projects]] - branch = "master" + digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" name = "github.com/spf13/jwalterweatherman" packages = ["."] - revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2" + pruneopts = "UT" + revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" + version = "v1.0.0" [[projects]] + digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" name = "github.com/spf13/pflag" packages = ["."] - revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" - version = "v1.0.2" + pruneopts = "UT" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" [[projects]] + digest = "1:214775c11fd26da94a100111a62daa25339198a4f9c57cb4aab352da889f5b93" name = "github.com/spf13/viper" packages = ["."] - revision = "907c19d40d9a6c9bb55f040ff4ae45271a4754b9" - version = "v1.1.0" + pruneopts = "UT" + revision = "2c12c60302a5a0e62ee102ca9bc996277c2f64f5" + version = "v1.2.1" [[projects]] branch = "master" + digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -282,32 +357,28 @@ "leveldb/opt", "leveldb/storage", "leveldb/table", - "leveldb/util" + "leveldb/util", ] - revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445" + pruneopts = "UT" + revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43" [[projects]] + digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f" name = "github.com/tendermint/btcd" packages = ["btcec"] + pruneopts = "UT" revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" [[projects]] - branch = "master" - name = "github.com/tendermint/ed25519" - packages = [ - ".", - "edwards25519", - "extra25519" - ] - revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" - -[[projects]] + digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431" name = "github.com/tendermint/go-amino" packages = ["."] - revision = "faa6e731944e2b7b6a46ad202902851e8ce85bee" - version = "v0.12.0" + pruneopts = "UT" + revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12" + version = "v0.14.0" [[projects]] + digest = "1:f98aef9b8b8d755b63d53f3c89aa0bd21280cf706be15e937c4e67c891074fb1" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -349,7 +420,6 @@ "rpc/core", "rpc/core/types", "rpc/grpc", - "rpc/lib", "rpc/lib/client", "rpc/lib/server", "rpc/lib/types", @@ -359,17 +429,20 @@ "state/txindex/null", "types", "types/time", - "version" + "version", ] - revision = "0c9c3292c918617624f6f3fbcd95eceade18bcd5" - version = "v0.25.0" + pruneopts = "UT" + revision = "bc0d672b26dbbd6858e97a570c87e3c20cbf199e" + version = "v0.26.0-rc0" [[projects]] - branch = "master" + digest = "1:1fdfa9436cc1c11afbdbfb56549e9201a92db0b0a0b7bef0797e7b68c9e8791f" name = "golang.org/x/crypto" packages = [ "chacha20poly1305", "curve25519", + "ed25519", + "ed25519/internal/edwards25519", "hkdf", "internal/chacha20", "internal/subtle", @@ -377,11 +450,14 @@ "nacl/secretbox", "poly1305", "ripemd160", - "salsa20/salsa" + "salsa20/salsa", ] - revision = "a2144134853fc9a27a7b1e3eb4f19f1a76df13c9" + pruneopts = "UT" + revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" + source = "github.com/tendermint/crypto" [[projects]] + digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" name = "golang.org/x/net" packages = [ "context", @@ -391,20 +467,24 @@ "idna", "internal/timeseries", "netutil", - "trace" + "trace", ] + pruneopts = "UT" revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" [[projects]] branch = "master" + digest = "1:c6a2356698f1f51289bc57ac8933745f15248283145df110fa3bb6964b3db77b" name = "golang.org/x/sys" packages = [ "cpu", - "unix" + "unix", ] - revision = "1c9583448a9c3aa0f9a6a5241bf73c0bd8aafded" + pruneopts = "UT" + revision = "d989b31c87461dc8ab2f1cac6792814e27fadea9" [[projects]] + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -420,17 +500,22 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + branch = "master" + digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] - revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200" + pruneopts = "UT" + revision = "94acd270e44e65579b9ee3cdab25034d33fed608" [[projects]] + digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", @@ -457,26 +542,55 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "UT" revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" version = "v1.13.0" [[projects]] branch = "v1" + digest = "1:af715ae33cc1f5695c4b2a4e4b21d008add8802a99e15bb467ac7c32edb5000d" name = "gopkg.in/check.v1" packages = ["."] + pruneopts = "UT" revision = "788fd78401277ebd861206a03c884797c6ec5541" [[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 - inputs-digest = "2750321cce87d5108f9f9a36678b67207f7cd57b3774b6a50571933633908af1" + input-imports = [ + "github.com/btcsuite/btcd/btcec", + "github.com/danil-lashin/iavl", + "github.com/gobuffalo/packr", + "github.com/gorilla/mux", + "github.com/rs/cors", + "github.com/spf13/viper", + "github.com/tendermint/go-amino", + "github.com/tendermint/tendermint/abci/types", + "github.com/tendermint/tendermint/config", + "github.com/tendermint/tendermint/crypto/ed25519", + "github.com/tendermint/tendermint/crypto/encoding/amino", + "github.com/tendermint/tendermint/libs/cli/flags", + "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/client", + "github.com/tendermint/tendermint/rpc/core/types", + "github.com/tendermint/tendermint/types", + "gopkg.in/check.v1", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index fd3209de1..b2bb7036f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -37,10 +37,6 @@ name = "github.com/gorilla/mux" version = "=1.6.2" -[[constraint]] - name = "github.com/gorilla/websocket" - version = "=1.2.0" - [[constraint]] name = "github.com/rs/cors" version = "=1.4.0" @@ -51,7 +47,7 @@ [[constraint]] name = "github.com/tendermint/tendermint" - version = "=v0.25.0" + version = "=v0.26.0-rc0" [[constraint]] branch = "v1" diff --git a/Makefile b/Makefile index 17fa8bf16..db9b84276 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ GOTOOLS = \ github.com/golang/dep/cmd/dep \ - gopkg.in/alecthomas/gometalinter.v2 + gopkg.in/alecthomas/gometalinter.v2 \ + github.com/gobuffalo/packr/packr PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_TAGS?=minter BUILD_FLAGS=-ldflags "-s -w -X minter/version.GitCommit=`git rev-parse --short=8 HEAD`" diff --git a/api/api.go b/api/api.go index 97e1b7e96..9b18e83ac 100644 --- a/api/api.go +++ b/api/api.go @@ -1,29 +1,33 @@ package api import ( + "encoding/json" "github.com/MinterTeam/minter-go-node/config" "github.com/MinterTeam/minter-go-node/eventsdb" "github.com/MinterTeam/minter-go-node/log" "github.com/gorilla/mux" "github.com/rs/cors" - "io" - "io/ioutil" "net/http" + "strings" + "sync" + "sync/atomic" "github.com/MinterTeam/minter-go-node/core/minter" "github.com/MinterTeam/minter-go-node/core/state" "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto/encoding/amino" - "github.com/tendermint/tendermint/node" rpc "github.com/tendermint/tendermint/rpc/client" "strconv" "time" ) var ( - cdc = amino.NewCodec() - blockchain *minter.Blockchain - client *rpc.Local + cdc = amino.NewCodec() + blockchain *minter.Blockchain + client *rpc.Local + connections = int32(0) + limitter = make(chan struct{}, 10) + cfg = config.GetConfig() ) func init() { @@ -31,19 +35,24 @@ func init() { eventsdb.RegisterAminoEvents(cdc) } -func RunApi(b *minter.Blockchain, node *node.Node) { - client = rpc.NewLocal(node) +func RunApi(b *minter.Blockchain, tmRPC *rpc.Local) { + client = tmRPC blockchain = b router := mux.NewRouter().StrictSlash(true) - router.HandleFunc("/api/bipVolume", wrapper(GetBipVolume)).Methods("GET") + stats := IpStats{ + ips: make(map[string]int), + lock: sync.Mutex{}, + } + + router.Use(RateLimit(cfg.APIPerIPLimit, cfg.APIPerIPLimitWindow, &stats)) + router.HandleFunc("/api/candidates", wrapper(GetCandidates)).Methods("GET") router.HandleFunc("/api/candidate/{pubkey}", wrapper(GetCandidate)).Methods("GET") router.HandleFunc("/api/validators", wrapper(GetValidators)).Methods("GET") router.HandleFunc("/api/balance/{address}", wrapper(GetBalance)).Methods("GET") - router.HandleFunc("/api/balanceWS", wrapper(GetBalanceWatcher)) router.HandleFunc("/api/transactionCount/{address}", wrapper(GetTransactionCount)).Methods("GET") router.HandleFunc("/api/sendTransaction", wrapper(SendTransaction)).Methods("POST") router.HandleFunc("/api/sendTransactionSync", wrapper(SendTransactionSync)).Methods("POST") @@ -74,13 +83,102 @@ func RunApi(b *minter.Blockchain, node *node.Node) { func wrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - defer io.Copy(ioutil.Discard, r.Body) - defer r.Body.Close() + if atomic.LoadInt32(&connections) > int32(cfg.APISimultaneousRequests) { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusTooManyRequests) + json.NewEncoder(w).Encode(Response{ + Code: http.StatusTooManyRequests, + Log: "Too many requests", + }) + return + } + + atomic.AddInt32(&connections, 1) + limitter <- struct{}{} + + defer func() { + log.With("module", "api").Info("Served API request", "req", r.RequestURI) + <-limitter + atomic.AddInt32(&connections, -1) + }() f(w, r) } } +type IpStats struct { + ips map[string]int + lock sync.Mutex +} + +func (s *IpStats) Reset() { + s.lock.Lock() + defer s.lock.Unlock() + + s.ips = make(map[string]int) +} + +func (s *IpStats) Add(identifier string, count int) int { + s.lock.Lock() + defer s.lock.Unlock() + + s.ips[identifier] += count + + return s.ips[identifier] +} + +type Stats interface { + // Reset will reset the map. + Reset() + + // Add would add "count" to the map at the key of "identifier", + // and returns an int which is the total count of the value + // at that key. + Add(identifier string, count int) int +} + +func RateLimit(limit int, window time.Duration, stats Stats) func(next http.Handler) http.Handler { + var windowStart time.Time + + // Clear the rate limit stats after each window. + ticker := time.NewTicker(window) + go func() { + windowStart = time.Now() + + for range ticker.C { + windowStart = time.Now() + stats.Reset() + } + }() + + return func(next http.Handler) http.Handler { + h := func(w http.ResponseWriter, r *http.Request) { + value := int(stats.Add(identifyRequest(r), 1)) + + XRateLimitRemaining := limit - value + if XRateLimitRemaining < 0 { + XRateLimitRemaining = 0 + } + + w.Header().Add("X-Rate-Limit-Limit", strconv.Itoa(limit)) + w.Header().Add("X-Rate-Limit-Remaining", strconv.Itoa(XRateLimitRemaining)) + w.Header().Add("X-Rate-Limit-Reset", strconv.Itoa(int(window.Seconds()-time.Since(windowStart).Seconds())+1)) + + if value >= limit { + w.WriteHeader(429) + } else { + next.ServeHTTP(w, r) + } + } + + return http.HandlerFunc(h) + } +} + +func identifyRequest(r *http.Request) string { + return strings.Split(r.Header.Get("X-Real-IP"), ":")[0] +} + func waitForTendermint() { for { _, err := client.Health() diff --git a/api/balance.go b/api/balance.go index a376e74a5..86b0812fb 100644 --- a/api/balance.go +++ b/api/balance.go @@ -17,7 +17,6 @@ type BalanceRequest struct { } func GetBalance(w http.ResponseWriter, r *http.Request) { - cState := GetStateForRequest(r) vars := mux.Vars(r) diff --git a/api/balance_watcher.go b/api/balance_watcher.go deleted file mode 100644 index b4f06b48d..000000000 --- a/api/balance_watcher.go +++ /dev/null @@ -1,86 +0,0 @@ -package api - -import ( - "encoding/json" - "github.com/MinterTeam/minter-go-node/core/minter" - "github.com/MinterTeam/minter-go-node/core/state" - "github.com/MinterTeam/minter-go-node/core/types" - "github.com/MinterTeam/minter-go-node/formula" - "github.com/MinterTeam/minter-go-node/log" - "github.com/gorilla/websocket" - "math/big" - "net/http" -) - -const maxClients = 10 - -var clients = make(map[*websocket.Conn]bool) -var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} - -func init() { - go handleBalanceChanges() -} - -func GetBalanceWatcher(w http.ResponseWriter, r *http.Request) { - - if len(clients) > maxClients { - w.WriteHeader(http.StatusBadGateway) - _ = json.NewEncoder(w).Encode(Response{ - Code: http.StatusBadGateway, - Result: nil, - Log: "Max balance watchers limit reached", - }) - return - } - - ws, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Error(err.Error()) - } - - clients[ws] = true -} - -func handleBalanceChanges() { - for change := range state.BalanceChangeChan { - handleBalanceChange(change) - } -} - -func handleBalanceChange(msg state.BalanceChangeStruct) { - - if len(clients) < 1 { - return - } - - defer func() { - if r := recover(); r != nil { - log.Error("Error in balance change handler") - } - }() - - balanceInBasecoin := big.NewInt(0) - - if msg.Coin == types.GetBaseCoin() { - balanceInBasecoin = msg.Balance - } else { - sCoin := minter.GetBlockchain().CurrentState().GetStateCoin(msg.Coin) - - if sCoin != nil { - balanceInBasecoin = formula.CalculateSaleReturn(sCoin.Data().Volume, sCoin.Data().ReserveBalance, sCoin.Data().Crr, msg.Balance) - } - } - - msg.BalanceInBasecoin = balanceInBasecoin - - for client := range clients { - go func() { - err := client.WriteJSON(msg) - if err != nil { - log.Info("ws error: %v", err) - _ = client.Close() - delete(clients, client) - } - }() - } -} diff --git a/api/block.go b/api/block.go index 25dc35ea5..adbb6b5bb 100644 --- a/api/block.go +++ b/api/block.go @@ -113,9 +113,7 @@ func Block(w http.ResponseWriter, r *http.Request) { precommits, _ := cdc.MarshalJSON(block.Block.LastCommit.Precommits) - encodedBlock, _ := cdc.MarshalBinary(block) - - size := len(encodedBlock) + size := len(cdc.MustMarshalBinaryLengthPrefixed(block)) var eventsRaw []byte diff --git a/api/candidate.go b/api/candidate.go index 50756635d..a9997a185 100644 --- a/api/candidate.go +++ b/api/candidate.go @@ -9,7 +9,6 @@ import ( ) func GetCandidate(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) pubkey := types.Hex2Bytes(strings.TrimLeft(vars["pubkey"], "Mp")) diff --git a/api/candidates.go b/api/candidates.go index 761ae6563..132774a9a 100644 --- a/api/candidates.go +++ b/api/candidates.go @@ -6,7 +6,6 @@ import ( ) func GetCandidates(w http.ResponseWriter, r *http.Request) { - cState := GetStateForRequest(r) candidates := cState.GetStateCandidates().GetData() diff --git a/api/get_bip_volume.go b/api/get_bip_volume.go deleted file mode 100644 index 2447b6de9..000000000 --- a/api/get_bip_volume.go +++ /dev/null @@ -1,20 +0,0 @@ -package api - -import ( - "encoding/json" - "net/http" -) - -type BipVolumeResult struct { - Volume string `json:"volume"` -} - -func GetBipVolume(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusBadRequest) - _ = json.NewEncoder(w).Encode(Response{ - Code: 501, - Log: "Not implemented", - }) - return -} diff --git a/api/transaction_count.go b/api/transaction_count.go index 7239b1fd4..dd810cd60 100644 --- a/api/transaction_count.go +++ b/api/transaction_count.go @@ -12,7 +12,6 @@ type TransactionCountResponse struct { } func GetTransactionCount(w http.ResponseWriter, r *http.Request) { - cState := GetStateForRequest(r) vars := mux.Vars(r) diff --git a/api/transactions.go b/api/transactions.go index 2a0f82f77..150164013 100644 --- a/api/transactions.go +++ b/api/transactions.go @@ -35,7 +35,6 @@ type ResultTxSearch struct { } func Transactions(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query().Get("query") rpcResult, err := client.TxSearch(query, false, 1, 100) diff --git a/api/validators.go b/api/validators.go index 8602fb6bb..c35ca1257 100644 --- a/api/validators.go +++ b/api/validators.go @@ -41,7 +41,6 @@ func makeResponseValidator(v state.Validator, state *state.StateDB) Validator { } func makeResponseCandidate(c state.Candidate) Candidate { - stakes := make([]Stake, len(c.Stakes)) for i, stake := range c.Stakes { @@ -65,7 +64,6 @@ func makeResponseCandidate(c state.Candidate) Candidate { } func GetValidators(w http.ResponseWriter, r *http.Request) { - height, _ := strconv.Atoi(r.URL.Query().Get("height")) if height <= 0 { diff --git a/cmd/minter/main.go b/cmd/minter/main.go index 6a5b2f076..8d8d44506 100644 --- a/cmd/minter/main.go +++ b/cmd/minter/main.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" + rpc "github.com/tendermint/tendermint/rpc/client" "os" ) @@ -35,10 +36,15 @@ func main() { app := minter.NewMinterBlockchain() node := startTendermintNode(app) - app.RunRPC(node) + client := rpc.NewLocal(node) + status, _ := client.Status() + if status.NodeInfo.Network != genesis.Network { + log.Error("Different networks") + os.Exit(1) + } if !cfg.ValidatorMode { - go api.RunApi(app, node) + go api.RunApi(app, client) go gui.Run(cfg.GUIListenAddress) } diff --git a/config/config.go b/config/config.go index dfb993855..f198fa740 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ import ( tmConfig "github.com/tendermint/tendermint/config" "os" "path/filepath" + "time" ) var ( @@ -55,28 +56,20 @@ func DefaultConfig() *Config { cfg.DBPath = "tmdata" cfg.Mempool.CacheSize = 100000 - cfg.Mempool.WalPath = "tmdata/mempool.wal" cfg.Mempool.Recheck = true - cfg.Mempool.RecheckEmpty = true - cfg.Mempool.Size = 10000 cfg.Consensus.WalPath = "tmdata/cs.wal/wal" - cfg.Consensus.TimeoutPropose = 2000 - cfg.Consensus.TimeoutProposeDelta = 500 - cfg.Consensus.TimeoutPrevote = 1000 - cfg.Consensus.TimeoutPrevoteDelta = 500 - cfg.Consensus.TimeoutPrecommit = 1000 - cfg.Consensus.TimeoutPrecommitDelta = 500 - cfg.Consensus.TimeoutCommit = 4500 + cfg.Consensus.TimeoutPropose = 2 * time.Second + cfg.Consensus.TimeoutProposeDelta = 500 * time.Millisecond + cfg.Consensus.TimeoutPrevote = 1 * time.Second + cfg.Consensus.TimeoutPrevoteDelta = 500 * time.Millisecond + cfg.Consensus.TimeoutPrecommit = 1 * time.Second + cfg.Consensus.TimeoutPrecommitDelta = 500 * time.Millisecond + cfg.Consensus.TimeoutCommit = 4500 * time.Millisecond cfg.PrivValidator = "config/priv_validator.json" - cfg.NodeKey = "config/node_key.json" - cfg.P2P.AddrBook = "config/addrbook.json" - cfg.P2P.ListenAddress = "tcp://0.0.0.0:26656" - cfg.P2P.SendRate = 5120000 // 5mb/s - cfg.P2P.RecvRate = 5120000 // 5mb/s return cfg } @@ -174,7 +167,6 @@ func GetTmConfig() *tmConfig.Config { // BaseConfig defines the base configuration for a Tendermint node type BaseConfig struct { - // chainID is unexposed and immutable but here for convenience chainID string @@ -233,26 +225,38 @@ type BaseConfig struct { APIListenAddress string `mapstructure:"api_listen_addr"` ValidatorMode bool `mapstructure:"validator_mode"` + + KeepStateHistory bool `mapstructure:"keep_state_history"` + + APISimultaneousRequests int `mapstructure:"api_simultaneous_requests"` + + APIPerIPLimit int `mapstructure:"api_per_ip_limit"` + + APIPerIPLimitWindow time.Duration `mapstructure:"api_per_ip_limit_window"` } // DefaultBaseConfig returns a default base configuration for a Tendermint node func DefaultBaseConfig() BaseConfig { return BaseConfig{ - Genesis: defaultGenesisJSONPath, - PrivValidator: defaultPrivValPath, - NodeKey: defaultNodeKeyPath, - Moniker: defaultMoniker, - ProxyApp: "tcp://127.0.0.1:26658", - ABCI: "socket", - LogLevel: DefaultPackageLogLevels(), - ProfListenAddress: "", - FastSync: true, - FilterPeers: false, - DBBackend: "leveldb", - DBPath: "data", - GUIListenAddress: ":3000", - APIListenAddress: ":8841", - ValidatorMode: false, + Genesis: defaultGenesisJSONPath, + PrivValidator: defaultPrivValPath, + NodeKey: defaultNodeKeyPath, + Moniker: defaultMoniker, + ProxyApp: "tcp://127.0.0.1:26658", + ABCI: "socket", + LogLevel: DefaultPackageLogLevels(), + ProfListenAddress: "", + FastSync: true, + FilterPeers: false, + DBBackend: "leveldb", + DBPath: "data", + GUIListenAddress: ":3000", + APIListenAddress: ":8841", + ValidatorMode: false, + KeepStateHistory: false, + APISimultaneousRequests: 100, + APIPerIPLimit: 1000, + APIPerIPLimitWindow: 60 * time.Second, } } diff --git a/config/toml.go b/config/toml.go index 118d26700..7f191d0e8 100644 --- a/config/toml.go +++ b/config/toml.go @@ -76,6 +76,18 @@ api_listen_addr = "{{ .BaseConfig.APIListenAddress }}" # Sets node to be in validator mode. Disables API, events, history of blocks, indexes, etc. validator_mode = {{ .BaseConfig.ValidatorMode }} +# If set to true node will save old states. This can be useful for applications which need all blockchain history data. +keep_state_history = {{ .BaseConfig.KeepStateHistory }} + +# Limit for simultaneous requests to API +api_simultaneous_requests = {{ .BaseConfig.APISimultaneousRequests }} + +# Limit API requests for client (by IP) +api_per_ip_limit = {{ .BaseConfig.APIPerIPLimit }} + +# How often API requests limits will be cleared +api_per_ip_limit_window = "{{ .BaseConfig.APIPerIPLimitWindow }}" + # 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 @@ -158,7 +170,7 @@ addr_book_file = "{{ js .P2P.AddrBook }}" addr_book_strict = {{ .P2P.AddrBookStrict }} # Time to wait before flushing messages out on the connection, in ms -flush_throttle_timeout = {{ .P2P.FlushThrottleTimeout }} +flush_throttle_timeout = "{{ .P2P.FlushThrottleTimeout }}" # Maximum number of inbound peers max_num_inbound_peers = {{ .P2P.MaxNumInboundPeers }} @@ -191,7 +203,6 @@ private_peer_ids = "{{ .P2P.PrivatePeerIDs }}" [mempool] recheck = {{ .Mempool.Recheck }} -recheck_empty = {{ .Mempool.RecheckEmpty }} broadcast = {{ .Mempool.Broadcast }} wal_dir = "{{ js .Mempool.WalPath }}" diff --git a/core/minter/minter.go b/core/minter/minter.go index 510dac24b..389763ff7 100644 --- a/core/minter/minter.go +++ b/core/minter/minter.go @@ -13,14 +13,11 @@ import ( "github.com/MinterTeam/minter-go-node/eventsdb" "github.com/MinterTeam/minter-go-node/genesis" "github.com/MinterTeam/minter-go-node/helpers" - "github.com/MinterTeam/minter-go-node/log" "github.com/tendermint/go-amino" abciTypes "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/node" - rpc "github.com/tendermint/tendermint/rpc/client" "math/big" - "os" + "sync" "sync/atomic" ) @@ -35,12 +32,16 @@ type Blockchain struct { height int64 rewards *big.Int validatorsStatuses map[[20]byte]int8 - tendermintRPC *rpc.Local + + lock sync.RWMutex } const ( ValidatorPresent = 1 ValidatorAbsent = 2 + + StateDBPrefix = "s" + AppDBPrefix = "a" ) var ( @@ -49,15 +50,15 @@ var ( ) func NewMinterBlockchain() *Blockchain { - ldb, err := db.NewGoLevelDB("minter", utils.GetMinterHome()+"/data") + ldb, err := db.NewGoLevelDB("state", utils.GetMinterHome()+"/data") if err != nil { panic(err) } blockchain = &Blockchain{ - stateDB: db.NewPrefixDB(ldb, []byte("s")), - appDB: db.NewPrefixDB(ldb, []byte("a")), + stateDB: db.NewPrefixDB(ldb, []byte(StateDBPrefix)), + appDB: db.NewPrefixDB(ldb, []byte(AppDBPrefix)), } blockchain.updateCurrentRootHash() @@ -72,17 +73,6 @@ func NewMinterBlockchain() *Blockchain { return blockchain } -func (app *Blockchain) RunRPC(node *node.Node) { - app.tendermintRPC = rpc.NewLocal(node) - - status, _ := app.tendermintRPC.Status() - - if status.NodeInfo.Network != genesis.Network { - log.Error("Different networks") - os.Exit(1) - } -} - func (app *Blockchain) SetOption(req abciTypes.RequestSetOption) abciTypes.ResponseSetOption { return abciTypes.ResponseSetOption{} } @@ -158,7 +148,6 @@ func (app *Blockchain) BeginBlock(req abciTypes.RequestBeginBlock) abciTypes.Res } func (app *Blockchain) EndBlock(req abciTypes.RequestEndBlock) abciTypes.ResponseEndBlock { - var updates []abciTypes.ValidatorUpdate stateValidators := app.stateDeliver.GetStateValidators() @@ -176,7 +165,6 @@ func (app *Blockchain) EndBlock(req abciTypes.RequestEndBlock) abciTypes.Respons // accumulate rewards for i, val := range vals { - // skip if candidate is not present if app.validatorsStatuses[val.GetAddress()] != ValidatorPresent { continue @@ -312,7 +300,6 @@ func (app *Blockchain) CheckTx(rawTx []byte) abciTypes.ResponseCheckTx { } func (app *Blockchain) Commit() abciTypes.ResponseCommit { - hash, _, err := app.stateDeliver.Commit(false) if err != nil { @@ -344,7 +331,6 @@ func (app *Blockchain) Stop() { } func (app *Blockchain) updateCurrentRootHash() { - // todo: make provider result := app.appDB.Get([]byte("root")) copy(app.rootHash[:], result) @@ -359,14 +345,23 @@ func (app *Blockchain) updateCurrentRootHash() { } func (app *Blockchain) updateCurrentState() { + app.lock.Lock() + defer app.lock.Unlock() + app.stateCheck = state.NewForCheck(app.stateDeliver) } func (app *Blockchain) CurrentState() *state.StateDB { + app.lock.RLock() + defer app.lock.RUnlock() + return state.NewForCheck(app.stateCheck) } func (app *Blockchain) GetStateForHeight(height int) (*state.StateDB, error) { + app.lock.RLock() + defer app.lock.RUnlock() + return state.New(int64(height), app.stateDB) } @@ -375,7 +370,6 @@ func (app *Blockchain) Height() int64 { } func (app *Blockchain) getCurrentValidators() abciTypes.ValidatorUpdates { - result := app.appDB.Get([]byte("validators")) if len(result) == 0 { @@ -384,7 +378,7 @@ func (app *Blockchain) getCurrentValidators() abciTypes.ValidatorUpdates { var vals abciTypes.ValidatorUpdates - err := cdc.UnmarshalBinary(result, &vals) + err := cdc.UnmarshalBinaryLengthPrefixed(result, &vals) if err != nil { panic(err) @@ -394,7 +388,7 @@ func (app *Blockchain) getCurrentValidators() abciTypes.ValidatorUpdates { } func (app *Blockchain) saveCurrentValidators(vals abciTypes.ValidatorUpdates) { - data, err := cdc.MarshalBinary(vals) + data, err := cdc.MarshalBinaryLengthPrefixed(vals) if err != nil { panic(err) @@ -402,7 +396,3 @@ func (app *Blockchain) saveCurrentValidators(vals abciTypes.ValidatorUpdates) { app.appDB.Set([]byte("validators"), data) } - -func GetBlockchain() *Blockchain { - return blockchain -} diff --git a/core/state/balance_watcher.go b/core/state/balance_watcher.go deleted file mode 100644 index 9dbb1b7e6..000000000 --- a/core/state/balance_watcher.go +++ /dev/null @@ -1,44 +0,0 @@ -package state - -import ( - "encoding/json" - "github.com/MinterTeam/minter-go-node/core/types" - "math/big" -) - -var ( - BalanceChangeChan = make(chan BalanceChangeStruct, 10) -) - -type BalanceChangeStruct struct { - Address types.Address - Coin types.CoinSymbol - Balance *big.Int - BalanceInBasecoin *big.Int -} - -func (s BalanceChangeStruct) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Address types.Address `json:"address"` - Coin string `json:"coin"` - Balance string `json:"balance"` - BalanceInBasecoin string `json:"balance_in_basecoin"` - }{ - Address: s.Address, - Coin: s.Coin.String(), - Balance: s.Balance.String(), - BalanceInBasecoin: s.BalanceInBasecoin.String(), - }) -} - -func EmitBalanceChange(address types.Address, coin types.CoinSymbol, balance *big.Int) { - if len(BalanceChangeChan) >= 10 { - return - } - - BalanceChangeChan <- BalanceChangeStruct{ - Address: address, - Coin: coin, - Balance: balance, - } -} diff --git a/core/state/state_object.go b/core/state/state_object.go index 7be9b5e8e..ed41cf3e8 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -169,7 +169,6 @@ func (s *stateAccount) SubBalance(coinSymbol types.CoinSymbol, amount *big.Int) } func (s *stateAccount) SetBalance(coinSymbol types.CoinSymbol, amount *big.Int) { - EmitBalanceChange(s.address, coinSymbol, amount) s.setBalance(coinSymbol, amount) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 5c508726b..fa9d2ffea 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -134,6 +134,7 @@ func (s *StateDB) Clear() { s.stateCandidates = nil s.stateCandidatesDirty = false s.stakeCache = make(map[types.CoinSymbol]StakeCache) + s.lock = sync.Mutex{} } // Retrieve the balance from the given address or 0 if object not found @@ -151,10 +152,8 @@ func (s *StateDB) GetBalances(addr types.Address) Balances { return stateObject.Balances() } - def := make(map[types.CoinSymbol]*big.Int) - return Balances{ - Data: def, + Data: make(map[types.CoinSymbol]*big.Int), } } @@ -611,7 +610,6 @@ func (s *StateDB) CreateCandidate( // Commit writes the state to the underlying in-memory trie database. func (s *StateDB) Commit(deleteEmptyObjects bool) (root []byte, version int64, err error) { - // Commit objects to the trie. for _, addr := range getOrderedObjectsKeys(s.stateAccountsDirty) { stateObject := s.stateAccounts[addr] @@ -661,7 +659,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root []byte, version int64, e hash, version, err := s.iavl.SaveVersion() - if cfg.ValidatorMode && version > 1 { + if !cfg.KeepStateHistory && version > 1 { err = s.iavl.DeleteVersion(version - 1) if err != nil { @@ -1043,6 +1041,18 @@ func (s *StateDB) SetCandidateOffline(pubkey []byte) { s.setStateCandidates(stateCandidates) s.MarkStateCandidateDirty() + + vals := s.getStateValidators() + + for i := range vals.data { + validator := &vals.data[i] + if bytes.Equal(validator.PubKey, pubkey) { + validator.toDrop = true + } + } + + s.setStateValidators(vals) + s.MarkStateValidatorsDirty() } func (s *StateDB) SetValidatorPresent(height int64, address [20]byte) { diff --git a/core/transaction/switch_candidate_status.go b/core/transaction/switch_candidate_status.go index cc7a15beb..1ccbd4019 100644 --- a/core/transaction/switch_candidate_status.go +++ b/core/transaction/switch_candidate_status.go @@ -34,7 +34,6 @@ func (data SetCandidateOnData) Gas() int64 { } func (data SetCandidateOnData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPool *big.Int, currentBlock int64) Response { - if !context.CoinExists(tx.GasCoin) { return Response{ Code: code.CoinNotExists, @@ -114,7 +113,6 @@ func (data SetCandidateOffData) Gas() int64 { } func (data SetCandidateOffData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPool *big.Int, currentBlock int64) Response { - if !context.CoinExists(tx.GasCoin) { return Response{ Code: code.CoinNotExists, diff --git a/core/validators/validators.go b/core/validators/validators.go index 837af6e37..43de75779 100644 --- a/core/validators/validators.go +++ b/core/validators/validators.go @@ -1,13 +1,15 @@ package validators func GetValidatorsCountForBlock(block int64) int { - count := 16 + (block/518400)*4 + return 100 // only for current testnet - if count > 256 { - return 256 - } - - return int(count) + //count := 16 + (block/518400)*4 + // + //if count > 256 { + // return 256 + //} + // + //return int(count) } func GetCandidatesCountForBlock(block int64) int { diff --git a/core/validators/validators_test.go b/core/validators/validators_test.go index 70cd7bf24..a5e1f4756 100644 --- a/core/validators/validators_test.go +++ b/core/validators/validators_test.go @@ -5,7 +5,7 @@ import ( ) type Results struct { - Block uint64 + Block int64 Result int } diff --git a/docker-compose.yml b/docker-compose.yml index 71567a4bd..811959508 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.4" services: minter: - image: minterteam/minter:0.5.1 + image: minterteam/minter:0.6.0 volumes: - ~/.minter:/minter ports: diff --git a/eventsdb/eventsdb.go b/eventsdb/eventsdb.go index 08dae00c9..e5c326e80 100644 --- a/eventsdb/eventsdb.go +++ b/eventsdb/eventsdb.go @@ -109,7 +109,7 @@ func (db *EventsDB) FlushEvents(height int64) error { } events := db.getEvents(height) - bytes, err := cdc.MarshalBinary(events) + bytes, err := cdc.MarshalBinaryLengthPrefixed(events) if err != nil { return err @@ -135,7 +135,7 @@ func (db *EventsDB) LoadEvents(height int64) Events { } var decoded Events - err := cdc.UnmarshalBinary(data, &decoded) + err := cdc.UnmarshalBinaryLengthPrefixed(data, &decoded) if err != nil { panic(err) diff --git a/genesis/genesis.go b/genesis/genesis.go index 9fb4dfc2c..0b72c2c3d 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -12,7 +12,8 @@ import ( ) var ( - Network = "minter-test-network-23" + Network = "minter-test-network-24" + genesisTime = time.Date(2018, 10, 30, 15, 0, 0, 0, time.UTC) ) func GetTestnetGenesis() (*tmtypes.GenesisDoc, error) { @@ -63,7 +64,7 @@ func GetTestnetGenesis() (*tmtypes.GenesisDoc, error) { genesis := tmtypes.GenesisDoc{ ChainID: Network, - GenesisTime: time.Date(2018, 10, 15, 15, 0, 0, 0, time.UTC), + GenesisTime: genesisTime, ConsensusParams: nil, Validators: validators, AppHash: appHash[:], diff --git a/gui/gui-packr.go b/gui/a_gui-packr.go similarity index 99% rename from gui/gui-packr.go rename to gui/a_gui-packr.go index 945658db0..5dce5808c 100644 --- a/gui/gui-packr.go +++ b/gui/a_gui-packr.go @@ -1,4 +1,4 @@ -// Code generated by github.com/gobuffalo/packr. DO NOT EDIT +// Code generated by github.com/gobuffalo/packr. DO NOT EDIT. package gui diff --git a/scripts/dist.sh b/scripts/dist.sh new file mode 100644 index 000000000..ad4316ccd --- /dev/null +++ b/scripts/dist.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +set -e + +# WARN: non hermetic build (people must run this script inside docker to +# produce deterministic binaries). + +# Get the version from the environment, or try to figure it out. +if [ -z $VERSION ]; then + VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) +fi +if [ -z "$VERSION" ]; then + echo "Please specify a version." + exit 1 +fi +echo "==> Building version $VERSION..." + +# Delete the old dir +echo "==> Removing old directory..." +rm -rf build/pkg +mkdir -p build/pkg + +# Get the git commit +GIT_COMMIT="$(git rev-parse --short=8 HEAD)" +GIT_IMPORT="github.com/MinterTeam/minter-go-node/version" + +# Determine the arch/os combos we're building for +XC_ARCH=${XC_ARCH:-"amd64"} +XC_OS=${XC_OS:-"darwin linux windows"} + +# Make sure build tools are available. +make get_tools + +# Get VENDORED dependencies +make get_vendor_deps + +packr + +# Build! +# ldflags: -s Omit the symbol table and debug information. +# -w Omit the DWARF symbol table. +echo "==> Building..." +IFS=' ' read -ra arch_list <<< "$XC_ARCH" +IFS=' ' read -ra os_list <<< "$XC_OS" +for arch in "${arch_list[@]}"; do + for os in "${os_list[@]}"; do + echo "--> $os/$arch" + GOOS=${os} GOARCH=${arch} go build -ldflags "-s -w -X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}" -tags="${BUILD_TAGS}" -o "build/pkg/${os}_${arch}/minter" ./cmd/minter + done +done + +# Zip all the files. +echo "==> Packaging..." +for PLATFORM in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type d); do + OSARCH=$(basename "${PLATFORM}") + echo "--> ${OSARCH}" + + pushd "$PLATFORM" >/dev/null 2>&1 + zip "../${OSARCH}.zip" ./* + popd >/dev/null 2>&1 +done + +# Add "minter" and $VERSION prefix to package name. +rm -rf ./build/dist +mkdir -p ./build/dist +for FILENAME in $(find ./build/pkg -mindepth 1 -maxdepth 1 -type f); do + FILENAME=$(basename "$FILENAME") + cp "./build/pkg/${FILENAME}" "./build/dist/minter_${VERSION}_${FILENAME}" +done + +# Make the checksums. +pushd ./build/dist +shasum -a256 ./* > "./minter_${VERSION}_SHA256SUMS" +popd + +# Done +echo +echo "==> Results:" +ls -hl ./build/dist + +exit 0 diff --git a/tests/acceptance/api/tests.go b/tests/acceptance/api/tests.go deleted file mode 100644 index 991323808..000000000 --- a/tests/acceptance/api/tests.go +++ /dev/null @@ -1,59 +0,0 @@ -package api - -import ( - "encoding/json" - "github.com/tendermint/tendermint/rpc/core/types" - "io/ioutil" - "net/http" -) - -func TestApiStatus() error { - result, err := http.Get("http://localhost:8841/api/status") - - if err != nil { - return err - } - - data, err := ioutil.ReadAll(result.Body) - - if err != nil { - return err - } - - var status struct { - Code int `json:"code"` - Result struct { - LatestBlockHash string `json:"latest_block_hash"` - LatestAppHash string `json:"latest_app_hash"` - LatestBlockHeight int `json:"latest_block_height"` - LatestBlockTime string `json:"latest_block_time"` - } `json:"result"` - } - - err = json.Unmarshal(data, &status) - - return err -} - -func TestApiBlock() error { - result, err := http.Get("http://localhost:8841/api/block/1") - - if err != nil { - return err - } - - data, err := ioutil.ReadAll(result.Body) - - if err != nil { - return err - } - - var blockResult struct { - Code int `json:"code"` - Result core_types.ResultBlock `json:"result"` - } - - err = json.Unmarshal(data, &blockResult) - - return err -} diff --git a/tests/acceptance/docker/default/config.toml b/tests/acceptance/docker/default/config.toml deleted file mode 100644 index 6f9982c1f..000000000 --- a/tests/acceptance/docker/default/config.toml +++ /dev/null @@ -1,163 +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:46658" - -# A custom human readable name for this node -moniker = "MinterNode" - -# 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 = "" - -# 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:46657" - -# TCP or UNIX socket address for the gRPC server to listen on -# NOTE: This server only supports /broadcast_tx_commit -grpc_laddr = "" - -# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool -unsafe = false - -##### peer to peer configuration options ##### -[p2p] - -# Address to listen for incoming connections -laddr = "tcp://0.0.0.0:46656" - -# Comma separated list of seed nodes to connect to -seeds = "" - -# Comma separated list of nodes to keep persistent connections to -persistent_peers = "" - -# 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_msg_packet_payload_size = 2048 - -# Rate at which packets can be sent, in bytes/second -send_rate = 1024000 - -# Rate at which packets can be received, in bytes/second -recv_rate = 1024000 - -# 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 - -##### mempool configuration options ##### -[mempool] - -recheck = true -recheck_empty = true -broadcast = true -wal_dir = "data/mempool.wal" - -##### consensus configuration options ##### -[consensus] - -wal_file = "data/cs.wal/wal" -wal_light = false - -# 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 - -# BlockSize -max_block_size_txs = 10000 -max_block_size_bytes = 1 - -# 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 \ No newline at end of file diff --git a/tests/acceptance/docker/default/genesis.json b/tests/acceptance/docker/default/genesis.json deleted file mode 100644 index 0f3844fc0..000000000 --- a/tests/acceptance/docker/default/genesis.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "genesis_time": "2018-06-09T00:00:00Z", - "chain_id": "minter-local-test-network", - "validators": [ - { - "pub_key": { - "type": "AC26791624DE60", - "value": "U4xehAxzuRaDFoTfpPVDrr2cEU5Zsa/U1hNEu/E4Yps=" - }, - "power": 100, - "name": "" - } - ], - "app_state": { - "first_validator_address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", - "initial_balances": [ - { - "address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", - "balance": { - "MNT": "10000000000000000000000000" - } - }, - { - "address": "Mxfe60014a6e9ac91618f5d1cab3fd58cded61ee99", - "balance": { - "MNT": "10000000000000000000000000" - } - } - ] - }, - "app_hash": "0000000000000000000000000000000000000000000000000000000000000000" -} \ No newline at end of file diff --git a/tests/acceptance/docker/default/priv_validator.json b/tests/acceptance/docker/default/priv_validator.json deleted file mode 100644 index 844fbffaf..000000000 --- a/tests/acceptance/docker/default/priv_validator.json +++ /dev/null @@ -1 +0,0 @@ -{"address":"30629BFDD02CF9FCDC90D01BBCC852F42E5B7C5F","pub_key":{"type":"AC26791624DE60","value":"U4xehAxzuRaDFoTfpPVDrr2cEU5Zsa/U1hNEu/E4Yps="},"last_height":0,"last_round":0,"last_step":0,"priv_key":{"type":"954568A3288910","value":"ZbQ7z1qT4wQJMvPckvPUAex2Bv1GujkIhWCM/L3U1qJTjF6EDHO5FoMWhN+k9UOuvZwRTlmxr9TWE0S78Thimw=="}} diff --git a/tests/acceptance/docker/docker-compose.yml b/tests/acceptance/docker/docker-compose.yml deleted file mode 100644 index b33434147..000000000 --- a/tests/acceptance/docker/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: "3.4" -services: - minter: - image: minterteam/minter:latest - volumes: - - ./data/.minter:/minter - ports: - - "8841:8841" diff --git a/tests/acceptance/helpers/helpers.go b/tests/acceptance/helpers/helpers.go deleted file mode 100644 index 94d0a747d..000000000 --- a/tests/acceptance/helpers/helpers.go +++ /dev/null @@ -1,134 +0,0 @@ -package helpers - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "os/exec" - "reflect" - "runtime" - "strings" - "time" -) - -var ( - path string -) - -func init() { - path, _ = os.Getwd() -} - -func GetTestName(test func() error) string { - path := strings.Split(runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name(), "/") - return path[len(path)-1] -} - -func CopyFile(fromFile string, toFile string) { - from, err := os.Open(fromFile) - if err != nil { - log.Fatal(err) - } - defer from.Close() - - to, err := os.OpenFile(toFile, os.O_RDWR|os.O_CREATE, 0666) - if err != nil { - log.Fatal(err) - } - defer to.Close() - - _, err = io.Copy(to, from) - if err != nil { - log.Fatal(err) - } -} - -func RunMinter(isReady chan bool) { - StopMinter() - - logger := log.New(os.Stdout, "minter-node ", log.LstdFlags) - - logger.Println("Starting Minter Node...") - - err := os.MkdirAll(path+"/docker/data/.tendermint/config/", os.ModePerm) - err = os.MkdirAll(path+"/docker/data/.tendermint/data/", os.ModePerm) - - if err != nil { - panic(err) - } - - err = exec.Command("cp", "-r", path+"/docker/default/", path+"/docker/data/.tendermint/config/").Run() - - if err != nil { - panic(err) - } - - cmd := exec.Command("docker-compose", "--file", path+"/docker/docker-compose.yml", "--project-name", "minter-test", "up") - cmd.Stderr = os.Stderr - err = cmd.Start() - - if err != nil { - logger.Fatal(err) - } - - timer := time.NewTimer(20 * time.Second) - - go func() { - <-timer.C - StopMinter() - logger.Fatalln("Minter node start timeout...") - }() - - for { - var status struct { - Code int `json:"code"` - Result struct { - LatestBlockHash string `json:"latest_block_hash"` - LatestAppHash string `json:"latest_app_hash"` - LatestBlockHeight int `json:"latest_block_height"` - LatestBlockTime string `json:"latest_block_time"` - } `json:"result"` - } - - response, err := http.Get("http://localhost:8841/api/status") - - if err == nil { - - data, _ := ioutil.ReadAll(response.Body) - - json.Unmarshal(data, &status) - - if status.Result.LatestBlockHeight > 0 { - isReady <- true - return - } - } - - time.Sleep(1 * time.Second) - } - - err = cmd.Wait() - if err != nil { - logger.Fatalf("Minter finished with error: %v", err) - } -} - -func StopMinter() { - fmt.Println("Stopping Minter...") - exec.Command("docker-compose", "--file", path+"/docker/docker-compose.yml", "--project-name", "minter-test", "stop").Run() - exec.Command("docker-compose", "--file", path+"/docker/docker-compose.yml", "--project-name", "minter-test", "rm", "--force").Run() - - err := exec.Command("rm", "-rf", path+"/docker/data/.minter").Run() - if err != nil { - log.Fatalf("%s", err) - } - - err = exec.Command("rm", "-rf", path+"/docker/data/.tendermint").Run() - if err != nil { - log.Fatalf("%s", err) - } -} diff --git a/tests/acceptance/main.go b/tests/acceptance/main.go deleted file mode 100644 index 8c0dce2be..000000000 --- a/tests/acceptance/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "github.com/MinterTeam/minter-go-node/tests/acceptance/api" - "github.com/MinterTeam/minter-go-node/tests/acceptance/helpers" - "log" - "os" - "strings" - "time" -) - -var ( - tests = []func() error{ - api.TestApiStatus, - api.TestApiBlock, - } - logger = log.New(os.Stdout, "main-test-routine ", log.LstdFlags) -) - -func main() { - logger.Println("Starting Minter Go Node acceptance testing...") - - minterNodeReady := make(chan bool) - go helpers.RunMinter(minterNodeReady) - <-minterNodeReady - - defer helpers.StopMinter() - - for _, test := range tests { - testName := helpers.GetTestName(test) - - if len(os.Args) > 1 { - if !strings.Contains(strings.ToLower(testName), strings.ToLower(os.Args[1])) { - continue - } - } - - logger.Printf("Running \"%s\"... \n", testName) - - start := time.Now() - err := test() - - if err != nil { - helpers.StopMinter() - logger.Fatalf("Failed test \"%s\"\nReason: %s", testName, err) - } - - elapsed := time.Since(start) - - logger.Printf("Completed \"%s\" in %s \n", testName, elapsed) - } - - logger.Printf("Completed all tests\n") -} diff --git a/version/version.go b/version/version.go index 5cdea40ad..f5cf04df6 100755 --- a/version/version.go +++ b/version/version.go @@ -3,13 +3,13 @@ package version // Version components const ( Maj = "0" - Min = "5" - Fix = "1" + Min = "6" + Fix = "0" ) var ( // Must be a string because scripts like dist.sh read this file. - Version = "0.5.1" + Version = "0.6.0" // GitCommit is the current HEAD set using ldflags. GitCommit string