From 2d04468296068f0df683e71da526b868d9a90517 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Fri, 5 Jul 2019 11:31:59 +0800 Subject: [PATCH 01/31] bump version to 0.32.0-dev --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index b3853175d3e..845c093ffab 100644 --- a/version/version.go +++ b/version/version.go @@ -18,7 +18,7 @@ const ( // TMCoreSemVer is the current version of Tendermint Core. // It's the Semantic Version of the software. // Must be a string because scripts like dist.sh read this file. - TMCoreSemVer = "0.31.0" + TMCoreSemVer = "0.32.0-dev" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From 3dbb2bf5b82a7e254be4954c08d831a2c52a0efd Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 21 Aug 2019 14:39:04 +0800 Subject: [PATCH 02/31] replace dep by go modules --- Gopkg.lock | 555 ----------------------------------------------------- Gopkg.toml | 109 ----------- go.mod | 51 +++++ go.sum | 114 +++++++++++ 4 files changed, 165 insertions(+), 664 deletions(-) delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml create mode 100644 go.mod create mode 100644 go.sum diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 9880f3f3988..00000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,555 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[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"] - pruneopts = "UT" - revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" - -[[projects]] - digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf" - name = "github.com/btcsuite/btcutil" - packages = [ - "base58", - "bech32", - ] - pruneopts = "UT" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" - -[[projects]] - digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "UT" - revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" - version = "v1.1.1" - -[[projects]] - digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70" - name = "github.com/fortytw2/leaktest" - packages = ["."] - pruneopts = "UT" - revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e" - version = "v1.2.0" - -[[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:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" - name = "github.com/go-stack/stack" - packages = ["."] - pruneopts = "UT" - revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" - version = "v1.8.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:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" - name = "github.com/gorilla/websocket" - packages = ["."] - pruneopts = "UT" - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" - -[[projects]] - digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "UT" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - 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: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 = ["."] - 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:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "UT" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promhttp", - ] - pruneopts = "UT" - revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3" - version = "v0.9.1" - -[[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", - ] - pruneopts = "UT" - revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" - -[[projects]] - branch = "master" - digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "nfs", - "xfs", - ] - pruneopts = "UT" - revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" - -[[projects]] - digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" - name = "github.com/rcrowley/go-metrics" - packages = ["."] - pruneopts = "UT" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - -[[projects]] - digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758" - name = "github.com/rs/cors" - packages = ["."] - pruneopts = "UT" - revision = "9a47f48565a795472d43519dd49aac781f3034fb" - version = "v1.6.0" - -[[projects]] - digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "UT" - revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" - version = "v1.1.2" - -[[projects]] - digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "UT" - revision = "8c9545af88b134710ab1cd196795e7f2388358d7" - version = "v1.3.0" - -[[projects]] - digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" - name = "github.com/spf13/cobra" - packages = ["."] - pruneopts = "UT" - revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" - version = "v0.0.1" - -[[projects]] - digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "UT" - revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" - version = "v1.0.0" - -[[projects]] - digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" - name = "github.com/spf13/viper" - packages = ["."] - pruneopts = "UT" - revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" - version = "v1.0.0" - -[[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:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c" - 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 = "6b91fda63f2e36186f1c9d0e48578defb69c5d43" - -[[projects]] - digest = "1:83f5e189eea2baad419a6a410984514266ff690075759c87e9ede596809bd0b8" - name = "github.com/tendermint/btcd" - packages = ["btcec"] - pruneopts = "UT" - revision = "80daadac05d1cd29571fccf27002d79667a88b58" - version = "v0.1.1" - -[[projects]] - digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a" - name = "github.com/tendermint/go-amino" - packages = ["."] - pruneopts = "UT" - revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885" - version = "v0.14.1" - -[[projects]] - digest = "1:00d2b3e64cdc3fa69aa250dfbe4cc38c4837d4f37e62279be2ae52107ffbbb44" - name = "golang.org/x/crypto" - packages = [ - "bcrypt", - "blowfish", - "chacha20poly1305", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "hkdf", - "internal/chacha20", - "internal/subtle", - "nacl/box", - "nacl/secretbox", - "openpgp/armor", - "openpgp/errors", - "poly1305", - "ripemd160", - "salsa20/salsa", - ] - pruneopts = "UT" - revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" - -[[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:6f86e2f2e2217cd4d74dec6786163cf80e4d2b99adb341ecc60a45113b844dca" - name = "golang.org/x/sys" - packages = [ - "cpu", - "unix", - ] - pruneopts = "UT" - revision = "7e31e0c00fa05cb5fbf4347b585621d6709e19a4" - -[[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:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "UT" - revision = "b69ba1387ce2108ac9bc8e8e5e5a46e7d5c72313" - -[[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/btcsuite/btcutil/base58", - "github.com/btcsuite/btcutil/bech32", - "github.com/fortytw2/leaktest", - "github.com/go-kit/kit/log", - "github.com/go-kit/kit/log/level", - "github.com/go-kit/kit/log/term", - "github.com/go-kit/kit/metrics", - "github.com/go-kit/kit/metrics/discard", - "github.com/go-kit/kit/metrics/prometheus", - "github.com/go-logfmt/logfmt", - "github.com/gogo/protobuf/gogoproto", - "github.com/gogo/protobuf/jsonpb", - "github.com/gogo/protobuf/proto", - "github.com/gogo/protobuf/types", - "github.com/golang/protobuf/proto", - "github.com/golang/protobuf/ptypes/timestamp", - "github.com/gorilla/websocket", - "github.com/jmhodges/levigo", - "github.com/pkg/errors", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/rcrowley/go-metrics", - "github.com/rs/cors", - "github.com/spf13/cobra", - "github.com/spf13/viper", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/require", - "github.com/syndtr/goleveldb/leveldb", - "github.com/syndtr/goleveldb/leveldb/errors", - "github.com/syndtr/goleveldb/leveldb/iterator", - "github.com/syndtr/goleveldb/leveldb/opt", - "github.com/tendermint/btcd/btcec", - "github.com/tendermint/go-amino", - "golang.org/x/crypto/bcrypt", - "golang.org/x/crypto/chacha20poly1305", - "golang.org/x/crypto/curve25519", - "golang.org/x/crypto/ed25519", - "golang.org/x/crypto/hkdf", - "golang.org/x/crypto/nacl/box", - "golang.org/x/crypto/nacl/secretbox", - "golang.org/x/crypto/openpgp/armor", - "golang.org/x/crypto/ripemd160", - "golang.org/x/net/context", - "golang.org/x/net/netutil", - "google.golang.org/grpc", - "google.golang.org/grpc/credentials", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 5ed5ddd5f05..00000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,109 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true -# -########################################################### - -# Allow only patch releases for serialization libraries -[[constraint]] - name = "github.com/tendermint/go-amino" - version = "~0.14.1" - -[[constraint]] - name = "github.com/gogo/protobuf" - version = "~1.1.1" - -[[constraint]] - name = "github.com/golang/protobuf" - version = "~1.1.0" - -# Allow only minor releases for other libraries -[[constraint]] - name = "github.com/go-kit/kit" - version = "^0.6.0" - -[[constraint]] - name = "github.com/gorilla/websocket" - version = "^1.2.0" - -[[constraint]] - name = "github.com/rs/cors" - version = "^1.6.0" - -[[constraint]] - name = "github.com/pkg/errors" - version = "^0.8.0" - -[[constraint]] - name = "github.com/spf13/cobra" - version = "^0.0.1" - -[[constraint]] - name = "github.com/spf13/viper" - version = "^1.0.0" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "^1.2.1" - -[[constraint]] - name = "google.golang.org/grpc" - version = "^1.13.0" - -[[constraint]] - name = "github.com/fortytw2/leaktest" - version = "^1.2.0" - -[[constraint]] - name = "github.com/prometheus/client_golang" - version = "^0.9.1" - -[[constraint]] - name = "github.com/tendermint/btcd" - version = "v0.1.1" - -################################### -## Some repos dont have releases. -## Pin to revision - -[[constraint]] - name = "golang.org/x/crypto" - revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" - -[[override]] - name = "github.com/jmhodges/levigo" - revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" - -# last revision used by go-crypto -[[constraint]] - name = "github.com/btcsuite/btcutil" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" - -[[constraint]] - name = "github.com/rcrowley/go-metrics" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - -[[constraint]] - name = "golang.org/x/net" - revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" - -[prune] - go-tests = true - unused-packages = true diff --git a/go.mod b/go.mod new file mode 100644 index 00000000000..10065184e29 --- /dev/null +++ b/go.mod @@ -0,0 +1,51 @@ +module github.com/tendermint/tendermint + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/VividCortex/gohistogram v1.0.0 // indirect + github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect + github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 + github.com/fortytw2/leaktest v1.2.0 + github.com/go-kit/kit v0.6.0 + github.com/go-logfmt/logfmt v0.3.0 + github.com/go-stack/stack v1.8.0 // indirect + github.com/gogo/protobuf v1.1.1 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/protobuf v1.2.0 + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/google/gofuzz v1.0.0 // indirect + github.com/gorilla/websocket v1.2.0 + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023 + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect + github.com/magiconair/properties v1.8.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/onsi/ginkgo v1.9.0 // indirect + github.com/onsi/gomega v1.6.0 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pkg/errors v0.8.0 + github.com/prometheus/client_golang v0.9.1 + github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect + github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect + github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d // indirect + github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 + github.com/rs/cors v1.6.0 + github.com/spf13/afero v1.1.2 // indirect + github.com/spf13/cast v1.3.0 // indirect + github.com/spf13/cobra v0.0.1 + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/spf13/pflag v1.0.3 // indirect + github.com/spf13/viper v1.0.0 + github.com/stretchr/testify v1.2.2 + github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e + github.com/tendermint/btcd v0.1.1 + github.com/tendermint/go-amino v0.14.1 + golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 + golang.org/x/net v0.0.0-20180906233101-161cd47e91fd + golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect + golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 // indirect + google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 // indirect + google.golang.org/grpc v1.13.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000000..3c18143ca72 --- /dev/null +++ b/go.sum @@ -0,0 +1,114 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 h1:BsZAJgCuMsoFZMZNyj7Lyt6sS8anDhedVrAMCOyPMIo= +github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +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/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q= +github.com/fortytw2/leaktest v1.2.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/go-kit/kit v0.6.0 h1:wTifptAGIyIuir4bRyN4h7+kAa2a4eepLYVmRe5qqQ8= +github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023 h1:y5P5G9cANJZt3MXlMrgELo5mNLZPXH8aGFFFG7IzPU0= +github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= +github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M= +github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/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 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 h1:Cto4X6SVMWRPBkJ/3YHn1iDGDGc/Z+sW+AEMKHMVvN4= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +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/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +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.1 h1:zZh3X5aZbdnoj+4XkaBxKfhO4ot82icYdhhREIAXIj8= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +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.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e h1:91EeXI4y4ShkyzkMqZ7QP/ZTIqwXp3RuDu5WFzxcFAs= +github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +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/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 h1:biUuj9O+0+XckRUCDzjoOGm6yFV5c0IHbm1ODP3e4Zw= +golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0 h1:bHIbVsCwmvbArgCJmLdgOdHFXlKqTOVjbibbS19cXHc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From a5307d7e7e0cadae49f7123ea2172766ecbf354e Mon Sep 17 00:00:00 2001 From: Haifeng Xi Date: Wed, 21 Aug 2019 19:15:49 +0800 Subject: [PATCH 03/31] revert #77 --- consensus/wal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/wal.go b/consensus/wal.go index 26428a4c626..740041aa77c 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -116,7 +116,7 @@ func (wal *baseWAL) OnStart() error { // Use Wait() to ensure it's finished shutting down // before cleaning up files. func (wal *baseWAL) OnStop() { - wal.group.Flush() +// wal.group.Flush() wal.group.Stop() wal.group.Close() } From 926392b21cb53533705682f4b663db5ad7205444 Mon Sep 17 00:00:00 2001 From: Haifeng Xi Date: Wed, 21 Aug 2019 20:56:52 +0800 Subject: [PATCH 04/31] reincarnate #77 --- consensus/wal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/wal.go b/consensus/wal.go index 740041aa77c..26428a4c626 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -116,7 +116,7 @@ func (wal *baseWAL) OnStart() error { // Use Wait() to ensure it's finished shutting down // before cleaning up files. func (wal *baseWAL) OnStop() { -// wal.group.Flush() + wal.group.Flush() wal.group.Stop() wal.group.Close() } From 638ee5a9d9e64612588ee96c79710dd5aba944ef Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Fri, 23 Aug 2019 10:53:52 +0800 Subject: [PATCH 05/31] update p2p to v0.32 --- go.mod | 24 +-- go.sum | 58 ++++-- libs/common/net.go | 17 ++ node/node.go | 4 +- p2p/README.md | 10 +- p2p/base_reactor.go | 24 ++- p2p/{wire.go => codec.go} | 2 +- p2p/conn/{wire.go => codec.go} | 2 +- p2p/conn/connection.go | 117 +++++++---- p2p/conn/connection_test.go | 13 +- p2p/conn/secret_connection.go | 111 ++++++---- p2p/conn/secret_connection_test.go | 83 ++++++-- p2p/dummy/peer.go | 100 --------- p2p/errors.go | 24 ++- p2p/metrics.go | 14 +- p2p/mock/reactor.go | 23 ++ p2p/netaddress.go | 85 ++++---- p2p/netaddress_test.go | 72 +++---- p2p/node_info.go | 2 +- p2p/node_info_test.go | 2 +- p2p/peer.go | 18 +- p2p/pex/addrbook.go | 50 ++--- p2p/pex/addrbook_test.go | 5 +- p2p/pex/{wire.go => codec.go} | 2 +- p2p/pex/errors.go | 21 +- p2p/pex/file.go | 13 +- p2p/pex/known_address.go | 12 -- p2p/pex/pex_reactor.go | 323 ++++++++++++++++------------- p2p/pex/pex_reactor_test.go | 195 +++++++++-------- p2p/switch.go | 235 +++++++++++++++------ p2p/switch_test.go | 187 +++++++++++++---- p2p/test_util.go | 24 +-- p2p/transport.go | 62 ++++-- p2p/transport_test.go | 176 ++++++++-------- p2p/trust/metric_test.go | 1 + p2p/trust/store.go | 4 +- p2p/trust/store_test.go | 2 +- p2p/upnp/upnp.go | 2 +- rpc/core/net.go | 4 +- rpc/core/pipe.go | 3 +- 40 files changed, 1254 insertions(+), 872 deletions(-) rename p2p/{wire.go => codec.go} (81%) rename p2p/conn/{wire.go => codec.go} (83%) delete mode 100644 p2p/dummy/peer.go create mode 100644 p2p/mock/reactor.go rename p2p/pex/{wire.go => codec.go} (72%) diff --git a/go.mod b/go.mod index 10065184e29..d51326a941e 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,6 @@ module github.com/tendermint/tendermint require ( - github.com/BurntSushi/toml v0.3.1 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 @@ -10,22 +9,21 @@ require ( github.com/go-logfmt/logfmt v0.3.0 github.com/go-stack/stack v1.8.0 // indirect github.com/gogo/protobuf v1.1.1 - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect - github.com/golang/protobuf v1.2.0 - github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/golang/protobuf v1.3.2 github.com/google/gofuzz v1.0.0 // indirect github.com/gorilla/websocket v1.2.0 github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023 + github.com/jmhodges/levigo v1.0.0 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect + github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.1.2 // indirect github.com/onsi/ginkgo v1.9.0 // indirect github.com/onsi/gomega v1.6.0 // indirect github.com/pelletier/go-toml v1.2.0 // indirect - github.com/pkg/errors v0.8.0 + github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v0.9.1 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect @@ -38,14 +36,12 @@ require ( github.com/spf13/jwalterweatherman v1.0.0 // indirect github.com/spf13/pflag v1.0.3 // indirect github.com/spf13/viper v1.0.0 - github.com/stretchr/testify v1.2.2 - github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e + github.com/stretchr/testify v1.3.0 + github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 github.com/tendermint/btcd v0.1.1 github.com/tendermint/go-amino v0.14.1 - golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 - golang.org/x/net v0.0.0-20180906233101-161cd47e91fd - golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect - golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 // indirect - google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 // indirect - google.golang.org/grpc v1.13.0 + github.com/tendermint/tm-db v0.1.1 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 + golang.org/x/net v0.0.0-20190628185345-da137c7871d7 + google.golang.org/grpc v1.22.0 ) diff --git a/go.sum b/go.sum index 3c18143ca72..789b016e14c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= @@ -6,8 +7,12 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 h1:BsZAJgCuMsoFZMZNyj7Lyt6sS8anDhedVrAMCOyPMIo= github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +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/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -22,10 +27,14 @@ github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -35,10 +44,12 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023 h1:y5P5G9cANJZt3MXlMrgELo5mNLZPXH8aGFFFG7IzPU0= -github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -46,14 +57,16 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= github.com/onsi/ginkgo v1.9.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.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M= github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= -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 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno= @@ -80,30 +93,46 @@ 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.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e h1:91EeXI4y4ShkyzkMqZ7QP/ZTIqwXp3RuDu5WFzxcFAs= -github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +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 v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= 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/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= +github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 h1:biUuj9O+0+XckRUCDzjoOGm6yFV5c0IHbm1ODP3e4Zw= -golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.13.0 h1:bHIbVsCwmvbArgCJmLdgOdHFXlKqTOVjbibbS19cXHc= -google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -112,3 +141,4 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/libs/common/net.go b/libs/common/net.go index bdbe38f79ae..c7fff4cc390 100644 --- a/libs/common/net.go +++ b/libs/common/net.go @@ -24,3 +24,20 @@ func ProtocolAndAddress(listenAddr string) (string, string) { } return protocol, address } + +// GetFreePort gets a free port from the operating system. +// Ripped from https://github.com/phayes/freeport. +// BSD-licensed. +func GetFreePort() (int, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0, err + } + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil +} diff --git a/node/node.go b/node/node.go index cb55c6dd315..61933d56df9 100644 --- a/node/node.go +++ b/node/node.go @@ -554,7 +554,7 @@ func (n *Node) OnStart() error { } // Start the transport. - addr, err := p2p.NewNetAddressStringWithOptionalID(n.config.P2P.ListenAddress) + addr, err := p2p.NewNetAddressString(p2p.IDAddressString(n.nodeKey.ID(), n.config.P2P.ListenAddress)) if err != nil { return err } @@ -572,7 +572,7 @@ func (n *Node) OnStart() error { // Always connect to persistent peers if n.config.P2P.PersistentPeers != "" { - err = n.sw.DialPeersAsync(n.addrBook, splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " "), true) + err = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " ")) if err != nil { return err } diff --git a/p2p/README.md b/p2p/README.md index 819a5056b4a..df4c2ae018c 100644 --- a/p2p/README.md +++ b/p2p/README.md @@ -4,8 +4,8 @@ The p2p package provides an abstraction around peer-to-peer communication. Docs: -- [Connection](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/connection.md) for details on how connections and multiplexing work -- [Peer](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/peer.md) for details on peer ID, handshakes, and peer exchange -- [Node](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/node.md) for details about different types of nodes and how they should work -- [Pex](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/reactors/pex/pex.md) for details on peer discovery and exchange -- [Config](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/config.md) for details on some config option +- [Connection](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/connection.md) for details on how connections and multiplexing work +- [Peer](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/peer.md) for details on peer ID, handshakes, and peer exchange +- [Node](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/node.md) for details about different types of nodes and how they should work +- [Pex](https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/pex/pex.md) for details on peer discovery and exchange +- [Config](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/config.md) for details on some config option diff --git a/p2p/base_reactor.go b/p2p/base_reactor.go index be65d2f14d8..3bccabd64f3 100644 --- a/p2p/base_reactor.go +++ b/p2p/base_reactor.go @@ -5,23 +5,40 @@ import ( "github.com/tendermint/tendermint/p2p/conn" ) +// Reactor is responsible for handling incoming messages on one or more +// Channel. Switch calls GetChannels when reactor is added to it. When a new +// peer joins our node, InitPeer and AddPeer are called. RemovePeer is called +// when the peer is stopped. Receive is called when a message is received on a +// channel associated with this reactor. +// +// Peer#Send or Peer#TrySend should be used to send the message to a peer. type Reactor interface { cmn.Service // Start, Stop // SetSwitch allows setting a switch. SetSwitch(*Switch) - // GetChannels returns the list of channel descriptors. + // GetChannels returns the list of MConnection.ChannelDescriptor. Make sure + // that each ID is unique across all the reactors added to the switch. GetChannels() []*conn.ChannelDescriptor - // AddPeer is called by the switch when a new peer is added. + // InitPeer is called by the switch before the peer is started. Use it to + // initialize data for the peer (e.g. peer state). + // + // NOTE: The switch won't call AddPeer nor RemovePeer if it fails to start + // the peer. Do not store any data associated with the peer in the reactor + // itself unless you don't want to have a state, which is never cleaned up. + InitPeer(peer Peer) Peer + + // AddPeer is called by the switch after the peer is added and successfully + // started. Use it to start goroutines communicating with the peer. AddPeer(peer Peer) // RemovePeer is called by the switch when the peer is stopped (due to error // or other reason). RemovePeer(peer Peer, reason interface{}) - // Receive is called when msgBytes is received from peer. + // Receive is called by the switch when msgBytes is received from the peer. // // NOTE reactor can not keep msgBytes around after Receive completes without // copying. @@ -51,3 +68,4 @@ func (*BaseReactor) GetChannels() []*conn.ChannelDescriptor { return nil func (*BaseReactor) AddPeer(peer Peer) {} func (*BaseReactor) RemovePeer(peer Peer, reason interface{}) {} func (*BaseReactor) Receive(chID byte, peer Peer, msgBytes []byte) {} +func (*BaseReactor) InitPeer(peer Peer) Peer { return peer } diff --git a/p2p/wire.go b/p2p/codec.go similarity index 81% rename from p2p/wire.go rename to p2p/codec.go index 40176e3af8b..191e3c52329 100644 --- a/p2p/wire.go +++ b/p2p/codec.go @@ -1,7 +1,7 @@ package p2p import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/p2p/conn/wire.go b/p2p/conn/codec.go similarity index 83% rename from p2p/conn/wire.go rename to p2p/conn/codec.go index 4bd778c7430..5231a6ca1b5 100644 --- a/p2p/conn/wire.go +++ b/p2p/conn/codec.go @@ -1,7 +1,7 @@ package conn import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index 9207349146f..a206af54238 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -85,23 +85,26 @@ type MConnection struct { errored uint32 config MConnConfig - // Closing quitSendRoutine will cause - // doneSendRoutine to close. + // Closing quitSendRoutine will cause the sendRoutine to eventually quit. + // doneSendRoutine is closed when the sendRoutine actually quits. quitSendRoutine chan struct{} doneSendRoutine chan struct{} + // Closing quitRecvRouting will cause the recvRouting to eventually quit. + quitRecvRoutine chan struct{} + // used to ensure FlushStop and OnStop // are safe to call concurrently. stopMtx sync.Mutex flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled. - pingTimer *cmn.RepeatTimer // send pings periodically + pingTimer *time.Ticker // send pings periodically // close conn if pong is not received in pongTimeout pongTimer *time.Timer pongTimeoutCh chan bool // true - timeout, false - peer sent pong - chStatsTimer *cmn.RepeatTimer // update channel stats periodically + chStatsTimer *time.Ticker // update channel stats periodically created time.Time // time of creation @@ -200,29 +203,36 @@ func (c *MConnection) OnStart() error { if err := c.BaseService.OnStart(); err != nil { return err } - c.quitSendRoutine = make(chan struct{}) - c.doneSendRoutine = make(chan struct{}) c.flushTimer = cmn.NewThrottleTimer("flush", c.config.FlushThrottle) - c.pingTimer = cmn.NewRepeatTimer("ping", c.config.PingInterval) + c.pingTimer = time.NewTicker(c.config.PingInterval) c.pongTimeoutCh = make(chan bool, 1) - c.chStatsTimer = cmn.NewRepeatTimer("chStats", updateStats) + c.chStatsTimer = time.NewTicker(updateStats) + c.quitSendRoutine = make(chan struct{}) + c.doneSendRoutine = make(chan struct{}) + c.quitRecvRoutine = make(chan struct{}) go c.sendRoutine() go c.recvRoutine() return nil } -// FlushStop replicates the logic of OnStop. -// It additionally ensures that all successful -// .Send() calls will get flushed before closing -// the connection. -func (c *MConnection) FlushStop() { +// stopServices stops the BaseService and timers and closes the quitSendRoutine. +// if the quitSendRoutine was already closed, it returns true, otherwise it returns false. +// It uses the stopMtx to ensure only one of FlushStop and OnStop can do this at a time. +func (c *MConnection) stopServices() (alreadyStopped bool) { c.stopMtx.Lock() defer c.stopMtx.Unlock() select { case <-c.quitSendRoutine: - // already quit via OnStop - return + // already quit + return true + default: + } + + select { + case <-c.quitRecvRoutine: + // already quit + return true default: } @@ -230,25 +240,40 @@ func (c *MConnection) FlushStop() { c.flushTimer.Stop() c.pingTimer.Stop() c.chStatsTimer.Stop() - if c.quitSendRoutine != nil { - close(c.quitSendRoutine) + + // inform the recvRouting that we are shutting down + close(c.quitRecvRoutine) + close(c.quitSendRoutine) + return false +} + +// FlushStop replicates the logic of OnStop. +// It additionally ensures that all successful +// .Send() calls will get flushed before closing +// the connection. +func (c *MConnection) FlushStop() { + if c.stopServices() { + return + } + + // this block is unique to FlushStop + { // wait until the sendRoutine exits // so we dont race on calling sendSomePacketMsgs <-c.doneSendRoutine - } - // Send and flush all pending msgs. - // By now, IsRunning == false, - // so any concurrent attempts to send will fail. - // Since sendRoutine has exited, we can call this - // safely - eof := c.sendSomePacketMsgs() - for !eof { - eof = c.sendSomePacketMsgs() + // Send and flush all pending msgs. + // Since sendRoutine has exited, we can call this + // safely + eof := c.sendSomePacketMsgs() + for !eof { + eof = c.sendSomePacketMsgs() + } + c.flush() + + // Now we can close the connection } - c.flush() - // Now we can close the connection c.conn.Close() // nolint: errcheck // We can't close pong safely here because @@ -261,21 +286,10 @@ func (c *MConnection) FlushStop() { // OnStop implements BaseService func (c *MConnection) OnStop() { - c.stopMtx.Lock() - defer c.stopMtx.Unlock() - - select { - case <-c.quitSendRoutine: - // already quit via FlushStop + if c.stopServices() { return - default: } - c.BaseService.OnStop() - c.flushTimer.Stop() - c.pingTimer.Stop() - c.chStatsTimer.Stop() - close(c.quitSendRoutine) c.conn.Close() // nolint: errcheck // We can't close pong safely here because @@ -398,11 +412,11 @@ FOR_LOOP: // NOTE: flushTimer.Set() must be called every time // something is written to .bufConnWriter. c.flush() - case <-c.chStatsTimer.Chan(): + case <-c.chStatsTimer.C: for _, channel := range c.channels { channel.updateStats() } - case <-c.pingTimer.Chan(): + case <-c.pingTimer.C: c.Logger.Debug("Send Ping") _n, err = cdc.MarshalBinaryLengthPrefixedWriter(c.bufConnWriter, PacketPing{}) if err != nil { @@ -433,7 +447,6 @@ FOR_LOOP: c.sendMonitor.Update(int(_n)) c.flush() case <-c.quitSendRoutine: - close(c.doneSendRoutine) break FOR_LOOP case <-c.send: // Send some PacketMsgs @@ -459,6 +472,7 @@ FOR_LOOP: // Cleanup c.stopPongTimer() + close(c.doneSendRoutine) } // Returns true if messages from channels were exhausted. @@ -547,9 +561,22 @@ FOR_LOOP: var err error _n, err = cdc.UnmarshalBinaryLengthPrefixedReader(c.bufConnReader, &packet, int64(c._maxPacketMsgSize)) c.recvMonitor.Update(int(_n)) + if err != nil { + // stopServices was invoked and we are shutting down + // receiving is excpected to fail since we will close the connection + select { + case <-c.quitRecvRoutine: + break FOR_LOOP + default: + } + if c.IsRunning() { - c.Logger.Error("Connection failed @ recvRoutine (reading byte)", "conn", c, "err", err) + if err == io.EOF { + c.Logger.Info("Connection is closed @ recvRoutine (likely by the other side)", "conn", c) + } else { + c.Logger.Error("Connection failed @ recvRoutine (reading byte)", "conn", c, "err", err) + } c.stopForError(err) } break FOR_LOOP @@ -704,7 +731,7 @@ type Channel struct { func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel { desc = desc.FillDefaults() if desc.Priority <= 0 { - cmn.PanicSanity("Channel default priority must be a positive integer") + panic("Channel default priority must be a positive integer") } return &Channel{ conn: conn, diff --git a/p2p/conn/connection_test.go b/p2p/conn/connection_test.go index a757f07a627..91e3e209942 100644 --- a/p2p/conn/connection_test.go +++ b/p2p/conn/connection_test.go @@ -30,7 +30,7 @@ func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msg cfg := DefaultMConnConfig() cfg.PingInterval = 90 * time.Millisecond cfg.PongTimeout = 45 * time.Millisecond - chDescs := []*ChannelDescriptor{&ChannelDescriptor{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} + chDescs := []*ChannelDescriptor{{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} c := NewMConnectionWithConfig(conn, chDescs, onReceive, onError, cfg) c.SetLogger(log.TestingLogger()) return c @@ -57,7 +57,8 @@ func TestMConnectionSendFlushStop(t *testing.T) { msgB := make([]byte, aminoMsgLength) _, err := server.Read(msgB) if err != nil { - t.Fatal(err) + t.Error(err) + return } errCh <- err }() @@ -223,7 +224,10 @@ func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { serverGotPing := make(chan struct{}) go func() { // read ping (one byte) - var packet, err = Packet(nil), error(nil) + var ( + packet Packet + err error + ) _, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &packet, maxPingPongPacketSize) require.Nil(t, err) serverGotPing <- struct{}{} @@ -492,8 +496,7 @@ func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { defer mconnServer.Stop() // send msg with unknown msg type - err := error(nil) - err = amino.EncodeUvarint(mconnClient.conn, 4) + err := amino.EncodeUvarint(mconnClient.conn, 4) assert.Nil(t, err) _, err = mconnClient.conn.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF}) assert.Nil(t, err) diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index 7cc4aa7d940..a4489f47536 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -2,12 +2,14 @@ package conn import ( "bytes" + "crypto/cipher" crand "crypto/rand" "crypto/sha256" "crypto/subtle" "encoding/binary" "errors" "io" + "math" "net" "sync" "time" @@ -16,6 +18,7 @@ import ( "golang.org/x/crypto/curve25519" "golang.org/x/crypto/nacl/box" + pool "github.com/libp2p/go-buffer-pool" "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" "golang.org/x/crypto/hkdf" @@ -44,11 +47,13 @@ var ( // Otherwise they are vulnerable to MITM. // (TODO(ismail): see also https://github.com/tendermint/tendermint/issues/3010) type SecretConnection struct { + // immutable - recvSecret *[aeadKeySize]byte - sendSecret *[aeadKeySize]byte - remPubKey crypto.PubKey - conn io.ReadWriteCloser + recvAead cipher.AEAD + sendAead cipher.AEAD + + remPubKey crypto.PubKey + conn io.ReadWriteCloser // net.Conn must be thread safe: // https://golang.org/pkg/net/#Conn. @@ -100,14 +105,22 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* // generate the secret used for receiving, sending, challenge via hkdf-sha2 on dhSecret recvSecret, sendSecret, challenge := deriveSecretAndChallenge(dhSecret, locIsLeast) + sendAead, err := chacha20poly1305.New(sendSecret[:]) + if err != nil { + return nil, errors.New("Invalid send SecretConnection Key") + } + recvAead, err := chacha20poly1305.New(recvSecret[:]) + if err != nil { + return nil, errors.New("Invalid receive SecretConnection Key") + } // Construct SecretConnection. sc := &SecretConnection{ conn: conn, recvBuffer: nil, recvNonce: new([aeadNonceSize]byte), sendNonce: new([aeadNonceSize]byte), - recvSecret: recvSecret, - sendSecret: sendSecret, + recvAead: recvAead, + sendAead: sendAead, } // Sign the challenge bytes for authentication. @@ -141,34 +154,39 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) { defer sc.sendMtx.Unlock() for 0 < len(data) { - var frame = make([]byte, totalFrameSize) - var chunk []byte - if dataMaxSize < len(data) { - chunk = data[:dataMaxSize] - data = data[dataMaxSize:] - } else { - chunk = data - data = nil - } - chunkLength := len(chunk) - binary.LittleEndian.PutUint32(frame, uint32(chunkLength)) - copy(frame[dataLenSize:], chunk) - - aead, err := chacha20poly1305.New(sc.sendSecret[:]) - if err != nil { - return n, errors.New("Invalid SecretConnection Key") - } - // encrypt the frame - var sealedFrame = make([]byte, aeadSizeOverhead+totalFrameSize) - aead.Seal(sealedFrame[:0], sc.sendNonce[:], frame, nil) - incrNonce(sc.sendNonce) - // end encryption - - _, err = sc.conn.Write(sealedFrame) - if err != nil { + if err := func() error { + var sealedFrame = pool.Get(aeadSizeOverhead + totalFrameSize) + var frame = pool.Get(totalFrameSize) + defer func() { + pool.Put(sealedFrame) + pool.Put(frame) + }() + var chunk []byte + if dataMaxSize < len(data) { + chunk = data[:dataMaxSize] + data = data[dataMaxSize:] + } else { + chunk = data + data = nil + } + chunkLength := len(chunk) + binary.LittleEndian.PutUint32(frame, uint32(chunkLength)) + copy(frame[dataLenSize:], chunk) + + // encrypt the frame + sc.sendAead.Seal(sealedFrame[:0], sc.sendNonce[:], frame, nil) + incrNonce(sc.sendNonce) + // end encryption + + _, err = sc.conn.Write(sealedFrame) + if err != nil { + return err + } + n += len(chunk) + return nil + }(); err != nil { return n, err } - n += len(chunk) } return } @@ -185,22 +203,19 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) { return } - sealedFrame := make([]byte, totalFrameSize+aeadSizeOverhead) + // read off the conn + var sealedFrame = pool.Get(aeadSizeOverhead + totalFrameSize) + defer pool.Put(sealedFrame) _, err = io.ReadFull(sc.conn, sealedFrame) if err != nil { return } - // decrypt the frame - aead, err := chacha20poly1305.New(sc.recvSecret[:]) - if err != nil { - return n, errors.New("Invalid SecretConnection Key") - } - // decrypt the frame. // reads and updates the sc.recvNonce - var frame = make([]byte, totalFrameSize) - _, err = aead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil) + var frame = pool.Get(totalFrameSize) + defer pool.Put(frame) + _, err = sc.recvAead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil) if err != nil { return n, errors.New("Failed to decrypt SecretConnection") } @@ -214,9 +229,11 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) { return 0, errors.New("chunkLength is greater than dataMaxSize") } var chunk = frame[dataLenSize : dataLenSize+chunkLength] - n = copy(data, chunk) - sc.recvBuffer = chunk[n:] + if n < len(chunk) { + sc.recvBuffer = make([]byte, len(chunk)-n) + copy(sc.recvBuffer, chunk[n:]) + } return } @@ -357,7 +374,7 @@ func deriveSecretAndChallenge(dhSecret *[32]byte, locIsLeast bool) (recvSecret, return } -// computeDHSecret computes a shared secret Diffie-Hellman secret +// computeDHSecret computes a Diffie-Hellman shared secret key // from our own local private key and the other's public key. // // It returns an error if the computed shared secret is all zeroes. @@ -371,7 +388,6 @@ func computeDHSecret(remPubKey, locPrivKey *[32]byte) (shrKey *[32]byte, err err if subtle.ConstantTimeCompare(shrKey[:], zero[:]) == 1 { return nil, ErrSharedSecretIsZero } - return } @@ -439,6 +455,11 @@ func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKey, signature [] // (little-endian in nonce[4:]). func incrNonce(nonce *[aeadNonceSize]byte) { counter := binary.LittleEndian.Uint64(nonce[4:]) + if counter == math.MaxUint64 { + // Terminates the session and makes sure the nonce would not re-used. + // See https://github.com/tendermint/tendermint/issues/3531 + panic("can't increase nonce without overflow") + } counter++ binary.LittleEndian.PutUint64(nonce[4:], counter) } diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 12098c47e60..9ab9695a363 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -192,7 +192,8 @@ func writeLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, txt string, n in for i := 0; i < n; i++ { _, err := conn.Write([]byte(txt)) if err != nil { - t.Fatalf("Failed to write to fooSecConn: %v", err) + t.Errorf("Failed to write to fooSecConn: %v", err) + return } } } @@ -255,6 +256,10 @@ func TestSecretConnectionReadWrite(t *testing.T) { for { n, err := nodeSecretConn.Read(readBuffer) if err == io.EOF { + if err := nodeConn.PipeReader.Close(); err != nil { + t.Error(err) + return nil, err, true + } return nil, nil, false } else if err != nil { t.Errorf("Failed to read from nodeSecretConn: %v", err) @@ -262,11 +267,6 @@ func TestSecretConnectionReadWrite(t *testing.T) { } *nodeReads = append(*nodeReads, string(readBuffer[:n])) } - if err := nodeConn.PipeReader.Close(); err != nil { - t.Error(err) - return nil, err, true - } - return nil, nil, false }, ) assert.True(t, ok, "Unexpected task abortion") @@ -384,10 +384,23 @@ func createGoldenTestVectors(t *testing.T) string { return data } -func BenchmarkSecretConnection(b *testing.B) { +func BenchmarkWriteSecretConnection(b *testing.B) { b.StopTimer() + b.ReportAllocs() fooSecConn, barSecConn := makeSecretConnPair(b) - fooWriteText := cmn.RandStr(dataMaxSize) + randomMsgSizes := []int{ + dataMaxSize / 10, + dataMaxSize / 3, + dataMaxSize / 2, + dataMaxSize, + dataMaxSize * 3 / 2, + dataMaxSize * 2, + dataMaxSize * 7 / 2, + } + fooWriteBytes := make([][]byte, 0, len(randomMsgSizes)) + for _, size := range randomMsgSizes { + fooWriteBytes = append(fooWriteBytes, cmn.RandBytes(size)) + } // Consume reads from bar's reader go func() { readBuffer := make([]byte, dataMaxSize) @@ -396,16 +409,19 @@ func BenchmarkSecretConnection(b *testing.B) { if err == io.EOF { return } else if err != nil { - b.Fatalf("Failed to read from barSecConn: %v", err) + b.Errorf("Failed to read from barSecConn: %v", err) + return } } }() b.StartTimer() for i := 0; i < b.N; i++ { - _, err := fooSecConn.Write([]byte(fooWriteText)) + idx := cmn.RandIntn(len(fooWriteBytes)) + _, err := fooSecConn.Write(fooWriteBytes[idx]) if err != nil { - b.Fatalf("Failed to write to fooSecConn: %v", err) + b.Errorf("Failed to write to fooSecConn: %v", err) + return } } b.StopTimer() @@ -416,11 +432,44 @@ func BenchmarkSecretConnection(b *testing.B) { //barSecConn.Close() race condition } -func fingerprint(bz []byte) []byte { - const fbsize = 40 - if len(bz) < fbsize { - return bz - } else { - return bz[:fbsize] +func BenchmarkReadSecretConnection(b *testing.B) { + b.StopTimer() + b.ReportAllocs() + fooSecConn, barSecConn := makeSecretConnPair(b) + randomMsgSizes := []int{ + dataMaxSize / 10, + dataMaxSize / 3, + dataMaxSize / 2, + dataMaxSize, + dataMaxSize * 3 / 2, + dataMaxSize * 2, + dataMaxSize * 7 / 2, } + fooWriteBytes := make([][]byte, 0, len(randomMsgSizes)) + for _, size := range randomMsgSizes { + fooWriteBytes = append(fooWriteBytes, cmn.RandBytes(size)) + } + go func() { + for i := 0; i < b.N; i++ { + idx := cmn.RandIntn(len(fooWriteBytes)) + _, err := fooSecConn.Write(fooWriteBytes[idx]) + if err != nil { + b.Errorf("Failed to write to fooSecConn: %v, %v,%v", err, i, b.N) + return + } + } + }() + + b.StartTimer() + for i := 0; i < b.N; i++ { + readBuffer := make([]byte, dataMaxSize) + _, err := barSecConn.Read(readBuffer) + + if err == io.EOF { + return + } else if err != nil { + b.Fatalf("Failed to read from barSecConn: %v", err) + } + } + b.StopTimer() } diff --git a/p2p/dummy/peer.go b/p2p/dummy/peer.go deleted file mode 100644 index d002a9d3c18..00000000000 --- a/p2p/dummy/peer.go +++ /dev/null @@ -1,100 +0,0 @@ -package dummy - -import ( - "net" - - cmn "github.com/tendermint/tendermint/libs/common" - p2p "github.com/tendermint/tendermint/p2p" - tmconn "github.com/tendermint/tendermint/p2p/conn" -) - -type peer struct { - cmn.BaseService - kv map[string]interface{} -} - -var _ p2p.Peer = (*peer)(nil) - -// NewPeer creates new dummy peer. -func NewPeer() *peer { - p := &peer{ - kv: make(map[string]interface{}), - } - p.BaseService = *cmn.NewBaseService(nil, "peer", p) - - return p -} - -// FlushStop just calls Stop. -func (p *peer) FlushStop() { - p.Stop() -} - -// ID always returns dummy. -func (p *peer) ID() p2p.ID { - return p2p.ID("dummy") -} - -// IsOutbound always returns false. -func (p *peer) IsOutbound() bool { - return false -} - -// IsPersistent always returns false. -func (p *peer) IsPersistent() bool { - return false -} - -// NodeInfo always returns empty node info. -func (p *peer) NodeInfo() p2p.NodeInfo { - return p2p.DefaultNodeInfo{} -} - -// RemoteIP always returns localhost. -func (p *peer) RemoteIP() net.IP { - return net.ParseIP("127.0.0.1") -} - -// Addr always returns tcp://localhost:8800. -func (p *peer) RemoteAddr() net.Addr { - return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8800} -} - -// CloseConn always returns nil. -func (p *peer) CloseConn() error { - return nil -} - -// Status always returns empry connection status. -func (p *peer) Status() tmconn.ConnectionStatus { - return tmconn.ConnectionStatus{} -} - -// Send does not do anything and just returns true. -func (p *peer) Send(byte, []byte) bool { - return true -} - -// TrySend does not do anything and just returns true. -func (p *peer) TrySend(byte, []byte) bool { - return true -} - -// Set records value under key specified in the map. -func (p *peer) Set(key string, value interface{}) { - p.kv[key] = value -} - -// Get returns a value associated with the key. Nil is returned if no value -// found. -func (p *peer) Get(key string) interface{} { - if value, ok := p.kv[key]; ok { - return value - } - return nil -} - -// OriginalAddr always returns nil. -func (p *peer) SocketAddr() *p2p.NetAddress { - return nil -} diff --git a/p2p/errors.go b/p2p/errors.go index 706150945b8..3650a7a0a8e 100644 --- a/p2p/errors.go +++ b/p2p/errors.go @@ -103,7 +103,7 @@ type ErrSwitchDuplicatePeerID struct { } func (e ErrSwitchDuplicatePeerID) Error() string { - return fmt.Sprintf("Duplicate peer ID %v", e.ID) + return fmt.Sprintf("duplicate peer ID %v", e.ID) } // ErrSwitchDuplicatePeerIP to be raised whena a peer is connecting with a known @@ -113,7 +113,7 @@ type ErrSwitchDuplicatePeerIP struct { } func (e ErrSwitchDuplicatePeerIP) Error() string { - return fmt.Sprintf("Duplicate peer IP %v", e.IP.String()) + return fmt.Sprintf("duplicate peer IP %v", e.IP.String()) } // ErrSwitchConnectToSelf to be raised when trying to connect to itself. @@ -122,7 +122,7 @@ type ErrSwitchConnectToSelf struct { } func (e ErrSwitchConnectToSelf) Error() string { - return fmt.Sprintf("Connect to self: %v", e.Addr) + return fmt.Sprintf("connect to self: %v", e.Addr) } type ErrSwitchAuthenticationFailure struct { @@ -132,7 +132,7 @@ type ErrSwitchAuthenticationFailure struct { func (e ErrSwitchAuthenticationFailure) Error() string { return fmt.Sprintf( - "Failed to authenticate peer. Dialed %v, but got peer with ID %s", + "failed to authenticate peer. Dialed %v, but got peer with ID %s", e.Dialed, e.Got, ) @@ -152,7 +152,7 @@ type ErrNetAddressNoID struct { } func (e ErrNetAddressNoID) Error() string { - return fmt.Sprintf("Address (%s) does not contain ID", e.Addr) + return fmt.Sprintf("address (%s) does not contain ID", e.Addr) } type ErrNetAddressInvalid struct { @@ -161,7 +161,7 @@ type ErrNetAddressInvalid struct { } func (e ErrNetAddressInvalid) Error() string { - return fmt.Sprintf("Invalid address (%s): %v", e.Addr, e.Err) + return fmt.Sprintf("invalid address (%s): %v", e.Addr, e.Err) } type ErrNetAddressLookup struct { @@ -170,5 +170,15 @@ type ErrNetAddressLookup struct { } func (e ErrNetAddressLookup) Error() string { - return fmt.Sprintf("Error looking up host (%s): %v", e.Addr, e.Err) + return fmt.Sprintf("error looking up host (%s): %v", e.Addr, e.Err) +} + +// ErrCurrentlyDialingOrExistingAddress indicates that we're currently +// dialing this address or it belongs to an existing peer. +type ErrCurrentlyDialingOrExistingAddress struct { + Addr string +} + +func (e ErrCurrentlyDialingOrExistingAddress) Error() string { + return fmt.Sprintf("connection with %s has been established or dialed", e.Addr) } diff --git a/p2p/metrics.go b/p2p/metrics.go index 9649ec99542..675dd9c7c76 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -7,7 +7,11 @@ import ( stdprometheus "github.com/prometheus/client_golang/prometheus" ) -const MetricsSubsystem = "p2p" +const ( + // MetricsSubsystem is a subsystem shared by all metrics exposed by this + // package. + MetricsSubsystem = "p2p" +) // Metrics contains metrics exposed by this package. type Metrics struct { @@ -24,8 +28,10 @@ type Metrics struct { } // PrometheusMetrics returns Metrics build using Prometheus client library. +// Optionally, labels can be provided along with their values ("foo", +// "fooValue"). func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { - var labels []string + labels := []string{} for i := 0; i < len(labelsAndValues); i += 2 { labels = append(labels, labelsAndValues[i]) } @@ -41,13 +47,13 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Subsystem: MetricsSubsystem, Name: "peer_receive_bytes_total", Help: "Number of bytes received from a given peer.", - }, append(labels, "peer_id")).With(labelsAndValues...), + }, append(labels, "peer_id", "chID")).With(labelsAndValues...), PeerSendBytesTotal: prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, Name: "peer_send_bytes_total", Help: "Number of bytes sent to a given peer.", - }, append(labels, "peer_id")).With(labelsAndValues...), + }, append(labels, "peer_id", "chID")).With(labelsAndValues...), PeerPendingSendBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, diff --git a/p2p/mock/reactor.go b/p2p/mock/reactor.go new file mode 100644 index 00000000000..cfce12bd1aa --- /dev/null +++ b/p2p/mock/reactor.go @@ -0,0 +1,23 @@ +package mock + +import ( + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/conn" +) + +type Reactor struct { + p2p.BaseReactor +} + +func NewReactor() *Reactor { + r := &Reactor{} + r.BaseReactor = *p2p.NewBaseReactor("Reactor", r) + r.SetLogger(log.TestingLogger()) + return r +} + +func (r *Reactor) GetChannels() []*conn.ChannelDescriptor { return []*conn.ChannelDescriptor{} } +func (r *Reactor) AddPeer(peer p2p.Peer) {} +func (r *Reactor) RemovePeer(peer p2p.Peer, reason interface{}) {} +func (r *Reactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {} diff --git a/p2p/netaddress.go b/p2p/netaddress.go index 5534ded9842..f39a60543a0 100644 --- a/p2p/netaddress.go +++ b/p2p/netaddress.go @@ -13,9 +13,7 @@ import ( "strings" "time" - "errors" - - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/pkg/errors" ) // NetAddress defines information about a peer on the network @@ -42,19 +40,24 @@ func IDAddressString(id ID, protocolHostPort string) string { // NewNetAddress returns a new NetAddress using the provided TCP // address. When testing, other net.Addr (except TCP) will result in // using 0.0.0.0:0. When normal run, other net.Addr (except TCP) will -// panic. +// panic. Panics if ID is invalid. // TODO: socks proxies? func NewNetAddress(id ID, addr net.Addr) *NetAddress { tcpAddr, ok := addr.(*net.TCPAddr) if !ok { if flag.Lookup("test.v") == nil { // normal run - cmn.PanicSanity(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr)) + panic(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr)) } else { // in testing netAddr := NewNetAddressIPPort(net.IP("0.0.0.0"), 0) netAddr.ID = id return netAddr } } + + if err := validateID(id); err != nil { + panic(fmt.Sprintf("Invalid ID %v: %v (addr: %v)", id, err, addr)) + } + ip := tcpAddr.IP port := uint16(tcpAddr.Port) na := NewNetAddressIPPort(ip, port) @@ -67,36 +70,20 @@ func NewNetAddress(id ID, addr net.Addr) *NetAddress { // Also resolves the host if host is not an IP. // Errors are of type ErrNetAddressXxx where Xxx is in (NoID, Invalid, Lookup) func NewNetAddressString(addr string) (*NetAddress, error) { - spl := strings.Split(addr, "@") - if len(spl) < 2 { - return nil, ErrNetAddressNoID{addr} - } - return NewNetAddressStringWithOptionalID(addr) -} - -// NewNetAddressStringWithOptionalID returns a new NetAddress using the -// provided address in the form of "ID@IP:Port", where the ID is optional. -// Also resolves the host if host is not an IP. -func NewNetAddressStringWithOptionalID(addr string) (*NetAddress, error) { addrWithoutProtocol := removeProtocolIfDefined(addr) - - var id ID spl := strings.Split(addrWithoutProtocol, "@") - if len(spl) == 2 { - idStr := spl[0] - idBytes, err := hex.DecodeString(idStr) - if err != nil { - return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} - } - if len(idBytes) != IDByteLength { - return nil, ErrNetAddressInvalid{ - addrWithoutProtocol, - fmt.Errorf("invalid hex length - got %d, expected %d", len(idBytes), IDByteLength)} - } + if len(spl) != 2 { + return nil, ErrNetAddressNoID{addr} + } - id, addrWithoutProtocol = ID(idStr), spl[1] + // get ID + if err := validateID(ID(spl[0])); err != nil { + return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} } + var id ID + id, addrWithoutProtocol = ID(spl[0]), spl[1] + // get host and port host, portStr, err := net.SplitHostPort(addrWithoutProtocol) if err != nil { return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} @@ -218,22 +205,28 @@ func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) { // Routable returns true if the address is routable. func (na *NetAddress) Routable() bool { + if err := na.Valid(); err != nil { + return false + } // TODO(oga) bitcoind doesn't include RFC3849 here, but should we? - return na.Valid() && !(na.RFC1918() || na.RFC3927() || na.RFC4862() || + return !(na.RFC1918() || na.RFC3927() || na.RFC4862() || na.RFC4193() || na.RFC4843() || na.Local()) } // For IPv4 these are either a 0 or all bits set address. For IPv6 a zero // address or one that matches the RFC3849 documentation address format. -func (na *NetAddress) Valid() bool { - if string(na.ID) != "" { - data, err := hex.DecodeString(string(na.ID)) - if err != nil || len(data) != IDByteLength { - return false - } +func (na *NetAddress) Valid() error { + if err := validateID(na.ID); err != nil { + return errors.Wrap(err, "invalid ID") + } + + if na.IP == nil { + return errors.New("no IP") } - return na.IP != nil && !(na.IP.IsUnspecified() || na.RFC3849() || - na.IP.Equal(net.IPv4bcast)) + if na.IP.IsUnspecified() || na.RFC3849() || na.IP.Equal(net.IPv4bcast) { + return errors.New("invalid IP") + } + return nil } // HasID returns true if the address has an ID. @@ -340,3 +333,17 @@ func removeProtocolIfDefined(addr string) string { return addr } + +func validateID(id ID) error { + if len(id) == 0 { + return errors.New("no ID") + } + idBytes, err := hex.DecodeString(string(id)) + if err != nil { + return err + } + if len(idBytes) != IDByteLength { + return fmt.Errorf("invalid hex length - got %d, expected %d", len(idBytes), IDByteLength) + } + return nil +} diff --git a/p2p/netaddress_test.go b/p2p/netaddress_test.go index e7b184a76de..e7d82cd7748 100644 --- a/p2p/netaddress_test.go +++ b/p2p/netaddress_test.go @@ -11,26 +11,36 @@ import ( func TestNewNetAddress(t *testing.T) { tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") require.Nil(t, err) - addr := NewNetAddress("", tcpAddr) - assert.Equal(t, "127.0.0.1:8080", addr.String()) + assert.Panics(t, func() { + NewNetAddress("", tcpAddr) + }) + + addr := NewNetAddress("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", tcpAddr) + assert.Equal(t, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", addr.String()) assert.NotPanics(t, func() { NewNetAddress("", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000}) }, "Calling NewNetAddress with UDPAddr should not panic in testing") } -func TestNewNetAddressStringWithOptionalID(t *testing.T) { +func TestNewNetAddressString(t *testing.T) { testCases := []struct { name string addr string expected string correct bool }{ - {"no node id, no protocol", "127.0.0.1:8080", "127.0.0.1:8080", true}, - {"no node id, tcp input", "tcp://127.0.0.1:8080", "127.0.0.1:8080", true}, - {"no node id, udp input", "udp://127.0.0.1:8080", "127.0.0.1:8080", true}, - {"malformed udp input", "udp//127.0.0.1:8080", "", false}, + {"no node id and no protocol", "127.0.0.1:8080", "", false}, + {"no node id w/ tcp input", "tcp://127.0.0.1:8080", "", false}, + {"no node id w/ udp input", "udp://127.0.0.1:8080", "", false}, + + {"no protocol", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, + {"tcp input", "tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, + {"udp input", "udp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, + {"malformed tcp input", "tcp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, + {"malformed udp input", "udp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, + // {"127.0.0:8080", false}, {"invalid host", "notahost", "", false}, {"invalid port", "127.0.0.1:notapath", "", false}, @@ -41,14 +51,13 @@ func TestNewNetAddressStringWithOptionalID(t *testing.T) { {"too short nodeId", "deadbeef@127.0.0.1:8080", "", false}, {"too short, not hex nodeId", "this-isnot-hex@127.0.0.1:8080", "", false}, {"not hex nodeId", "xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, - {"correct nodeId", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, {"too short nodeId w/tcp", "tcp://deadbeef@127.0.0.1:8080", "", false}, {"too short notHex nodeId w/tcp", "tcp://this-isnot-hex@127.0.0.1:8080", "", false}, {"notHex nodeId w/tcp", "tcp://xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, {"correct nodeId w/tcp", "tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, - {"no node id when expected", "tcp://@127.0.0.1:8080", "", false}, + {"no node id", "tcp://@127.0.0.1:8080", "", false}, {"no node id or IP", "tcp://@", "", false}, {"tcp no host, w/ port", "tcp://:26656", "", false}, {"empty", "", "", false}, @@ -59,7 +68,7 @@ func TestNewNetAddressStringWithOptionalID(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - addr, err := NewNetAddressStringWithOptionalID(tc.addr) + addr, err := NewNetAddressString(tc.addr) if tc.correct { if assert.Nil(t, err, tc.addr) { assert.Equal(t, tc.expected, addr.String()) @@ -71,28 +80,6 @@ func TestNewNetAddressStringWithOptionalID(t *testing.T) { } } -func TestNewNetAddressString(t *testing.T) { - testCases := []struct { - addr string - expected string - correct bool - }{ - {"127.0.0.1:8080", "127.0.0.1:8080", false}, - {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, - } - - for _, tc := range testCases { - addr, err := NewNetAddressString(tc.addr) - if tc.correct { - if assert.Nil(t, err, tc.addr) { - assert.Equal(t, tc.expected, addr.String()) - } - } else { - assert.NotNil(t, err, tc.addr) - } - } -} - func TestNewNetAddressStrings(t *testing.T) { addrs, errs := NewNetAddressStrings([]string{ "127.0.0.1:8080", @@ -115,15 +102,20 @@ func TestNetAddressProperties(t *testing.T) { local bool routable bool }{ - {"127.0.0.1:8080", true, true, false}, - {"ya.ru:80", true, false, true}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true, true, false}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@ya.ru:80", true, false, true}, } for _, tc := range testCases { - addr, err := NewNetAddressStringWithOptionalID(tc.addr) + addr, err := NewNetAddressString(tc.addr) require.Nil(t, err) - assert.Equal(t, tc.valid, addr.Valid()) + err = addr.Valid() + if tc.valid { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } assert.Equal(t, tc.local, addr.Local()) assert.Equal(t, tc.routable, addr.Routable()) } @@ -136,15 +128,15 @@ func TestNetAddressReachabilityTo(t *testing.T) { other string reachability int }{ - {"127.0.0.1:8080", "127.0.0.1:8081", 0}, - {"ya.ru:80", "127.0.0.1:8080", 1}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8081", 0}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@ya.ru:80", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", 1}, } for _, tc := range testCases { - addr, err := NewNetAddressStringWithOptionalID(tc.addr) + addr, err := NewNetAddressString(tc.addr) require.Nil(t, err) - other, err := NewNetAddressStringWithOptionalID(tc.other) + other, err := NewNetAddressString(tc.other) require.Nil(t, err) assert.Equal(t, tc.reachability, addr.ReachabilityTo(other)) diff --git a/p2p/node_info.go b/p2p/node_info.go index 566f8a821b0..8195471ed71 100644 --- a/p2p/node_info.go +++ b/p2p/node_info.go @@ -9,7 +9,7 @@ import ( ) const ( - maxNodeInfoSize = 10240 // 10Kb + maxNodeInfoSize = 10240 // 10KB maxNumChannels = 16 // plenty of room for upgrades, for now ) diff --git a/p2p/node_info_test.go b/p2p/node_info_test.go index 19567d2bf99..9ed80b28b95 100644 --- a/p2p/node_info_test.go +++ b/p2p/node_info_test.go @@ -19,7 +19,7 @@ func TestNodeInfoValidate(t *testing.T) { channels[i] = byte(i) } dupChannels := make([]byte, 5) - copy(dupChannels[:], channels[:5]) + copy(dupChannels, channels[:5]) dupChannels = append(dupChannels, testCh) nonAscii := "¢§µ" diff --git a/p2p/peer.go b/p2p/peer.go index fab3b42d46c..80be0db53f4 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -248,7 +248,11 @@ func (p *peer) Send(chID byte, msgBytes []byte) bool { } res := p.mconn.Send(chID, msgBytes) if res { - p.metrics.PeerSendBytesTotal.With("peer_id", string(p.ID())).Add(float64(len(msgBytes))) + labels := []string{ + "peer_id", string(p.ID()), + "chID", fmt.Sprintf("%#x", chID), + } + p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) } return res } @@ -263,7 +267,11 @@ func (p *peer) TrySend(chID byte, msgBytes []byte) bool { } res := p.mconn.TrySend(chID, msgBytes) if res { - p.metrics.PeerSendBytesTotal.With("peer_id", string(p.ID())).Add(float64(len(msgBytes))) + labels := []string{ + "peer_id", string(p.ID()), + "chID", fmt.Sprintf("%#x", chID), + } + p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) } return res } @@ -369,7 +377,11 @@ func createMConnection( // which does onPeerError. panic(fmt.Sprintf("Unknown channel %X", chID)) } - p.metrics.PeerReceiveBytesTotal.With("peer_id", string(p.ID())).Add(float64(len(msgBytes))) + labels := []string{ + "peer_id", string(p.ID()), + "chID", fmt.Sprintf("%#x", chID), + } + p.metrics.PeerReceiveBytesTotal.With(labels...).Add(float64(len(msgBytes))) reactor.Receive(chID, p, msgBytes) } diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 8d185a59305..27bcef9e8c5 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -66,8 +66,7 @@ type AddrBook interface { // Send a selection of addresses with bias GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddress - // TODO: remove - ListOfKnownAddresses() []*knownAddress + Size() int // Persist to disk Save() @@ -179,11 +178,11 @@ func (a *addrBook) OurAddress(addr *p2p.NetAddress) bool { return ok } -func (a *addrBook) AddPrivateIDs(IDs []string) { +func (a *addrBook) AddPrivateIDs(ids []string) { a.mtx.Lock() defer a.mtx.Unlock() - for _, id := range IDs { + for _, id := range ids { a.privateIDs[p2p.ID(id)] = struct{}{} } } @@ -254,7 +253,7 @@ func (a *addrBook) PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress { bookSize := a.size() if bookSize <= 0 { if bookSize < 0 { - a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) + panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) } return nil } @@ -339,7 +338,7 @@ func (a *addrBook) GetSelection() []*p2p.NetAddress { bookSize := a.size() if bookSize <= 0 { if bookSize < 0 { - a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) + panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) } return nil } @@ -389,7 +388,7 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre bookSize := a.size() if bookSize <= 0 { if bookSize < 0 { - a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) + panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) } return nil } @@ -414,18 +413,6 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre return selection } -// ListOfKnownAddresses returns the new and old addresses. -func (a *addrBook) ListOfKnownAddresses() []*knownAddress { - a.mtx.Lock() - defer a.mtx.Unlock() - - addrs := []*knownAddress{} - for _, addr := range a.addrLookup { - addrs = append(addrs, addr.copy()) - } - return addrs -} - //------------------------------------------------ // Size returns the number of addresses in the book. @@ -473,8 +460,7 @@ func (a *addrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAd case bucketTypeOld: return a.bucketsOld[bucketIdx] default: - cmn.PanicSanity("Should not happen") - return nil + panic("Invalid bucket type") } } @@ -600,16 +586,16 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return ErrAddrBookNilAddr{addr, src} } - if a.routabilityStrict && !addr.Routable() { - return ErrAddrBookNonRoutable{addr} + if err := addr.Valid(); err != nil { + return ErrAddrBookInvalidAddr{Addr: addr, AddrErr: err} } - if !addr.Valid() { - return ErrAddrBookInvalidAddr{addr} + if _, ok := a.privateIDs[addr.ID]; ok { + return ErrAddrBookPrivate{addr} } - if !addr.HasID() { - return ErrAddrBookInvalidAddrNoID{addr} + if _, ok := a.privateIDs[src.ID]; ok { + return ErrAddrBookPrivateSrc{src} } // TODO: we should track ourAddrs by ID and by IP:PORT and refuse both. @@ -617,12 +603,8 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return ErrAddrBookSelf{addr} } - if _, ok := a.privateIDs[addr.ID]; ok { - return ErrAddrBookPrivate{addr} - } - - if _, ok := a.privateIDs[src.ID]; ok { - return ErrAddrBookPrivateSrc{src} + if a.routabilityStrict && !addr.Routable() { + return ErrAddrBookNonRoutable{addr} } ka := a.addrLookup[addr.ID] @@ -661,7 +643,7 @@ func (a *addrBook) randomPickAddresses(bucketType byte, num int) []*p2p.NetAddre } total := 0 for _, bucket := range buckets { - total = total + len(bucket) + total += len(bucket) } addresses := make([]*knownAddress, 0, total) for _, bucket := range buckets { diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index afc94d1f2bd..13bac28c866 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" @@ -342,7 +343,9 @@ func TestAddrBookGetSelectionWithBias(t *testing.T) { good++ } } + got, expected := int((float64(good)/float64(len(selection)))*100), (100 - biasTowardsNewAddrs) + // compute some slack to protect against small differences due to rounding: slack := int(math.Round(float64(100) / float64(len(selection)))) if got > expected+slack { @@ -520,7 +523,7 @@ func TestMultipleAddrBookAddressSelection(t *testing.T) { // Test for two books with sizes from following ranges ranges := [...][]int{{33, 100}, {100, 175}} - var bookSizes []int + bookSizes := make([]int, 0, len(ranges)) for _, r := range ranges { bookSizes = append(bookSizes, cmn.RandIntn(r[1]-r[0])+r[0]) } diff --git a/p2p/pex/wire.go b/p2p/pex/codec.go similarity index 72% rename from p2p/pex/wire.go rename to p2p/pex/codec.go index 57fc938582c..c88b1941c14 100644 --- a/p2p/pex/wire.go +++ b/p2p/pex/codec.go @@ -1,7 +1,7 @@ package pex import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) var cdc *amino.Codec = amino.NewCodec() diff --git a/p2p/pex/errors.go b/p2p/pex/errors.go index 1f44ceee7e0..911389a9e20 100644 --- a/p2p/pex/errors.go +++ b/p2p/pex/errors.go @@ -30,6 +30,10 @@ func (err ErrAddrBookPrivate) Error() string { return fmt.Sprintf("Cannot add private peer with address %v", err.Addr) } +func (err ErrAddrBookPrivate) PrivateAddr() bool { + return true +} + type ErrAddrBookPrivateSrc struct { Src *p2p.NetAddress } @@ -38,6 +42,10 @@ func (err ErrAddrBookPrivateSrc) Error() string { return fmt.Sprintf("Cannot add peer coming from private peer with address %v", err.Src) } +func (err ErrAddrBookPrivateSrc) PrivateAddr() bool { + return true +} + type ErrAddrBookNilAddr struct { Addr *p2p.NetAddress Src *p2p.NetAddress @@ -48,17 +56,10 @@ func (err ErrAddrBookNilAddr) Error() string { } type ErrAddrBookInvalidAddr struct { - Addr *p2p.NetAddress + Addr *p2p.NetAddress + AddrErr error } func (err ErrAddrBookInvalidAddr) Error() string { - return fmt.Sprintf("Cannot add invalid address %v", err.Addr) -} - -type ErrAddrBookInvalidAddrNoID struct { - Addr *p2p.NetAddress -} - -func (err ErrAddrBookInvalidAddrNoID) Error() string { - return fmt.Sprintf("Cannot add address with no ID %v", err.Addr) + return fmt.Sprintf("Cannot add invalid address %v: %v", err.Addr, err.AddrErr) } diff --git a/p2p/pex/file.go b/p2p/pex/file.go index 33fec033695..a42eddaf9bb 100644 --- a/p2p/pex/file.go +++ b/p2p/pex/file.go @@ -16,16 +16,15 @@ type addrBookJSON struct { } func (a *addrBook) saveToFile(filePath string) { - a.Logger.Info("Saving AddrBook to file", "size", a.Size()) - a.mtx.Lock() defer a.mtx.Unlock() - // Compile Addrs - addrs := []*knownAddress{} + + a.Logger.Info("Saving AddrBook to file", "size", a.size()) + + addrs := make([]*knownAddress, 0, len(a.addrLookup)) for _, ka := range a.addrLookup { addrs = append(addrs, ka) } - aJSON := &addrBookJSON{ Key: a.key, Addrs: addrs, @@ -54,14 +53,14 @@ func (a *addrBook) loadFromFile(filePath string) bool { // Load addrBookJSON{} r, err := os.Open(filePath) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Error opening file %s: %v", filePath, err)) + panic(fmt.Sprintf("Error opening file %s: %v", filePath, err)) } defer r.Close() // nolint: errcheck aJSON := &addrBookJSON{} dec := json.NewDecoder(r) err = dec.Decode(aJSON) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Error reading file %s: %v", filePath, err)) + panic(fmt.Sprintf("Error reading file %s: %v", filePath, err)) } // Restore all the fields... diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index 5673dec1197..acde385bcfb 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -33,18 +33,6 @@ func (ka *knownAddress) ID() p2p.ID { return ka.Addr.ID } -func (ka *knownAddress) copy() *knownAddress { - return &knownAddress{ - Addr: ka.Addr, - Src: ka.Src, - Attempts: ka.Attempts, - LastAttempt: ka.LastAttempt, - LastSuccess: ka.LastSuccess, - BucketType: ka.BucketType, - Buckets: ka.Buckets, - } -} - func (ka *knownAddress) isOld() bool { return ka.BucketType == bucketTypeOld } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 8a042b82b2f..55cde5a3504 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -3,7 +3,6 @@ package pex import ( "fmt" "reflect" - "sort" "sync" "time" @@ -35,16 +34,11 @@ const ( // Seed/Crawler constants - // We want seeds to only advertise good peers. Therefore they should wait at - // least as long as we expect it to take for a peer to become good before - // disconnecting. - // see consensus/reactor.go: blocksToContributeToBecomeGoodPeer - // 10000 blocks assuming 1s blocks ~ 2.7 hours. - defaultSeedDisconnectWaitPeriod = 3 * time.Hour + // minTimeBetweenCrawls is a minimum time between attempts to crawl a peer. + minTimeBetweenCrawls = 2 * time.Minute - defaultCrawlPeerInterval = 2 * time.Minute // don't redial for this. TODO: back-off. what for? - - defaultCrawlPeersPeriod = 30 * time.Second // check some peers every this + // check some peers every this + crawlPeerPeriod = 30 * time.Second maxAttemptsToDial = 16 // ~ 35h in total (last attempt - 18h) @@ -54,6 +48,24 @@ const ( biasToSelectNewPeers = 30 // 70 to select good peers ) +type errMaxAttemptsToDial struct { +} + +func (e errMaxAttemptsToDial) Error() string { + return fmt.Sprintf("reached max attempts %d to dial", maxAttemptsToDial) +} + +type errTooEarlyToDial struct { + backoffDuration time.Duration + lastDialed time.Time +} + +func (e errTooEarlyToDial) Error() string { + return fmt.Sprintf( + "too early to dial (backoff duration: %d, last dialed: %v, time since: %v)", + e.backoffDuration, e.lastDialed, time.Since(e.lastDialed)) +} + // PEXReactor handles PEX (peer exchange) and ensures that an // adequate number of peers are connected to the switch. // @@ -77,6 +89,9 @@ type PEXReactor struct { seedAddrs []*p2p.NetAddress attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)} + + // seed/crawled mode fields + crawlPeerInfos map[p2p.ID]crawlPeerInfo } func (r *PEXReactor) minReceiveRequestInterval() time.Duration { @@ -90,6 +105,11 @@ type PEXReactorConfig struct { // Seed/Crawler mode SeedMode bool + // We want seeds to only advertise good peers. Therefore they should wait at + // least as long as we expect it to take for a peer to become good before + // disconnecting. + SeedDisconnectWaitPeriod time.Duration + // Seeds is a list of addresses reactor may use // if it can't connect to peers in the addrbook. Seeds []string @@ -108,6 +128,7 @@ func NewPEXReactor(b AddrBook, config *PEXReactorConfig) *PEXReactor { ensurePeersPeriod: defaultEnsurePeersPeriod, requestsSent: cmn.NewCMap(), lastReceivedRequests: cmn.NewCMap(), + crawlPeerInfos: make(map[p2p.ID]crawlPeerInfo), } r.BaseReactor = *p2p.NewBaseReactor("PEXReactor", r) return r @@ -124,7 +145,7 @@ func (r *PEXReactor) OnStart() error { if err != nil { return err } else if numOnline == 0 && r.book.Empty() { - return errors.New("Address book is empty, and could not connect to any seed nodes") + return errors.New("Address book is empty and couldn't resolve any seed nodes") } r.seedAddrs = seedAddrs @@ -183,6 +204,13 @@ func (r *PEXReactor) AddPeer(p Peer) { } } +// RemovePeer implements Reactor by resetting peer's requests info. +func (r *PEXReactor) RemovePeer(p Peer, reason interface{}) { + id := string(p.ID()) + r.requestsSent.Delete(id) + r.lastReceivedRequests.Delete(id) +} + func (r *PEXReactor) logErrAddrBook(err error) { if err != nil { switch err.(type) { @@ -195,13 +223,6 @@ func (r *PEXReactor) logErrAddrBook(err error) { } } -// RemovePeer implements Reactor. -func (r *PEXReactor) RemovePeer(p Peer, reason interface{}) { - id := string(p.ID()) - r.requestsSent.Delete(id) - r.lastReceivedRequests.Delete(id) -} - // Receive implements Reactor by handling incoming PEX messages. func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) { msg, err := decodeMsg(msgBytes) @@ -282,7 +303,7 @@ func (r *PEXReactor) receiveRequest(src Peer) error { now := time.Now() minInterval := r.minReceiveRequestInterval() if now.Sub(lastReceived) < minInterval { - return fmt.Errorf("Peer (%v) sent next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", + return fmt.Errorf("peer (%v) sent next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", src.ID(), lastReceived, now, @@ -293,14 +314,14 @@ func (r *PEXReactor) receiveRequest(src Peer) error { return nil } -// RequestAddrs asks peer for more addresses if we do not already -// have a request out for this peer. +// RequestAddrs asks peer for more addresses if we do not already have a +// request out for this peer. func (r *PEXReactor) RequestAddrs(p Peer) { - r.Logger.Debug("Request addrs", "from", p) id := string(p.ID()) if r.requestsSent.Has(id) { return } + r.Logger.Debug("Request addrs", "from", p) r.requestsSent.Set(id, struct{}{}) p.Send(PexChannel, cdc.MustMarshalBinaryBare(&pexRequestMessage{})) } @@ -311,7 +332,7 @@ func (r *PEXReactor) RequestAddrs(p Peer) { func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { id := string(src.ID()) if !r.requestsSent.Has(id) { - return errors.New("Unsolicited pexAddrsMessage") + return errors.New("unsolicited pexAddrsMessage") } r.requestsSent.Delete(id) @@ -319,23 +340,18 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { if err != nil { return err } - for _, netAddr := range addrs { - // Validate netAddr. Disconnect from a peer if it sends us invalid data. - if netAddr == nil { - return errors.New("nil address in pexAddrsMessage") - } - // TODO: extract validating logic from NewNetAddressStringWithOptionalID - // and put it in netAddr#Valid (#2722) - na, err := p2p.NewNetAddressString(netAddr.String()) - if err != nil { - return fmt.Errorf("%s address in pexAddrsMessage is invalid: %v", - netAddr.String(), - err, - ) + + srcIsSeed := false + for _, seedAddr := range r.seedAddrs { + if seedAddr.Equals(srcAddr) { + srcIsSeed = true + break } + } + for _, netAddr := range addrs { // NOTE: we check netAddr validity and routability in book#AddAddress. - err = r.book.AddAddress(na, srcAddr) + err = r.book.AddAddress(netAddr, srcAddr) if err != nil { r.logErrAddrBook(err) // XXX: should we be strict about incoming data and disconnect from a @@ -344,13 +360,23 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { } // If this address came from a seed node, try to connect to it without - // waiting. - for _, seedAddr := range r.seedAddrs { - if seedAddr.Equals(srcAddr) { - r.ensurePeers() - } + // waiting (#2093) + if srcIsSeed { + r.Logger.Info("Will dial address, which came from seed", "addr", netAddr, "seed", srcAddr) + go func(addr *p2p.NetAddress) { + err := r.dialPeer(addr) + if err != nil { + switch err.(type) { + case errMaxAttemptsToDial, errTooEarlyToDial: + r.Logger.Debug(err.Error(), "addr", addr) + default: + r.Logger.Error(err.Error(), "addr", addr) + } + } + }(netAddr) } } + return nil } @@ -372,9 +398,9 @@ func (r *PEXReactor) ensurePeersRoutine() { ) // Randomize first round of communication to avoid thundering herd. - // If no potential peers are present directly start connecting so we guarantee - // swift setup with the help of configured seeds. - if r.hasPotentialPeers() { + // If no peers are present directly start connecting so we guarantee swift + // setup with the help of configured seeds. + if r.nodeHasSomePeersOrDialingAny() { time.Sleep(time.Duration(jitter)) } @@ -446,11 +472,21 @@ func (r *PEXReactor) ensurePeers() { // Dial picked addresses for _, addr := range toDial { - go r.dialPeer(addr) + go func(addr *p2p.NetAddress) { + err := r.dialPeer(addr) + if err != nil { + switch err.(type) { + case errMaxAttemptsToDial, errTooEarlyToDial: + r.Logger.Debug(err.Error(), "addr", addr) + default: + r.Logger.Error(err.Error(), "addr", addr) + } + } + }(addr) } - // If we need more addresses, pick a random peer and ask for more. if r.book.NeedMoreAddrs() { + // 1) Pick a random peer and ask for more. peers := r.Switch.Peers().List() peersCount := len(peers) if peersCount > 0 { @@ -458,12 +494,14 @@ func (r *PEXReactor) ensurePeers() { r.Logger.Info("We need more addresses. Sending pexRequest to random peer", "peer", peer) r.RequestAddrs(peer) } - } - // If we are not connected to nor dialing anybody, fallback to dialing a seed. - if out+in+dial+len(toDial) == 0 { - r.Logger.Info("No addresses to dial nor connected peers. Falling back to seeds") - r.dialSeeds() + // 2) Dial seeds if we are not dialing anyone. + // This is done in addition to asking a peer for addresses to work-around + // peers not participating in PEX. + if len(toDial) == 0 { + r.Logger.Info("No addresses to dial. Falling back to seeds") + r.dialSeeds() + } } } @@ -476,17 +514,16 @@ func (r *PEXReactor) dialAttemptsInfo(addr *p2p.NetAddress) (attempts int, lastD return atd.number, atd.lastDialed } -func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { +func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) error { attempts, lastDialed := r.dialAttemptsInfo(addr) if attempts > maxAttemptsToDial { - // Do not log the message if the addr gets readded. - if attempts == maxAttemptsToDial+1 { - r.Logger.Info("Reached max attempts to dial", "addr", addr, "attempts", attempts) - r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) - } + // TODO(melekes): have a blacklist in the addrbook with peers whom we've + // failed to connect to. Then we can clean up attemptsToDial, which acts as + // a blacklist currently. + // https://github.com/tendermint/tendermint/issues/3572 r.book.MarkBad(addr) - return + return errMaxAttemptsToDial{} } // exponential backoff if it's not our first attempt to dial given address @@ -495,30 +532,30 @@ func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { backoffDuration := jitterSeconds + ((1 << uint(attempts)) * time.Second) sinceLastDialed := time.Since(lastDialed) if sinceLastDialed < backoffDuration { - r.Logger.Debug("Too early to dial", "addr", addr, "backoff_duration", backoffDuration, "last_dialed", lastDialed, "time_since", sinceLastDialed) - return + return errTooEarlyToDial{backoffDuration, lastDialed} } } - err := r.Switch.DialPeerWithAddress(addr, false) + err := r.Switch.DialPeerWithAddress(addr) if err != nil { - r.Logger.Error("Dialing failed", "addr", addr, "err", err, "attempts", attempts) - // TODO: detect more "bad peer" scenarios - if _, ok := err.(p2p.ErrSwitchAuthenticationFailure); ok { - r.book.MarkBad(addr) + if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { + return err + } + + markAddrInBookBasedOnErr(addr, r.book, err) + switch err.(type) { + case p2p.ErrSwitchAuthenticationFailure: + // NOTE: addr is removed from addrbook in markAddrInBookBasedOnErr r.attemptsToDial.Delete(addr.DialString()) - } else { - r.book.MarkAttempt(addr) - // FIXME: if the addr is going to be removed from the addrbook (hard to - // tell at this point), we need to Delete it from attemptsToDial, not - // record another attempt. - // record attempt + default: r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) } - } else { - // cleanup any history - r.attemptsToDial.Delete(addr.DialString()) + return errors.Wrapf(err, "dialing failed (attempts: %d)", attempts+1) } + + // cleanup any history + r.attemptsToDial.Delete(addr.DialString()) + return nil } // checkSeeds checks that addresses are well formed. @@ -541,7 +578,7 @@ func (r *PEXReactor) checkSeeds() (numOnline int, netAddrs []*p2p.NetAddress, er return 0, nil, errors.Wrap(e, "seed node configuration has error") } } - return + return numOnline, netAddrs, nil } // randomly dial seeds until we connect to one or exhaust them @@ -551,13 +588,16 @@ func (r *PEXReactor) dialSeeds() { for _, i := range perm { // dial a random seed seedAddr := r.seedAddrs[i] - err := r.Switch.DialPeerWithAddress(seedAddr, false) + err := r.Switch.DialPeerWithAddress(seedAddr) if err == nil { return } r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr) } - r.Switch.Logger.Error("Couldn't connect to any seeds") + // do not write error message if there were no seeds specified in config + if len(r.seedAddrs) > 0 { + r.Switch.Logger.Error("Couldn't connect to any seeds") + } } // AttemptsToDial returns the number of attempts to dial specific address. It @@ -576,103 +616,98 @@ func (r *PEXReactor) AttemptsToDial(addr *p2p.NetAddress) int { // Seed/Crawler Mode causes this node to quickly disconnect // from peers, except other seed nodes. func (r *PEXReactor) crawlPeersRoutine() { - // Do an initial crawl - r.crawlPeers() + // If we have any seed nodes, consult them first + if len(r.seedAddrs) > 0 { + r.dialSeeds() + } else { + // Do an initial crawl + r.crawlPeers(r.book.GetSelection()) + } // Fire periodically - ticker := time.NewTicker(defaultCrawlPeersPeriod) + ticker := time.NewTicker(crawlPeerPeriod) for { select { case <-ticker.C: r.attemptDisconnects() - r.crawlPeers() + r.crawlPeers(r.book.GetSelection()) + r.cleanupCrawlPeerInfos() case <-r.Quit(): return } } } -// hasPotentialPeers indicates if there is a potential peer to connect to, by -// consulting the Switch as well as the AddrBook. -func (r *PEXReactor) hasPotentialPeers() bool { +// nodeHasSomePeersOrDialingAny returns true if the node is connected to some +// peers or dialing them currently. +func (r *PEXReactor) nodeHasSomePeersOrDialingAny() bool { out, in, dial := r.Switch.NumPeers() - - return out+in+dial > 0 && len(r.book.ListOfKnownAddresses()) > 0 + return out+in+dial > 0 } -// crawlPeerInfo handles temporary data needed for the -// network crawling performed during seed/crawler mode. +// crawlPeerInfo handles temporary data needed for the network crawling +// performed during seed/crawler mode. type crawlPeerInfo struct { - // The listening address of a potential peer we learned about - Addr *p2p.NetAddress - - // The last time we attempt to reach this address - LastAttempt time.Time - - // The last time we successfully reached this address - LastSuccess time.Time + Addr *p2p.NetAddress `json:"addr"` + // The last time we crawled the peer or attempted to do so. + LastCrawled time.Time `json:"last_crawled"` } -// oldestFirst implements sort.Interface for []crawlPeerInfo -// based on the LastAttempt field. -type oldestFirst []crawlPeerInfo - -func (of oldestFirst) Len() int { return len(of) } -func (of oldestFirst) Swap(i, j int) { of[i], of[j] = of[j], of[i] } -func (of oldestFirst) Less(i, j int) bool { return of[i].LastAttempt.Before(of[j].LastAttempt) } - -// getPeersToCrawl returns addresses of potential peers that we wish to validate. -// NOTE: The status information is ordered as described above. -func (r *PEXReactor) getPeersToCrawl() []crawlPeerInfo { - var of oldestFirst +// crawlPeers will crawl the network looking for new peer addresses. +func (r *PEXReactor) crawlPeers(addrs []*p2p.NetAddress) { + now := time.Now() - // TODO: be more selective - addrs := r.book.ListOfKnownAddresses() for _, addr := range addrs { - if len(addr.ID()) == 0 { - continue // dont use peers without id - } - - of = append(of, crawlPeerInfo{ - Addr: addr.Addr, - LastAttempt: addr.LastAttempt, - LastSuccess: addr.LastSuccess, - }) - } - sort.Sort(of) - return of -} + peerInfo, ok := r.crawlPeerInfos[addr.ID] -// crawlPeers will crawl the network looking for new peer addresses. (once) -func (r *PEXReactor) crawlPeers() { - peerInfos := r.getPeersToCrawl() - - now := time.Now() - // Use addresses we know of to reach additional peers - for _, pi := range peerInfos { - // Do not attempt to connect with peers we recently dialed - if now.Sub(pi.LastAttempt) < defaultCrawlPeerInterval { + // Do not attempt to connect with peers we recently crawled. + if ok && now.Sub(peerInfo.LastCrawled) < minTimeBetweenCrawls { continue } - // Otherwise, attempt to connect with the known address - err := r.Switch.DialPeerWithAddress(pi.Addr, false) + + // Record crawling attempt. + r.crawlPeerInfos[addr.ID] = crawlPeerInfo{ + Addr: addr, + LastCrawled: now, + } + + err := r.dialPeer(addr) if err != nil { - r.book.MarkAttempt(pi.Addr) + switch err.(type) { + case errMaxAttemptsToDial, errTooEarlyToDial: + r.Logger.Debug(err.Error(), "addr", addr) + default: + r.Logger.Error(err.Error(), "addr", addr) + } continue } - // Ask for more addresses - peer := r.Switch.Peers().Get(pi.Addr.ID) + + peer := r.Switch.Peers().Get(addr.ID) if peer != nil { r.RequestAddrs(peer) } } } +func (r *PEXReactor) cleanupCrawlPeerInfos() { + for id, info := range r.crawlPeerInfos { + // If we did not crawl a peer for 24 hours, it means the peer was removed + // from the addrbook => remove + // + // 10000 addresses / maxGetSelection = 40 cycles to get all addresses in + // the ideal case, + // 40 * crawlPeerPeriod ~ 20 minutes + if time.Since(info.LastCrawled) > 24*time.Hour { + delete(r.crawlPeerInfos, id) + } + } +} + // attemptDisconnects checks if we've been with each peer long enough to disconnect func (r *PEXReactor) attemptDisconnects() { for _, peer := range r.Switch.Peers().List() { - if peer.Status().Duration < defaultSeedDisconnectWaitPeriod { + if peer.Status().Duration < r.config.SeedDisconnectWaitPeriod { continue } if peer.IsPersistent() { @@ -682,6 +717,16 @@ func (r *PEXReactor) attemptDisconnects() { } } +func markAddrInBookBasedOnErr(addr *p2p.NetAddress, book AddrBook, err error) { + // TODO: detect more "bad peer" scenarios + switch err.(type) { + case p2p.ErrSwitchAuthenticationFailure: + book.MarkBad(addr) + default: + book.MarkAttempt(addr) + } +} + //----------------------------------------------------------------------------- // Messages diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 1b648954b1f..f4b7cc265f0 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -3,7 +3,6 @@ package pex import ( "fmt" "io/ioutil" - "net" "os" "path/filepath" "testing" @@ -12,14 +11,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/p2p/conn" + "github.com/tendermint/tendermint/p2p/mock" ) var ( @@ -148,8 +143,8 @@ func TestPEXReactorRequestMessageAbuse(t *testing.T) { sw := createSwitchAndAddReactors(r) sw.SetAddrBook(book) - peer := newMockPeer() - p2p.AddPeerToSwitch(sw, peer) + peer := mock.NewPeer(nil) + p2p.AddPeerToSwitchPeerSet(sw, peer) assert.True(t, sw.Peers().Has(peer.ID())) id := string(peer.ID()) @@ -178,8 +173,8 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { sw := createSwitchAndAddReactors(r) sw.SetAddrBook(book) - peer := newMockPeer() - p2p.AddPeerToSwitch(sw, peer) + peer := mock.NewPeer(nil) + p2p.AddPeerToSwitchPeerSet(sw, peer) assert.True(t, sw.Peers().Has(peer.ID())) id := string(peer.ID()) @@ -209,26 +204,26 @@ func TestCheckSeeds(t *testing.T) { defer os.RemoveAll(dir) // nolint: errcheck // 1. test creating peer with no seeds works - peer := testCreateDefaultPeer(dir, 0) - require.Nil(t, peer.Start()) - peer.Stop() + peerSwitch := testCreateDefaultPeer(dir, 0) + require.Nil(t, peerSwitch.Start()) + peerSwitch.Stop() // 2. create seed seed := testCreateSeed(dir, 1, []*p2p.NetAddress{}, []*p2p.NetAddress{}) // 3. test create peer with online seed works - peer = testCreatePeerWithSeed(dir, 2, seed) - require.Nil(t, peer.Start()) - peer.Stop() + peerSwitch = testCreatePeerWithSeed(dir, 2, seed) + require.Nil(t, peerSwitch.Start()) + peerSwitch.Stop() // 4. test create peer with all seeds having unresolvable DNS fails badPeerConfig := &PEXReactorConfig{ Seeds: []string{"ed3dfd27bfc4af18f67a49862f04cc100696e84d@bad.network.addr:26657", "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657"}, } - peer = testCreatePeerWithConfig(dir, 2, badPeerConfig) - require.Error(t, peer.Start()) - peer.Stop() + peerSwitch = testCreatePeerWithConfig(dir, 2, badPeerConfig) + require.Error(t, peerSwitch.Start()) + peerSwitch.Stop() // 5. test create peer with one good seed address succeeds badPeerConfig = &PEXReactorConfig{ @@ -236,9 +231,9 @@ func TestCheckSeeds(t *testing.T) { "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657", seed.NetAddress().String()}, } - peer = testCreatePeerWithConfig(dir, 2, badPeerConfig) - require.Nil(t, peer.Start()) - peer.Stop() + peerSwitch = testCreatePeerWithConfig(dir, 2, badPeerConfig) + require.Nil(t, peerSwitch.Start()) + peerSwitch.Stop() } func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { @@ -290,31 +285,109 @@ func TestConnectionSpeedForPeerReceivedFromSeed(t *testing.T) { assertPeersWithTimeout(t, []*p2p.Switch{secondPeer}, 10*time.Millisecond, 1*time.Second, 2) } -func TestPEXReactorCrawlStatus(t *testing.T) { - pexR, book := createReactor(&PEXReactorConfig{SeedMode: true}) +func TestPEXReactorSeedMode(t *testing.T) { + // directory to store address books + dir, err := ioutil.TempDir("", "pex_reactor") + require.Nil(t, err) + defer os.RemoveAll(dir) // nolint: errcheck + + pexRConfig := &PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 10 * time.Millisecond} + pexR, book := createReactor(pexRConfig) defer teardownReactor(book) - // Seed/Crawler mode uses data from the Switch sw := createSwitchAndAddReactors(pexR) sw.SetAddrBook(book) + err = sw.Start() + require.NoError(t, err) + defer sw.Stop() - // Create a peer, add it to the peer set and the addrbook. - peer := p2p.CreateRandomPeer(false) - p2p.AddPeerToSwitch(pexR.Switch, peer) - addr1 := peer.SocketAddr() - pexR.book.AddAddress(addr1, addr1) + assert.Zero(t, sw.Peers().Size()) - // Add a non-connected address to the book. - _, addr2 := p2p.CreateRoutableAddr() - pexR.book.AddAddress(addr2, addr1) + peerSwitch := testCreateDefaultPeer(dir, 1) + require.NoError(t, peerSwitch.Start()) + defer peerSwitch.Stop() - // Get some peerInfos to crawl - peerInfos := pexR.getPeersToCrawl() + // 1. Test crawlPeers dials the peer + pexR.crawlPeers([]*p2p.NetAddress{peerSwitch.NetAddress()}) + assert.Equal(t, 1, sw.Peers().Size()) + assert.True(t, sw.Peers().Has(peerSwitch.NodeInfo().ID())) - // Make sure it has the proper number of elements - assert.Equal(t, 2, len(peerInfos)) + // 2. attemptDisconnects should not disconnect because of wait period + pexR.attemptDisconnects() + assert.Equal(t, 1, sw.Peers().Size()) - // TODO: test + // sleep for SeedDisconnectWaitPeriod + time.Sleep(pexRConfig.SeedDisconnectWaitPeriod + 1*time.Millisecond) + + // 3. attemptDisconnects should disconnect after wait period + pexR.attemptDisconnects() + assert.Equal(t, 0, sw.Peers().Size()) +} + +func TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode(t *testing.T) { + // directory to store address books + dir, err := ioutil.TempDir("", "pex_reactor") + require.Nil(t, err) + defer os.RemoveAll(dir) // nolint: errcheck + + pexRConfig := &PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 1 * time.Millisecond} + pexR, book := createReactor(pexRConfig) + defer teardownReactor(book) + + sw := createSwitchAndAddReactors(pexR) + sw.SetAddrBook(book) + err = sw.Start() + require.NoError(t, err) + defer sw.Stop() + + assert.Zero(t, sw.Peers().Size()) + + peerSwitch := testCreateDefaultPeer(dir, 1) + require.NoError(t, peerSwitch.Start()) + defer peerSwitch.Stop() + + err = sw.AddPersistentPeers([]string{peerSwitch.NetAddress().String()}) + require.NoError(t, err) + + // 1. Test crawlPeers dials the peer + pexR.crawlPeers([]*p2p.NetAddress{peerSwitch.NetAddress()}) + assert.Equal(t, 1, sw.Peers().Size()) + assert.True(t, sw.Peers().Has(peerSwitch.NodeInfo().ID())) + + // sleep for SeedDisconnectWaitPeriod + time.Sleep(pexRConfig.SeedDisconnectWaitPeriod + 1*time.Millisecond) + + // 2. attemptDisconnects should not disconnect because the peer is persistent + pexR.attemptDisconnects() + assert.Equal(t, 1, sw.Peers().Size()) +} + +func TestPEXReactorDialsPeerUpToMaxAttemptsInSeedMode(t *testing.T) { + // directory to store address books + dir, err := ioutil.TempDir("", "pex_reactor") + require.Nil(t, err) + defer os.RemoveAll(dir) // nolint: errcheck + + pexR, book := createReactor(&PEXReactorConfig{SeedMode: true}) + defer teardownReactor(book) + + sw := createSwitchAndAddReactors(pexR) + sw.SetAddrBook(book) + err = sw.Start() + require.NoError(t, err) + defer sw.Stop() + + peer := mock.NewPeer(nil) + addr := peer.SocketAddr() + + err = book.AddAddress(addr, addr) + require.NoError(t, err) + + assert.True(t, book.HasAddress(addr)) + // imitate maxAttemptsToDial reached + pexR.attemptsToDial.Store(addr.DialString(), _attemptsToDial{maxAttemptsToDial + 1, time.Now()}) + pexR.crawlPeers([]*p2p.NetAddress{addr}) + assert.False(t, book.HasAddress(addr)) } // connect a peer to a seed, wait a bit, then stop it. @@ -365,7 +438,7 @@ func TestPEXReactorSeedModeFlushStop(t *testing.T) { reactor := switches[0].Reactors()["pex"].(*PEXReactor) peerID := switches[1].NodeInfo().ID() - err = switches[1].DialPeerWithAddress(switches[0].NetAddress(), false) + err = switches[1].DialPeerWithAddress(switches[0].NetAddress()) assert.NoError(t, err) // sleep up to a second while waiting for the peer to send us a message. @@ -419,7 +492,7 @@ func TestPEXReactorDialPeer(t *testing.T) { sw := createSwitchAndAddReactors(pexR) sw.SetAddrBook(book) - peer := newMockPeer() + peer := mock.NewPeer(nil) addr := peer.SocketAddr() assert.Equal(t, 0, pexR.AttemptsToDial(addr)) @@ -445,46 +518,6 @@ func TestPEXReactorDialPeer(t *testing.T) { } } -type mockPeer struct { - *cmn.BaseService - pubKey crypto.PubKey - addr *p2p.NetAddress - outbound, persistent bool -} - -func newMockPeer() mockPeer { - _, netAddr := p2p.CreateRoutableAddr() - mp := mockPeer{ - addr: netAddr, - pubKey: ed25519.GenPrivKey().PubKey(), - } - mp.BaseService = cmn.NewBaseService(nil, "MockPeer", mp) - mp.Start() - return mp -} - -func (mp mockPeer) FlushStop() { mp.Stop() } -func (mp mockPeer) ID() p2p.ID { return mp.addr.ID } -func (mp mockPeer) IsOutbound() bool { return mp.outbound } -func (mp mockPeer) IsPersistent() bool { return mp.persistent } -func (mp mockPeer) NodeInfo() p2p.NodeInfo { - return p2p.DefaultNodeInfo{ - ID_: mp.addr.ID, - ListenAddr: mp.addr.DialString(), - } -} -func (mp mockPeer) RemoteIP() net.IP { return net.ParseIP("127.0.0.1") } -func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} } -func (mp mockPeer) Send(byte, []byte) bool { return false } -func (mp mockPeer) TrySend(byte, []byte) bool { return false } -func (mp mockPeer) Set(string, interface{}) {} -func (mp mockPeer) Get(string) interface{} { return nil } -func (mp mockPeer) SocketAddr() *p2p.NetAddress { return mp.addr } -func (mp mockPeer) RemoteAddr() net.Addr { - return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8800} -} -func (mp mockPeer) CloseConn() error { return nil } - func assertPeersWithTimeout( t *testing.T, switches []*p2p.Switch, diff --git a/p2p/switch.go b/p2p/switch.go index 841254dd0db..66c2f9e4aaa 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -6,6 +6,8 @@ import ( "sync" "time" + "github.com/pkg/errors" + "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p/conn" @@ -75,6 +77,8 @@ type Switch struct { nodeInfo NodeInfo // our node info nodeKey *NodeKey // our node privkey addrBook AddrBook + // peers addresses with whom we'll maintain constant connection + persistentPeersAddrs []*NetAddress transport Transport @@ -102,16 +106,17 @@ func NewSwitch( options ...SwitchOption, ) *Switch { sw := &Switch{ - config: cfg, - reactors: make(map[string]Reactor), - chDescs: make([]*conn.ChannelDescriptor, 0), - reactorsByCh: make(map[byte]Reactor), - peers: NewPeerSet(), - dialing: cmn.NewCMap(), - reconnecting: cmn.NewCMap(), - metrics: NopMetrics(), - transport: transport, - filterTimeout: defaultFilterTimeout, + config: cfg, + reactors: make(map[string]Reactor), + chDescs: make([]*conn.ChannelDescriptor, 0), + reactorsByCh: make(map[byte]Reactor), + peers: NewPeerSet(), + dialing: cmn.NewCMap(), + reconnecting: cmn.NewCMap(), + metrics: NopMetrics(), + transport: transport, + filterTimeout: defaultFilterTimeout, + persistentPeersAddrs: make([]*NetAddress, 0), } // Ensure we have a completely undeterministic PRNG. @@ -147,13 +152,11 @@ func WithMetrics(metrics *Metrics) SwitchOption { // AddReactor adds the given reactor to the switch. // NOTE: Not goroutine safe. func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor { - // Validate the reactor. - // No two reactors can share the same channel. - reactorChannels := reactor.GetChannels() - for _, chDesc := range reactorChannels { + for _, chDesc := range reactor.GetChannels() { chID := chDesc.ID + // No two reactors can share the same channel. if sw.reactorsByCh[chID] != nil { - cmn.PanicSanity(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chID, sw.reactorsByCh[chID], reactor)) + panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chID, sw.reactorsByCh[chID], reactor)) } sw.chDescs = append(sw.chDescs, chDesc) sw.reactorsByCh[chID] = reactor @@ -163,6 +166,23 @@ func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor { return reactor } +// RemoveReactor removes the given Reactor from the Switch. +// NOTE: Not goroutine safe. +func (sw *Switch) RemoveReactor(name string, reactor Reactor) { + for _, chDesc := range reactor.GetChannels() { + // remove channel description + for i := 0; i < len(sw.chDescs); i++ { + if chDesc.ID == sw.chDescs[i].ID { + sw.chDescs = append(sw.chDescs[:i], sw.chDescs[i+1:]...) + break + } + } + delete(sw.reactorsByCh, chDesc.ID) + } + delete(sw.reactors, name) + reactor.SetSwitch(nil) +} + // Reactors returns a map of reactors registered on the switch. // NOTE: Not goroutine safe. func (sw *Switch) Reactors() map[string]Reactor { @@ -216,11 +236,7 @@ func (sw *Switch) OnStart() error { func (sw *Switch) OnStop() { // Stop peers for _, p := range sw.peers.List() { - sw.transport.Cleanup(p) - p.Stop() - if sw.peers.Remove(p) { - sw.metrics.Peers.Add(float64(-1)) - } + sw.stopAndRemovePeer(p, nil) } // Stop reactors @@ -240,21 +256,26 @@ func (sw *Switch) OnStop() { // // NOTE: Broadcast uses goroutines, so order of broadcast may not be preserved. func (sw *Switch) Broadcast(chID byte, msgBytes []byte) chan bool { - successChan := make(chan bool, len(sw.peers.List())) sw.Logger.Debug("Broadcast", "channel", chID, "msgBytes", fmt.Sprintf("%X", msgBytes)) + + peers := sw.peers.List() var wg sync.WaitGroup - for _, peer := range sw.peers.List() { - wg.Add(1) - go func(peer Peer) { + wg.Add(len(peers)) + successChan := make(chan bool, len(peers)) + + for _, peer := range peers { + go func(p Peer) { defer wg.Done() - success := peer.Send(chID, msgBytes) + success := p.Send(chID, msgBytes) successChan <- success }(peer) } + go func() { wg.Wait() close(successChan) }() + return successChan } @@ -290,7 +311,19 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) { sw.stopAndRemovePeer(peer, reason) if peer.IsPersistent() { - go sw.reconnectToPeer(peer.SocketAddr()) + var addr *NetAddress + if peer.IsOutbound() { // socket address for outbound peers + addr = peer.SocketAddr() + } else { // self-reported address for inbound peers + var err error + addr, err = peer.NodeInfo().NetAddress() + if err != nil { + sw.Logger.Error("Wanted to reconnect to inbound peer, but self-reported address is wrong", + "peer", peer, "err", err) + return + } + } + go sw.reconnectToPeer(addr) } } @@ -302,14 +335,20 @@ func (sw *Switch) StopPeerGracefully(peer Peer) { } func (sw *Switch) stopAndRemovePeer(peer Peer, reason interface{}) { - if sw.peers.Remove(peer) { - sw.metrics.Peers.Add(float64(-1)) - } sw.transport.Cleanup(peer) peer.Stop() + for _, reactor := range sw.reactors { reactor.RemovePeer(peer, reason) } + + // Removing a peer should go last to avoid a situation where a peer + // reconnect to our node and the switch calls InitPeer before + // RemovePeer is finished. + // https://github.com/tendermint/tendermint/issues/3338 + if sw.peers.Remove(peer) { + sw.metrics.Peers.Add(float64(-1)) + } } // reconnectToPeer tries to reconnect to the addr, first repeatedly @@ -334,14 +373,11 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { return } - if sw.IsDialingOrExistingAddress(addr) { - sw.Logger.Debug("Peer connection has been established or dialed while we waiting next try", "addr", addr) - return - } - - err := sw.DialPeerWithAddress(addr, true) + err := sw.DialPeerWithAddress(addr) if err == nil { return // success + } else if _, ok := err.(ErrCurrentlyDialingOrExistingAddress); ok { + return } sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "addr", addr) @@ -360,9 +396,12 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { // sleep an exponentially increasing amount sleepIntervalSeconds := math.Pow(reconnectBackOffBaseSeconds, float64(i)) sw.randomSleep(time.Duration(sleepIntervalSeconds) * time.Second) - err := sw.DialPeerWithAddress(addr, true) + + err := sw.DialPeerWithAddress(addr) if err == nil { return // success + } else if _, ok := err.(ErrCurrentlyDialingOrExistingAddress); ok { + return } sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "addr", addr) } @@ -385,35 +424,61 @@ func (sw *Switch) MarkPeerAsGood(peer Peer) { //--------------------------------------------------------------------- // Dialing -// DialPeersAsync dials a list of peers asynchronously in random order (optionally, making them persistent). +type privateAddr interface { + PrivateAddr() bool +} + +func isPrivateAddr(err error) bool { + te, ok := errors.Cause(err).(privateAddr) + return ok && te.PrivateAddr() +} + +// DialPeersAsync dials a list of peers asynchronously in random order. // Used to dial peers from config on startup or from unsafe-RPC (trusted sources). -// TODO: remove addrBook arg since it's now set on the switch -func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent bool) error { +// It ignores ErrNetAddressLookup. However, if there are other errors, first +// encounter is returned. +// Nop if there are no peers. +func (sw *Switch) DialPeersAsync(peers []string) error { netAddrs, errs := NewNetAddressStrings(peers) - // only log errors, dial correct addresses + // report all the errors for _, err := range errs { sw.Logger.Error("Error in peer's address", "err", err) } + // return first non-ErrNetAddressLookup error + for _, err := range errs { + if _, ok := err.(ErrNetAddressLookup); ok { + continue + } + return err + } + sw.dialPeersAsync(netAddrs) + return nil +} +func (sw *Switch) dialPeersAsync(netAddrs []*NetAddress) { ourAddr := sw.NetAddress() // TODO: this code feels like it's in the wrong place. // The integration tests depend on the addrBook being saved // right away but maybe we can change that. Recall that // the addrBook is only written to disk every 2min - if addrBook != nil { + if sw.addrBook != nil { // add peers to `addrBook` for _, netAddr := range netAddrs { // do not add our address or ID if !netAddr.Same(ourAddr) { - if err := addrBook.AddAddress(netAddr, ourAddr); err != nil { - sw.Logger.Error("Can't add peer's address to addrbook", "err", err) + if err := sw.addrBook.AddAddress(netAddr, ourAddr); err != nil { + if isPrivateAddr(err) { + sw.Logger.Debug("Won't add peer's address to addrbook", "err", err) + } else { + sw.Logger.Error("Can't add peer's address to addrbook", "err", err) + } } } } // Persist some peers to disk right away. // NOTE: integration tests depend on this - addrBook.Save() + sw.addrBook.Save() } // permute the list, dial them in random order. @@ -430,15 +495,10 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b sw.randomSleep(0) - if sw.IsDialingOrExistingAddress(addr) { - sw.Logger.Debug("Ignore attempt to connect to an existing peer", "addr", addr) - return - } - - err := sw.DialPeerWithAddress(addr, persistent) + err := sw.DialPeerWithAddress(addr) if err != nil { switch err.(type) { - case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeerID: + case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeerID, ErrCurrentlyDialingOrExistingAddress: sw.Logger.Debug("Error dialing peer", "err", err) default: sw.Logger.Error("Error dialing peer", "err", err) @@ -446,15 +506,21 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b } }(i) } - return nil } -// DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects and authenticates successfully. -// If `persistent == true`, the switch will always try to reconnect to this peer if the connection ever fails. -func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) error { +// DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects +// and authenticates successfully. +// If we're currently dialing this address or it belongs to an existing peer, +// ErrCurrentlyDialingOrExistingAddress is returned. +func (sw *Switch) DialPeerWithAddress(addr *NetAddress) error { + if sw.IsDialingOrExistingAddress(addr) { + return ErrCurrentlyDialingOrExistingAddress{addr.String()} + } + sw.dialing.Set(string(addr.ID), addr) defer sw.dialing.Delete(string(addr.ID)) - return sw.addOutboundPeerWithConfig(addr, sw.config, persistent) + + return sw.addOutboundPeerWithConfig(addr, sw.config) } // sleep for interval plus some random amount of ms on [0, dialRandomizerIntervalMilliseconds] @@ -471,6 +537,38 @@ func (sw *Switch) IsDialingOrExistingAddress(addr *NetAddress) bool { (!sw.config.AllowDuplicateIP && sw.peers.HasIP(addr.IP)) } +// AddPersistentPeers allows you to set persistent peers. It ignores +// ErrNetAddressLookup. However, if there are other errors, first encounter is +// returned. +func (sw *Switch) AddPersistentPeers(addrs []string) error { + sw.Logger.Info("Adding persistent peers", "addrs", addrs) + netAddrs, errs := NewNetAddressStrings(addrs) + // report all the errors + for _, err := range errs { + sw.Logger.Error("Error in peer's address", "err", err) + } + // return first non-ErrNetAddressLookup error + for _, err := range errs { + if _, ok := err.(ErrNetAddressLookup); ok { + continue + } + return err + } + sw.persistentPeersAddrs = netAddrs + return nil +} + +func (sw *Switch) isPeerPersistentFn() func(*NetAddress) bool { + return func(na *NetAddress) bool { + for _, pa := range sw.persistentPeersAddrs { + if pa.Equals(na) { + return true + } + } + return false + } +} + func (sw *Switch) acceptRoutine() { for { p, err := sw.transport.Accept(peerConfig{ @@ -478,16 +576,15 @@ func (sw *Switch) acceptRoutine() { onPeerError: sw.StopPeerForError, reactorsByCh: sw.reactorsByCh, metrics: sw.metrics, + isPersistent: sw.isPeerPersistentFn(), }) if err != nil { - switch err.(type) { + switch err := err.(type) { case ErrRejected: - rErr := err.(ErrRejected) - - if rErr.IsSelf() { + if err.IsSelf() { // Remove the given address from the address book and add to our addresses // to avoid dialing in the future. - addr := rErr.Addr() + addr := err.Addr() sw.addrBook.RemoveAddress(&addr) sw.addrBook.AddOurAddress(&addr) } @@ -559,13 +656,12 @@ func (sw *Switch) acceptRoutine() { // dial the peer; make secret connection; authenticate against the dialed ID; // add the peer. -// if dialing fails, start the reconnect loop. If handhsake fails, its over. -// If peer is started succesffuly, reconnectLoop will start when -// StopPeerForError is called +// if dialing fails, start the reconnect loop. If handshake fails, it's over. +// If peer is started successfully, reconnectLoop will start when +// StopPeerForError is called. func (sw *Switch) addOutboundPeerWithConfig( addr *NetAddress, cfg *config.P2PConfig, - persistent bool, ) error { sw.Logger.Info("Dialing peer", "address", addr) @@ -578,7 +674,7 @@ func (sw *Switch) addOutboundPeerWithConfig( p, err := sw.transport.Dial(*addr, peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, - persistent: persistent, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, metrics: sw.metrics, }) @@ -597,7 +693,7 @@ func (sw *Switch) addOutboundPeerWithConfig( // retry persistent peers after // any dial error besides IsSelf() - if persistent { + if sw.isPeerPersistentFn()(addr) { go sw.reconnectToPeer(addr) } @@ -660,6 +756,11 @@ func (sw *Switch) addPeer(p Peer) error { return nil } + // Add some data to the peer, which is required by reactors. + for _, reactor := range sw.reactors { + p = reactor.InitPeer(p) + } + // Start the peer's send/recv routines. // Must start it before adding it to the peer set // to prevent Start and Stop from being called concurrently. diff --git a/p2p/switch_test.go b/p2p/switch_test.go index b0be305991f..0879acc2df5 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -12,11 +12,11 @@ import ( "regexp" "strconv" "sync" + "sync/atomic" "testing" "time" - stdprometheus "github.com/prometheus/client_golang/prometheus" - + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -168,7 +168,7 @@ func TestSwitchFiltersOutItself(t *testing.T) { rp.Start() // addr should be rejected in addPeer based on the same ID - err := s1.DialPeerWithAddress(rp.Addr(), false) + err := s1.DialPeerWithAddress(rp.Addr()) if assert.Error(t, err) { if err, ok := err.(ErrRejected); ok { if !err.IsSelf() { @@ -213,6 +213,7 @@ func TestSwitchPeerFilter(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) if err != nil { @@ -257,6 +258,7 @@ func TestSwitchPeerFilterTimeout(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) if err != nil { @@ -271,9 +273,8 @@ func TestSwitchPeerFilterTimeout(t *testing.T) { func TestSwitchPeerFilterDuplicate(t *testing.T) { sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) - err := sw.Start() + sw.Start() defer sw.Stop() - require.NoError(t, err) // simulate remote peer rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} @@ -283,6 +284,7 @@ func TestSwitchPeerFilterDuplicate(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) if err != nil { @@ -328,6 +330,7 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) require.Nil(err) @@ -345,7 +348,7 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { } func TestSwitchStopPeerForError(t *testing.T) { - s := httptest.NewServer(stdprometheus.UninstrumentedHandler()) + s := httptest.NewServer(promhttp.Handler()) defer s.Close() scrapeMetrics := func() string { @@ -392,49 +395,32 @@ func TestSwitchStopPeerForError(t *testing.T) { assert.EqualValues(t, 0, peersMetricValue()) } -func TestSwitchReconnectsToPersistentPeer(t *testing.T) { - assert, require := assert.New(t), require.New(t) - +func TestSwitchReconnectsToOutboundPersistentPeer(t *testing.T) { sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) err := sw.Start() - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer sw.Stop() - // simulate remote peer + // 1. simulate failure by closing connection rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} rp.Start() defer rp.Stop() - p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ - chDescs: sw.chDescs, - onPeerError: sw.StopPeerForError, - persistent: true, - reactorsByCh: sw.reactorsByCh, - }) - require.Nil(err) - - require.Nil(sw.addPeer(p)) + err = sw.AddPersistentPeers([]string{rp.Addr().String()}) + require.NoError(t, err) - require.NotNil(sw.Peers().Get(rp.ID())) + err = sw.DialPeerWithAddress(rp.Addr()) + require.Nil(t, err) + require.NotNil(t, sw.Peers().Get(rp.ID())) - // simulate failure by closing connection + p := sw.Peers().List()[0] p.(*peer).CloseConn() - // TODO: remove sleep, detect the disconnection, wait for reconnect - npeers := sw.Peers().Size() - for i := 0; i < 20; i++ { - time.Sleep(250 * time.Millisecond) - npeers = sw.Peers().Size() - if npeers > 0 { - break - } - } - assert.NotZero(npeers) - assert.False(p.IsRunning()) + waitUntilSwitchHasAtLeastNPeers(sw, 1) + assert.False(t, p.IsRunning()) // old peer instance + assert.Equal(t, 1, sw.Peers().Size()) // new peer instance - // simulate another remote peer + // 2. simulate first time dial failure rp = &remotePeer{ PrivKey: ed25519.GenPrivKey(), Config: cfg, @@ -445,23 +431,68 @@ func TestSwitchReconnectsToPersistentPeer(t *testing.T) { rp.Start() defer rp.Stop() - // simulate first time dial failure conf := config.DefaultP2PConfig() - conf.TestDialFail = true - err = sw.addOutboundPeerWithConfig(rp.Addr(), conf, true) - require.NotNil(err) - + conf.TestDialFail = true // will trigger a reconnect + err = sw.addOutboundPeerWithConfig(rp.Addr(), conf) + require.NotNil(t, err) // DialPeerWithAddres - sw.peerConfig resets the dialer + waitUntilSwitchHasAtLeastNPeers(sw, 2) + assert.Equal(t, 2, sw.Peers().Size()) +} + +func TestSwitchReconnectsToInboundPersistentPeer(t *testing.T) { + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + // 1. simulate failure by closing the connection + rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} + rp.Start() + defer rp.Stop() + + err = sw.AddPersistentPeers([]string{rp.Addr().String()}) + require.NoError(t, err) + + conn, err := rp.Dial(sw.NetAddress()) + require.NoError(t, err) + time.Sleep(50 * time.Millisecond) + require.NotNil(t, sw.Peers().Get(rp.ID())) + + conn.Close() + + waitUntilSwitchHasAtLeastNPeers(sw, 1) + assert.Equal(t, 1, sw.Peers().Size()) +} + +func TestSwitchDialPeersAsync(t *testing.T) { + if testing.Short() { + return + } + + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} + rp.Start() + defer rp.Stop() + + err = sw.DialPeersAsync([]string{rp.Addr().String()}) + require.NoError(t, err) + time.Sleep(dialRandomizerIntervalMilliseconds * time.Millisecond) + require.NotNil(t, sw.Peers().Get(rp.ID())) +} - // TODO: same as above +func waitUntilSwitchHasAtLeastNPeers(sw *Switch, n int) { for i := 0; i < 20; i++ { time.Sleep(250 * time.Millisecond) - npeers = sw.Peers().Size() - if npeers > 1 { + has := sw.Peers().Size() + if has >= n { break } } - assert.EqualValues(2, npeers) } func TestSwitchFullConnectivity(t *testing.T) { @@ -563,6 +594,7 @@ func TestSwitchAcceptRoutineErrorCases(t *testing.T) { assert.NoError(t, err) sw.Stop() }) + // TODO(melekes) check we remove our address from addrBook sw = NewSwitch(cfg, errorTransport{ErrTransportClosed{}}) assert.NotPanics(t, func() { @@ -572,6 +604,71 @@ func TestSwitchAcceptRoutineErrorCases(t *testing.T) { }) } +// mockReactor checks that InitPeer never called before RemovePeer. If that's +// not true, InitCalledBeforeRemoveFinished will return true. +type mockReactor struct { + *BaseReactor + + // atomic + removePeerInProgress uint32 + initCalledBeforeRemoveFinished uint32 +} + +func (r *mockReactor) RemovePeer(peer Peer, reason interface{}) { + atomic.StoreUint32(&r.removePeerInProgress, 1) + defer atomic.StoreUint32(&r.removePeerInProgress, 0) + time.Sleep(100 * time.Millisecond) +} + +func (r *mockReactor) InitPeer(peer Peer) Peer { + if atomic.LoadUint32(&r.removePeerInProgress) == 1 { + atomic.StoreUint32(&r.initCalledBeforeRemoveFinished, 1) + } + + return peer +} + +func (r *mockReactor) InitCalledBeforeRemoveFinished() bool { + return atomic.LoadUint32(&r.initCalledBeforeRemoveFinished) == 1 +} + +// see stopAndRemovePeer +func TestSwitchInitPeerIsNotCalledBeforeRemovePeer(t *testing.T) { + // make reactor + reactor := &mockReactor{} + reactor.BaseReactor = NewBaseReactor("mockReactor", reactor) + + // make switch + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", func(i int, sw *Switch) *Switch { + sw.AddReactor("mock", reactor) + return sw + }) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + // add peer + rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} + rp.Start() + defer rp.Stop() + _, err = rp.Dial(sw.NetAddress()) + require.NoError(t, err) + // wait till the switch adds rp to the peer set + time.Sleep(50 * time.Millisecond) + + // stop peer asynchronously + go sw.StopPeerForError(sw.Peers().Get(rp.ID()), "test") + + // simulate peer reconnecting to us + _, err = rp.Dial(sw.NetAddress()) + require.NoError(t, err) + // wait till the switch adds rp to the peer set + time.Sleep(50 * time.Millisecond) + + // make sure reactor.RemovePeer is finished before InitPeer is called + assert.False(t, reactor.InitCalledBeforeRemoveFinished()) +} + func BenchmarkSwitchBroadcast(b *testing.B) { s1, s2 := MakeSwitchPair(b, func(i int, sw *Switch) *Switch { // Make bar reactors of bar channels each diff --git a/p2p/test_util.go b/p2p/test_util.go index bf485cd6b80..fa175aeb4d3 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -27,7 +27,7 @@ func (ni mockNodeInfo) NetAddress() (*NetAddress, error) { return ni.addr, ni func (ni mockNodeInfo) Validate() error { return nil } func (ni mockNodeInfo) CompatibleWith(other NodeInfo) error { return nil } -func AddPeerToSwitch(sw *Switch, peer Peer) { +func AddPeerToSwitchPeerSet(sw *Switch, peer Peer) { sw.peers.Add(peer) } @@ -248,35 +248,25 @@ func testNodeInfo(id ID, name string) NodeInfo { } func testNodeInfoWithNetwork(id ID, name, network string) NodeInfo { - port, err := getFreePort() - if err != nil { - panic(err) - } return DefaultNodeInfo{ ProtocolVersion: defaultProtocolVersion, ID_: id, - ListenAddr: fmt.Sprintf("127.0.0.1:%d", port), + ListenAddr: fmt.Sprintf("127.0.0.1:%d", getFreePort()), Network: network, Version: "1.2.3-rc0-deadbeef", Channels: []byte{testCh}, Moniker: name, Other: DefaultNodeInfoOther{ TxIndex: "on", - RPCAddress: fmt.Sprintf("127.0.0.1:%d", port), + RPCAddress: fmt.Sprintf("127.0.0.1:%d", getFreePort()), }, } } -func getFreePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") +func getFreePort() int { + port, err := cmn.GetFreePort() if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err + panic(err) } - defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil + return port } diff --git a/p2p/transport.go b/p2p/transport.go index aa89d1a92be..8d6ea236ec9 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -37,11 +37,15 @@ type accept struct { // events. // TODO(xla): Refactor out with more static Reactor setup and PeerBehaviour. type peerConfig struct { - chDescs []*conn.ChannelDescriptor - onPeerError func(Peer, interface{}) - outbound, persistent bool - reactorsByCh map[byte]Reactor - metrics *Metrics + chDescs []*conn.ChannelDescriptor + onPeerError func(Peer, interface{}) + outbound bool + // isPersistent allows you to set a function, which, given socket address + // (for outbound peers) OR self-reported address (for inbound peers), tells + // if the peer is persistent or not. + isPersistent func(*NetAddress) bool + reactorsByCh map[byte]Reactor + metrics *Metrics } // Transport emits and connects to Peers. The implementation of Peer is left to @@ -204,7 +208,7 @@ func (mt *MultiplexTransport) Dial( return nil, err } - secretConn, nodeInfo, err := mt.upgrade(c) + secretConn, nodeInfo, err := mt.upgrade(c, &addr) if err != nil { return nil, err } @@ -274,7 +278,7 @@ func (mt *MultiplexTransport) acceptPeers() { err := mt.filterConn(c) if err == nil { - secretConn, nodeInfo, err = mt.upgrade(c) + secretConn, nodeInfo, err = mt.upgrade(c, nil) if err == nil { addr := c.RemoteAddr() id := PubKeyToID(secretConn.RemotePubKey()) @@ -296,9 +300,9 @@ func (mt *MultiplexTransport) acceptPeers() { // Cleanup removes the given address from the connections set and // closes the connection. -func (mt *MultiplexTransport) Cleanup(peer Peer) { - mt.conns.RemoveAddr(peer.RemoteAddr()) - _ = peer.CloseConn() +func (mt *MultiplexTransport) Cleanup(p Peer) { + mt.conns.RemoveAddr(p.RemoteAddr()) + _ = p.CloseConn() } func (mt *MultiplexTransport) cleanup(c net.Conn) error { @@ -352,6 +356,7 @@ func (mt *MultiplexTransport) filterConn(c net.Conn) (err error) { func (mt *MultiplexTransport) upgrade( c net.Conn, + dialedAddr *NetAddress, ) (secretConn *conn.SecretConnection, nodeInfo NodeInfo, err error) { defer func() { if err != nil { @@ -363,11 +368,28 @@ func (mt *MultiplexTransport) upgrade( if err != nil { return nil, nil, ErrRejected{ conn: c, - err: fmt.Errorf("secrect conn failed: %v", err), + err: fmt.Errorf("secret conn failed: %v", err), isAuthFailure: true, } } + // For outgoing conns, ensure connection key matches dialed key. + connID := PubKeyToID(secretConn.RemotePubKey()) + if dialedAddr != nil { + if dialedID := dialedAddr.ID; connID != dialedID { + return nil, nil, ErrRejected{ + conn: c, + id: connID, + err: fmt.Errorf( + "conn.ID (%v) dialed ID (%v) mismatch", + connID, + dialedID, + ), + isAuthFailure: true, + } + } + } + nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo) if err != nil { return nil, nil, ErrRejected{ @@ -386,12 +408,12 @@ func (mt *MultiplexTransport) upgrade( } // Ensure connection key matches self reported key. - if connID := PubKeyToID(secretConn.RemotePubKey()); connID != nodeInfo.ID() { + if connID != nodeInfo.ID() { return nil, nil, ErrRejected{ conn: c, id: connID, err: fmt.Errorf( - "conn.ID (%v) NodeInfo.ID (%v) missmatch", + "conn.ID (%v) NodeInfo.ID (%v) mismatch", connID, nodeInfo.ID(), ), @@ -428,9 +450,21 @@ func (mt *MultiplexTransport) wrapPeer( socketAddr *NetAddress, ) Peer { + persistent := false + if cfg.isPersistent != nil { + if cfg.outbound { + persistent = cfg.isPersistent(socketAddr) + } else { + selfReportedAddr, err := ni.NetAddress() + if err == nil { + persistent = cfg.isPersistent(selfReportedAddr) + } + } + } + peerConn := newPeerConn( cfg.outbound, - cfg.persistent, + persistent, c, socketAddr, ) diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 182b2889986..7580f025960 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -37,6 +37,7 @@ func TestTransportMultiplexConnFilter(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) + id := mt.nodeKey.ID() MultiplexTransportConnFilters( func(_ ConnSet, _ net.Conn, _ []net.IP) error { return nil }, @@ -46,7 +47,7 @@ func TestTransportMultiplexConnFilter(t *testing.T) { }, )(mt) - addr, err := NewNetAddressStringWithOptionalID("127.0.0.1:0") + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } @@ -58,13 +59,9 @@ func TestTransportMultiplexConnFilter(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(id, mt.listener.Addr()) - _, err = addr.Dial() + _, err := addr.Dial() if err != nil { errc <- err return @@ -94,6 +91,7 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) + id := mt.nodeKey.ID() MultiplexTransportFilterTimeout(5 * time.Millisecond)(mt) MultiplexTransportConnFilters( @@ -103,7 +101,7 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { }, )(mt) - addr, err := NewNetAddressStringWithOptionalID("127.0.0.1:0") + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } @@ -115,13 +113,9 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(id, mt.listener.Addr()) - _, err = addr.Dial() + _, err := addr.Dial() if err != nil { errc <- err return @@ -142,44 +136,21 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { func TestTransportMultiplexAcceptMultiple(t *testing.T) { mt := testSetupMultiplexTransport(t) + laddr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) var ( - seed = rand.New(rand.NewSource(time.Now().UnixNano())) - errc = make(chan error, seed.Intn(64)+64) + seed = rand.New(rand.NewSource(time.Now().UnixNano())) + nDialers = seed.Intn(64) + 64 + errc = make(chan error, nDialers) ) // Setup dialers. - for i := 0; i < cap(errc); i++ { - go func() { - var ( - pv = ed25519.GenPrivKey() - dialer = newMultiplexTransport( - testNodeInfo(PubKeyToID(pv.PubKey()), defaultNodeName), - NodeKey{ - PrivKey: pv, - }, - ) - ) - - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } - - _, err = dialer.Dial(*addr, peerConfig{}) - if err != nil { - errc <- err - return - } - - // Signal that the connection was established. - errc <- nil - }() + for i := 0; i < nDialers; i++ { + go testDialer(*laddr, errc) } // Catch connection errors. - for i := 0; i < cap(errc); i++ { + for i := 0; i < nDialers; i++ { if err := <-errc; err != nil { t.Fatal(err) } @@ -217,6 +188,27 @@ func TestTransportMultiplexAcceptMultiple(t *testing.T) { } } +func testDialer(dialAddr NetAddress, errc chan error) { + var ( + pv = ed25519.GenPrivKey() + dialer = newMultiplexTransport( + testNodeInfo(PubKeyToID(pv.PubKey()), defaultNodeName), + NodeKey{ + PrivKey: pv, + }, + ) + ) + + _, err := dialer.Dial(dialAddr, peerConfig{}) + if err != nil { + errc <- err + return + } + + // Signal that the connection was established. + errc <- nil +} + func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { mt := testSetupMultiplexTransport(t) @@ -230,11 +222,7 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { // Simulate slow Peer. go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) c, err := addr.Dial() if err != nil { @@ -281,14 +269,9 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { }, ) ) + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } - - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -328,13 +311,9 @@ func TestTransportMultiplexValidateNodeInfo(t *testing.T) { ) ) - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -371,14 +350,9 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } - - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -401,6 +375,35 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) { } } +func TestTransportMultiplexDialRejectWrongID(t *testing.T) { + mt := testSetupMultiplexTransport(t) + + var ( + pv = ed25519.GenPrivKey() + dialer = newMultiplexTransport( + testNodeInfo(PubKeyToID(pv.PubKey()), ""), // Should not be empty + NodeKey{ + PrivKey: pv, + }, + ) + ) + + wrongID := PubKeyToID(ed25519.GenPrivKey().PubKey()) + addr := NewNetAddress(wrongID, mt.listener.Addr()) + + _, err := dialer.Dial(*addr, peerConfig{}) + if err != nil { + t.Logf("connection failed: %v", err) + if err, ok := err.(ErrRejected); ok { + if !err.IsAuthFailure() { + t.Errorf("expected auth failure") + } + } else { + t.Errorf("expected ErrRejected") + } + } +} + func TestTransportMultiplexRejectIncompatible(t *testing.T) { mt := testSetupMultiplexTransport(t) @@ -416,14 +419,9 @@ func TestTransportMultiplexRejectIncompatible(t *testing.T) { }, ) ) + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } - - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -448,13 +446,9 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = mt.Dial(*addr, peerConfig{}) + _, err := mt.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -466,7 +460,7 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { if err := <-errc; err != nil { if err, ok := err.(ErrRejected); ok { if !err.IsSelf() { - t.Errorf("expected to reject self") + t.Errorf("expected to reject self, got: %v", err) } } else { t.Errorf("expected ErrRejected") @@ -478,7 +472,7 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { _, err := mt.Accept(peerConfig{}) if err, ok := err.(ErrRejected); ok { if !err.IsSelf() { - t.Errorf("expected to reject self") + t.Errorf("expected to reject self, got: %v", err) } } else { t.Errorf("expected ErrRejected") @@ -498,13 +492,13 @@ func TestTransportConnDuplicateIPFilter(t *testing.T) { ) cs.Set(c, []net.IP{ - net.IP{10, 0, 10, 1}, - net.IP{10, 0, 10, 2}, - net.IP{10, 0, 10, 3}, + {10, 0, 10, 1}, + {10, 0, 10, 2}, + {10, 0, 10, 3}, }) if err := filter(cs, c, []net.IP{ - net.IP{10, 0, 10, 2}, + {10, 0, 10, 2}, }); err == nil { t.Errorf("expected Peer to be rejected as duplicate") } @@ -563,12 +557,14 @@ func TestTransportHandshake(t *testing.T) { } } +// create listener func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport { var ( pv = ed25519.GenPrivKey() + id = PubKeyToID(pv.PubKey()) mt = newMultiplexTransport( testNodeInfo( - PubKeyToID(pv.PubKey()), "transport", + id, "transport", ), NodeKey{ PrivKey: pv, @@ -576,7 +572,7 @@ func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport { ) ) - addr, err := NewNetAddressStringWithOptionalID("127.0.0.1:0") + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } diff --git a/p2p/trust/metric_test.go b/p2p/trust/metric_test.go index f690ce5575b..0273dad698f 100644 --- a/p2p/trust/metric_test.go +++ b/p2p/trust/metric_test.go @@ -65,6 +65,7 @@ func TestTrustMetricCopyNilPointer(t *testing.T) { } // XXX: This test fails non-deterministically +//nolint:unused,deadcode func _TestTrustMetricStopPause(t *testing.T) { // The TestTicker will provide manual control over // the passing of time within the metric diff --git a/p2p/trust/store.go b/p2p/trust/store.go index d6b4c049d51..b0324a1a700 100644 --- a/p2p/trust/store.go +++ b/p2p/trust/store.go @@ -10,7 +10,7 @@ import ( "time" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" + dbm "github.com/tendermint/tm-db" ) const defaultStorePeriodicSaveInterval = 1 * time.Minute @@ -156,7 +156,7 @@ func (tms *TrustMetricStore) loadFromDB() bool { peers := make(map[string]MetricHistoryJSON) err := json.Unmarshal(bytes, &peers) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Could not unmarshal Trust Metric Store DB data: %v", err)) + panic(fmt.Sprintf("Could not unmarshal Trust Metric Store DB data: %v", err)) } // If history data exists in the file, diff --git a/p2p/trust/store_test.go b/p2p/trust/store_test.go index e1bea863615..d6498d82310 100644 --- a/p2p/trust/store_test.go +++ b/p2p/trust/store_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" ) func TestTrustMetricStoreSaveLoad(t *testing.T) { diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go index d53974fc4cf..89f35c5dfce 100644 --- a/p2p/upnp/upnp.go +++ b/p2p/upnp/upnp.go @@ -197,7 +197,7 @@ func localIPv4() (net.IP, error) { } func getServiceURL(rootURL string) (url, urnDomain string, err error) { - r, err := http.Get(rootURL) + r, err := http.Get(rootURL) // nolint: gosec if err != nil { return } diff --git a/rpc/core/net.go b/rpc/core/net.go index 4d95c2ef007..6dba1583ec0 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -73,7 +73,7 @@ func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { } // starts go routines to dial each peer after random delays logger.Info("DialSeeds", "addrBook", addrBook, "seeds", seeds) - err := p2pPeers.DialPeersAsync(addrBook, seeds, false) + err := p2pPeers.DialPeersAsync(seeds) if err != nil { return &ctypes.ResultDialSeeds{}, err } @@ -86,7 +86,7 @@ func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, } // starts go routines to dial each peer after random delays logger.Info("DialPeers", "addrBook", addrBook, "peers", peers, "persistent", persistent) - err := p2pPeers.DialPeersAsync(addrBook, peers, persistent) + err := p2pPeers.DialPeersAsync(peers) if err != nil { return &ctypes.ResultDialPeers{}, err } diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 23649544372..bb5ed34d17e 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -40,7 +40,8 @@ type transport interface { } type peers interface { - DialPeersAsync(p2p.AddrBook, []string, bool) error + AddPersistentPeers([]string) error + DialPeersAsync([]string) error NumPeers() (outbound, inbound, dialig int) Peers() p2p.IPeerSet } From 621185189375623858c68dacc913dd6937d091f1 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Mon, 26 Aug 2019 18:16:39 +0800 Subject: [PATCH 06/31] Remove db and add dependency tendermint/tm-db --- abci/example/kvstore/kvstore.go | 2 +- abci/example/kvstore/persistent_kvstore.go | 2 +- blockchain/reactor_test.go | 3 +- blockchain/store.go | 7 +- blockchain/store_test.go | 12 +- config/config.go | 15 +- config/toml.go | 13 +- consensus/common_test.go | 10 +- consensus/reactor_test.go | 5 +- consensus/replay.go | 6 +- consensus/replay_file.go | 3 +- consensus/replay_test.go | 8 +- consensus/wal_generator.go | 2 +- evidence/pool.go | 5 +- evidence/pool_test.go | 3 +- evidence/reactor_test.go | 3 +- evidence/store.go | 4 +- evidence/store_test.go | 2 +- go.mod | 23 +- go.sum | 62 +- libs/db/backend_test.go | 222 ----- libs/db/c_level_db.go | 318 ------- libs/db/c_level_db_test.go | 101 --- libs/db/common_test.go | 190 ----- libs/db/db.go | 54 -- libs/db/db_test.go | 206 ----- libs/db/debug_db.go | 257 ------ libs/db/fsdb.go | 270 ------ libs/db/go_level_db.go | 336 -------- libs/db/go_level_db_test.go | 106 --- libs/db/mem_batch.go | 76 -- libs/db/mem_db.go | 255 ------ libs/db/prefix_db.go | 336 -------- libs/db/prefix_db_test.go | 192 ----- libs/db/remotedb/doc.go | 37 - libs/db/remotedb/grpcdb/client.go | 30 - libs/db/remotedb/grpcdb/doc.go | 32 - libs/db/remotedb/grpcdb/example_test.go | 52 -- libs/db/remotedb/grpcdb/server.go | 200 ----- libs/db/remotedb/proto/defs.pb.go | 914 --------------------- libs/db/remotedb/proto/defs.proto | 71 -- libs/db/remotedb/remotedb.go | 266 ------ libs/db/remotedb/remotedb_test.go | 123 --- libs/db/remotedb/test.crt | 25 - libs/db/remotedb/test.key | 27 - libs/db/types.go | 136 --- libs/db/util.go | 45 - libs/db/util_test.go | 99 --- lite/dbprovider.go | 2 +- lite/dynamic_verifier_test.go | 5 +- lite/provider_test.go | 3 +- lite/proxy/verifier.go | 4 +- node/node.go | 5 +- p2p/trust/store.go | 2 +- p2p/trust/store_test.go | 2 +- rpc/core/pipe.go | 2 +- state/execution.go | 14 +- state/execution_test.go | 6 +- state/state_test.go | 6 +- state/store.go | 2 +- state/tx_filter_test.go | 2 +- state/txindex/kv/kv.go | 2 +- state/txindex/kv/kv_test.go | 2 +- state/validation.go | 4 +- 64 files changed, 129 insertions(+), 5100 deletions(-) delete mode 100644 libs/db/backend_test.go delete mode 100644 libs/db/c_level_db.go delete mode 100644 libs/db/c_level_db_test.go delete mode 100644 libs/db/common_test.go delete mode 100644 libs/db/db.go delete mode 100644 libs/db/db_test.go delete mode 100644 libs/db/debug_db.go delete mode 100644 libs/db/fsdb.go delete mode 100644 libs/db/go_level_db.go delete mode 100644 libs/db/go_level_db_test.go delete mode 100644 libs/db/mem_batch.go delete mode 100644 libs/db/mem_db.go delete mode 100644 libs/db/prefix_db.go delete mode 100644 libs/db/prefix_db_test.go delete mode 100644 libs/db/remotedb/doc.go delete mode 100644 libs/db/remotedb/grpcdb/client.go delete mode 100644 libs/db/remotedb/grpcdb/doc.go delete mode 100644 libs/db/remotedb/grpcdb/example_test.go delete mode 100644 libs/db/remotedb/grpcdb/server.go delete mode 100644 libs/db/remotedb/proto/defs.pb.go delete mode 100644 libs/db/remotedb/proto/defs.proto delete mode 100644 libs/db/remotedb/remotedb.go delete mode 100644 libs/db/remotedb/remotedb_test.go delete mode 100644 libs/db/remotedb/test.crt delete mode 100644 libs/db/remotedb/test.key delete mode 100644 libs/db/types.go delete mode 100644 libs/db/util.go delete mode 100644 libs/db/util_test.go diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 955baefb4db..8bfe204f7de 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -9,8 +9,8 @@ import ( "github.com/tendermint/tendermint/abci/example/code" "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/version" + dbm "github.com/tendermint/tm-db" ) var ( diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index f969eebfefa..9f3dce6a15d 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -9,8 +9,8 @@ import ( "github.com/tendermint/tendermint/abci/example/code" "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" ) const ( diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index f6c29d65d8f..667df2988d6 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -6,17 +6,16 @@ import ( "time" "github.com/stretchr/testify/assert" - abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) var config *cfg.Config diff --git a/blockchain/store.go b/blockchain/store.go index 4b48eff4693..4bdfdaf2217 100644 --- a/blockchain/store.go +++ b/blockchain/store.go @@ -5,9 +5,8 @@ import ( "sync" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) /* @@ -189,9 +188,9 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s func (bs *BlockStore) RetreatLastBlock() { height := bs.height bs.db.Delete(calcBlockMetaKey(height)) - bs.db.Delete(calcBlockCommitKey(height-1)) + bs.db.Delete(calcBlockCommitKey(height - 1)) bs.db.Delete(calcSeenCommitKey(height)) - BlockStoreStateJSON{Height: height-1 }.Save(bs.db) + BlockStoreStateJSON{Height: height - 1}.Save(bs.db) // Done! bs.mtx.Lock() bs.height = height diff --git a/blockchain/store_test.go b/blockchain/store_test.go index a52039fa46d..c0021bd7295 100644 --- a/blockchain/store_test.go +++ b/blockchain/store_test.go @@ -11,13 +11,11 @@ import ( "github.com/stretchr/testify/require" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/db" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" sm "github.com/tendermint/tendermint/state" - "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { @@ -34,7 +32,7 @@ func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { } func TestLoadBlockStoreStateJSON(t *testing.T) { - db := db.NewMemDB() + db := dbm.NewMemDB() bsj := &BlockStoreStateJSON{Height: 1000} bsj.Save(db) @@ -45,7 +43,7 @@ func TestLoadBlockStoreStateJSON(t *testing.T) { } func TestNewBlockStore(t *testing.T) { - db := db.NewMemDB() + db := dbm.NewMemDB() db.Set(blockStoreKey, []byte(`{"height": "10000"}`)) bs := NewBlockStore(db) require.Equal(t, int64(10000), bs.Height(), "failed to properly parse blockstore") @@ -74,8 +72,8 @@ func TestNewBlockStore(t *testing.T) { assert.Equal(t, bs.Height(), int64(0), "expecting nil bytes to be unmarshaled alright") } -func freshBlockStore() (*BlockStore, db.DB) { - db := db.NewMemDB() +func freshBlockStore() (*BlockStore, dbm.DB) { + db := dbm.NewMemDB() return NewBlockStore(db), db } diff --git a/config/config.go b/config/config.go index 02188e1f03d..36dfe318849 100644 --- a/config/config.go +++ b/config/config.go @@ -151,7 +151,18 @@ type BaseConfig struct { ReplayHeight int64 `mapstructure:"replay_height"` - // Database backend: leveldb | memdb | cleveldb + // Database backend: goleveldb | cleveldb | boltdb + // * goleveldb (github.com/syndtr/goleveldb - most popular implementation) + // - pure go + // - stable + // * cleveldb (uses levigo wrapper) + // - fast + // - requires gcc + // - use cleveldb build tag (go build -tags cleveldb) + // * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) + // - EXPERIMENTAL + // - may be faster is some use-cases (random reads - indexer) + // - use boltdb build tag (go build -tags boltdb) DBBackend string `mapstructure:"db_backend"` // Database directory @@ -203,7 +214,7 @@ func DefaultBaseConfig() BaseConfig { Deprecated: false, ReplayHeight: -1, FilterPeers: false, - DBBackend: "leveldb", + DBBackend: "goleveldb", DBPath: "data", } } diff --git a/config/toml.go b/config/toml.go index 7c39322e4a3..01919ad8e24 100644 --- a/config/toml.go +++ b/config/toml.go @@ -82,7 +82,18 @@ fast_sync = {{ .BaseConfig.FastSync }} # shutdown deprecated = {{ .BaseConfig.Deprecated }} -# Database backend: leveldb | memdb | cleveldb +# Database backend: goleveldb | cleveldb | boltdb +# * goleveldb (github.com/syndtr/goleveldb - most popular implementation) +# - pure go +# - stable +# * cleveldb (uses levigo wrapper) +# - fast +# - requires gcc +# - use cleveldb build tag (go build -tags cleveldb) +# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) +# - EXPERIMENTAL +# - may be faster is some use-cases (random reads - indexer) +# - use boltdb build tag (go build -tags boltdb) db_backend = "{{ .BaseConfig.DBBackend }}" # Database directory diff --git a/consensus/common_test.go b/consensus/common_test.go index e8bb0dbe9a7..69cc700f0c7 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -13,13 +13,15 @@ import ( "testing" "time" + "github.com/go-kit/kit/log/term" abcicli "github.com/tendermint/tendermint/abci/client" + "github.com/tendermint/tendermint/abci/example/counter" + "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" cstypes "github.com/tendermint/tendermint/consensus/types" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" @@ -27,11 +29,7 @@ import ( sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - - "github.com/tendermint/tendermint/abci/example/counter" - "github.com/tendermint/tendermint/abci/example/kvstore" - - "github.com/go-kit/kit/log/term" + dbm "github.com/tendermint/tm-db" ) const ( diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index be3f607e4f7..b3ea0d34cd2 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -13,18 +13,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/abci/client" + abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) func init() { diff --git a/consensus/replay.go b/consensus/replay.go index f1e5458c31d..23c1a76832f 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -7,21 +7,17 @@ import ( "io" "reflect" "runtime" - //"strconv" - //"strings" "time" abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" - //auto "github.com/tendermint/tendermint/libs/autofile" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/version" + dbm "github.com/tendermint/tm-db" ) var crc32c = crc32.MakeTable(crc32.Castagnoli) diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 45036a3e553..60ab9346657 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -10,15 +10,14 @@ import ( "strings" "github.com/pkg/errors" - bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) const ( diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 4233ef44e61..cfd9ba64487 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -14,20 +14,18 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" auto "github.com/tendermint/tendermint/libs/autofile" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/version" - - cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" + dbm "github.com/tendermint/tm-db" ) var consensusReplayConfig *cfg.Config diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 5ff597a52e2..912f80b3f99 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -16,12 +16,12 @@ import ( cfg "github.com/tendermint/tendermint/config" auto "github.com/tendermint/tendermint/libs/autofile" cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + db "github.com/tendermint/tm-db" ) // WALGenerateNBlocks generates a consensus WAL. It does this by spining up a diff --git a/evidence/pool.go b/evidence/pool.go index 318cc62b921..c2d4e023b38 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -4,12 +4,11 @@ import ( "fmt" "sync" - clist "github.com/tendermint/tendermint/libs/clist" - dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/clist" "github.com/tendermint/tendermint/libs/log" - sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) // EvidencePool maintains a pool of valid evidence diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 0640c1da70b..428318dfa0f 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -6,11 +6,10 @@ import ( "testing" "github.com/stretchr/testify/assert" - - dbm "github.com/tendermint/tendermint/libs/db" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) var mockState = sm.State{} diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index 1c4e731abc8..3d5f0ea5b1b 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -8,13 +8,12 @@ import ( "github.com/go-kit/kit/log/term" "github.com/stretchr/testify/assert" - cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) // evidenceLogger is a TestingLogger which uses a different diff --git a/evidence/store.go b/evidence/store.go index cc58e6ca2e2..8b07acb7e7a 100644 --- a/evidence/store.go +++ b/evidence/store.go @@ -3,8 +3,8 @@ package evidence import ( "fmt" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) /* @@ -104,7 +104,7 @@ func (store *EvidenceStore) listEvidence(prefixKey string, maxBytes int64) (evid val := iter.Value() count++ // In block validation, the evidence total size is calculated by (evidence quantity) * (maximum evidence size) - if maxBytes > 0 && count * int64(types.MaxEvidenceBytes) > maxBytes { + if maxBytes > 0 && count*int64(types.MaxEvidenceBytes) > maxBytes { return evidence } // types.MaxEvidenceBytes must be greater than len(val) diff --git a/evidence/store_test.go b/evidence/store_test.go index 35eb28d01f3..259b12d7e82 100644 --- a/evidence/store_test.go +++ b/evidence/store_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) //------------------------------------------- diff --git a/go.mod b/go.mod index 10065184e29..8a1301c741d 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,6 @@ module github.com/tendermint/tendermint require ( - github.com/BurntSushi/toml v0.3.1 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 @@ -10,22 +9,17 @@ require ( github.com/go-logfmt/logfmt v0.3.0 github.com/go-stack/stack v1.8.0 // indirect github.com/gogo/protobuf v1.1.1 - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect - github.com/golang/protobuf v1.2.0 - github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/golang/protobuf v1.3.2 github.com/google/gofuzz v1.0.0 // indirect github.com/gorilla/websocket v1.2.0 github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/magiconair/properties v1.8.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.1.2 // indirect - github.com/onsi/ginkgo v1.9.0 // indirect - github.com/onsi/gomega v1.6.0 // indirect github.com/pelletier/go-toml v1.2.0 // indirect - github.com/pkg/errors v0.8.0 + github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v0.9.1 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect @@ -38,14 +32,11 @@ require ( github.com/spf13/jwalterweatherman v1.0.0 // indirect github.com/spf13/pflag v1.0.3 // indirect github.com/spf13/viper v1.0.0 - github.com/stretchr/testify v1.2.2 - github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e + github.com/stretchr/testify v1.3.0 github.com/tendermint/btcd v0.1.1 github.com/tendermint/go-amino v0.14.1 - golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 - golang.org/x/net v0.0.0-20180906233101-161cd47e91fd - golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect - golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 // indirect - google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 // indirect - google.golang.org/grpc v1.13.0 + github.com/tendermint/tm-db v0.1.1 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 + golang.org/x/net v0.0.0-20190628185345-da137c7871d7 + google.golang.org/grpc v1.22.0 ) diff --git a/go.sum b/go.sum index 3c18143ca72..e0c486eac78 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= @@ -6,8 +7,11 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 h1:BsZAJgCuMsoFZMZNyj7Lyt6sS8anDhedVrAMCOyPMIo= github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +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/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -22,21 +26,24 @@ github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023 h1:y5P5G9cANJZt3MXlMrgELo5mNLZPXH8aGFFFG7IzPU0= -github.com/jmhodges/levigo v0.0.0-20161115193449-c42d9e0ca023/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -46,14 +53,12 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= -github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M= -github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= -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 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno= @@ -80,35 +85,48 @@ 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.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e h1:91EeXI4y4ShkyzkMqZ7QP/ZTIqwXp3RuDu5WFzxcFAs= -github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +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 v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= 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/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= +github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 h1:biUuj9O+0+XckRUCDzjoOGm6yFV5c0IHbm1ODP3e4Zw= -golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.13.0 h1:bHIbVsCwmvbArgCJmLdgOdHFXlKqTOVjbibbS19cXHc= -google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/libs/db/backend_test.go b/libs/db/backend_test.go deleted file mode 100644 index fb2a3d0b3fb..00000000000 --- a/libs/db/backend_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package db - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tendermint/libs/common" -) - -func cleanupDBDir(dir, name string) { - err := os.RemoveAll(filepath.Join(dir, name) + ".db") - if err != nil { - panic(err) - } -} - -func testBackendGetSetDelete(t *testing.T, backend DBBackendType) { - // Default - dirname, err := ioutil.TempDir("", fmt.Sprintf("test_backend_%s_", backend)) - require.Nil(t, err) - db := NewDB("testdb", backend, dirname) - defer cleanupDBDir(dirname, "testdb") - - // A nonexistent key should return nil, even if the key is empty - require.Nil(t, db.Get([]byte(""))) - - // A nonexistent key should return nil, even if the key is nil - require.Nil(t, db.Get(nil)) - - // A nonexistent key should return nil. - key := []byte("abc") - require.Nil(t, db.Get(key)) - - // Set empty value. - db.Set(key, []byte("")) - require.NotNil(t, db.Get(key)) - require.Empty(t, db.Get(key)) - - // Set nil value. - db.Set(key, nil) - require.NotNil(t, db.Get(key)) - require.Empty(t, db.Get(key)) - - // Delete. - db.Delete(key) - require.Nil(t, db.Get(key)) -} - -func TestBackendsGetSetDelete(t *testing.T) { - for dbType := range backends { - testBackendGetSetDelete(t, dbType) - } -} - -func withDB(t *testing.T, creator dbCreator, fn func(DB)) { - name := fmt.Sprintf("test_%x", cmn.RandStr(12)) - dir := os.TempDir() - db, err := creator(name, dir) - require.Nil(t, err) - defer cleanupDBDir(dir, name) - fn(db) - db.Close() -} - -func TestBackendsNilKeys(t *testing.T) { - - // Test all backends. - for dbType, creator := range backends { - withDB(t, creator, func(db DB) { - t.Run(fmt.Sprintf("Testing %s", dbType), func(t *testing.T) { - - // Nil keys are treated as the empty key for most operations. - expect := func(key, value []byte) { - if len(key) == 0 { // nil or empty - assert.Equal(t, db.Get(nil), db.Get([]byte(""))) - assert.Equal(t, db.Has(nil), db.Has([]byte(""))) - } - assert.Equal(t, db.Get(key), value) - assert.Equal(t, db.Has(key), value != nil) - } - - // Not set - expect(nil, nil) - - // Set nil value - db.Set(nil, nil) - expect(nil, []byte("")) - - // Set empty value - db.Set(nil, []byte("")) - expect(nil, []byte("")) - - // Set nil, Delete nil - db.Set(nil, []byte("abc")) - expect(nil, []byte("abc")) - db.Delete(nil) - expect(nil, nil) - - // Set nil, Delete empty - db.Set(nil, []byte("abc")) - expect(nil, []byte("abc")) - db.Delete([]byte("")) - expect(nil, nil) - - // Set empty, Delete nil - db.Set([]byte(""), []byte("abc")) - expect(nil, []byte("abc")) - db.Delete(nil) - expect(nil, nil) - - // Set empty, Delete empty - db.Set([]byte(""), []byte("abc")) - expect(nil, []byte("abc")) - db.Delete([]byte("")) - expect(nil, nil) - - // SetSync nil, DeleteSync nil - db.SetSync(nil, []byte("abc")) - expect(nil, []byte("abc")) - db.DeleteSync(nil) - expect(nil, nil) - - // SetSync nil, DeleteSync empty - db.SetSync(nil, []byte("abc")) - expect(nil, []byte("abc")) - db.DeleteSync([]byte("")) - expect(nil, nil) - - // SetSync empty, DeleteSync nil - db.SetSync([]byte(""), []byte("abc")) - expect(nil, []byte("abc")) - db.DeleteSync(nil) - expect(nil, nil) - - // SetSync empty, DeleteSync empty - db.SetSync([]byte(""), []byte("abc")) - expect(nil, []byte("abc")) - db.DeleteSync([]byte("")) - expect(nil, nil) - }) - }) - } -} - -func TestGoLevelDBBackend(t *testing.T) { - name := fmt.Sprintf("test_%x", cmn.RandStr(12)) - db := NewDB(name, GoLevelDBBackend, "") - defer cleanupDBDir("", name) - - _, ok := db.(*GoLevelDB) - assert.True(t, ok) -} - -func TestDBIterator(t *testing.T) { - for dbType := range backends { - t.Run(fmt.Sprintf("%v", dbType), func(t *testing.T) { - testDBIterator(t, dbType) - }) - } -} - -func testDBIterator(t *testing.T, backend DBBackendType) { - name := fmt.Sprintf("test_%x", cmn.RandStr(12)) - dir := os.TempDir() - db := NewDB(name, backend, dir) - defer cleanupDBDir(dir, name) - - for i := 0; i < 10; i++ { - if i != 6 { // but skip 6. - db.Set(int642Bytes(int64(i)), nil) - } - } - - verifyIterator(t, db.Iterator(nil, nil), []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator") - verifyIterator(t, db.ReverseIterator(nil, nil), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator") - - verifyIterator(t, db.Iterator(nil, int642Bytes(0)), []int64(nil), "forward iterator to 0") - verifyIterator(t, db.ReverseIterator(int642Bytes(10), nil), []int64(nil), "reverse iterator from 10 (ex)") - - verifyIterator(t, db.Iterator(int642Bytes(0), nil), []int64{0, 1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 0") - verifyIterator(t, db.Iterator(int642Bytes(1), nil), []int64{1, 2, 3, 4, 5, 7, 8, 9}, "forward iterator from 1") - verifyIterator(t, db.ReverseIterator(nil, int642Bytes(10)), []int64{9, 8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 10 (ex)") - verifyIterator(t, db.ReverseIterator(nil, int642Bytes(9)), []int64{8, 7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 9 (ex)") - verifyIterator(t, db.ReverseIterator(nil, int642Bytes(8)), []int64{7, 5, 4, 3, 2, 1, 0}, "reverse iterator from 8 (ex)") - - verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(6)), []int64{5}, "forward iterator from 5 to 6") - verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(7)), []int64{5}, "forward iterator from 5 to 7") - verifyIterator(t, db.Iterator(int642Bytes(5), int642Bytes(8)), []int64{5, 7}, "forward iterator from 5 to 8") - verifyIterator(t, db.Iterator(int642Bytes(6), int642Bytes(7)), []int64(nil), "forward iterator from 6 to 7") - verifyIterator(t, db.Iterator(int642Bytes(6), int642Bytes(8)), []int64{7}, "forward iterator from 6 to 8") - verifyIterator(t, db.Iterator(int642Bytes(7), int642Bytes(8)), []int64{7}, "forward iterator from 7 to 8") - - verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(5)), []int64{4}, "reverse iterator from 5 (ex) to 4") - verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(6)), []int64{5, 4}, "reverse iterator from 6 (ex) to 4") - verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(7)), []int64{5, 4}, "reverse iterator from 7 (ex) to 4") - verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(6)), []int64{5}, "reverse iterator from 6 (ex) to 5") - verifyIterator(t, db.ReverseIterator(int642Bytes(5), int642Bytes(7)), []int64{5}, "reverse iterator from 7 (ex) to 5") - verifyIterator(t, db.ReverseIterator(int642Bytes(6), int642Bytes(7)), []int64(nil), "reverse iterator from 7 (ex) to 6") - - verifyIterator(t, db.Iterator(int642Bytes(0), int642Bytes(1)), []int64{0}, "forward iterator from 0 to 1") - verifyIterator(t, db.ReverseIterator(int642Bytes(8), int642Bytes(9)), []int64{8}, "reverse iterator from 9 (ex) to 8") - - verifyIterator(t, db.Iterator(int642Bytes(2), int642Bytes(4)), []int64{2, 3}, "forward iterator from 2 to 4") - verifyIterator(t, db.Iterator(int642Bytes(4), int642Bytes(2)), []int64(nil), "forward iterator from 4 to 2") - verifyIterator(t, db.ReverseIterator(int642Bytes(2), int642Bytes(4)), []int64{3, 2}, "reverse iterator from 4 (ex) to 2") - verifyIterator(t, db.ReverseIterator(int642Bytes(4), int642Bytes(2)), []int64(nil), "reverse iterator from 2 (ex) to 4") - -} - -func verifyIterator(t *testing.T, itr Iterator, expected []int64, msg string) { - var list []int64 - for itr.Valid() { - list = append(list, bytes2Int64(itr.Key())) - itr.Next() - } - assert.Equal(t, expected, list, msg) -} diff --git a/libs/db/c_level_db.go b/libs/db/c_level_db.go deleted file mode 100644 index decb1af51dc..00000000000 --- a/libs/db/c_level_db.go +++ /dev/null @@ -1,318 +0,0 @@ -// +build gcc - -package db - -import ( - "bytes" - "fmt" - "path/filepath" - - "github.com/jmhodges/levigo" -) - -func init() { - dbCreator := func(name string, dir string) (DB, error) { - return NewCLevelDB(name, dir) - } - registerDBCreator(LevelDBBackend, dbCreator, true) - registerDBCreator(CLevelDBBackend, dbCreator, false) -} - -var _ DB = (*CLevelDB)(nil) - -type CLevelDB struct { - db *levigo.DB - ro *levigo.ReadOptions - wo *levigo.WriteOptions - woSync *levigo.WriteOptions -} - -func NewCLevelDB(name string, dir string) (*CLevelDB, error) { - dbPath := filepath.Join(dir, name+".db") - - opts := levigo.NewOptions() - opts.SetCache(levigo.NewLRUCache(1 << 30)) - opts.SetCreateIfMissing(true) - db, err := levigo.Open(dbPath, opts) - if err != nil { - return nil, err - } - ro := levigo.NewReadOptions() - wo := levigo.NewWriteOptions() - woSync := levigo.NewWriteOptions() - woSync.SetSync(true) - database := &CLevelDB{ - db: db, - ro: ro, - wo: wo, - woSync: woSync, - } - return database, nil -} - -// Implements DB. -func (db *CLevelDB) Get(key []byte) []byte { - key = nonNilBytes(key) - res, err := db.db.Get(db.ro, key) - if err != nil { - panic(err) - } - return res -} - -// Implements DB. -func (db *CLevelDB) Has(key []byte) bool { - return db.Get(key) != nil -} - -// Implements DB. -func (db *CLevelDB) Set(key []byte, value []byte) { - key = nonNilBytes(key) - value = nonNilBytes(value) - err := db.db.Put(db.wo, key, value) - if err != nil { - panic(err) - } -} - -// Implements DB. -func (db *CLevelDB) SetSync(key []byte, value []byte) { - key = nonNilBytes(key) - value = nonNilBytes(value) - err := db.db.Put(db.woSync, key, value) - if err != nil { - panic(err) - } -} - -// Implements DB. -func (db *CLevelDB) Delete(key []byte) { - key = nonNilBytes(key) - err := db.db.Delete(db.wo, key) - if err != nil { - panic(err) - } -} - -// Implements DB. -func (db *CLevelDB) DeleteSync(key []byte) { - key = nonNilBytes(key) - err := db.db.Delete(db.woSync, key) - if err != nil { - panic(err) - } -} - -func (db *CLevelDB) DB() *levigo.DB { - return db.db -} - -// Implements DB. -func (db *CLevelDB) Close() { - db.db.Close() - db.ro.Close() - db.wo.Close() - db.woSync.Close() -} - -// Implements DB. -func (db *CLevelDB) Print() { - itr := db.Iterator(nil, nil) - defer itr.Close() - for ; itr.Valid(); itr.Next() { - key := itr.Key() - value := itr.Value() - fmt.Printf("[%X]:\t[%X]\n", key, value) - } -} - -// Implements DB. -func (db *CLevelDB) Stats() map[string]string { - // TODO: Find the available properties for the C LevelDB implementation - keys := []string{} - - stats := make(map[string]string) - for _, key := range keys { - str := db.db.PropertyValue(key) - stats[key] = str - } - return stats -} - -//---------------------------------------- -// Batch - -// Implements DB. -func (db *CLevelDB) NewBatch() Batch { - batch := levigo.NewWriteBatch() - return &cLevelDBBatch{db, batch} -} - -type cLevelDBBatch struct { - db *CLevelDB - batch *levigo.WriteBatch -} - -// Implements Batch. -func (mBatch *cLevelDBBatch) Set(key, value []byte) { - mBatch.batch.Put(key, value) -} - -// Implements Batch. -func (mBatch *cLevelDBBatch) Delete(key []byte) { - mBatch.batch.Delete(key) -} - -// Implements Batch. -func (mBatch *cLevelDBBatch) Write() { - err := mBatch.db.db.Write(mBatch.db.wo, mBatch.batch) - if err != nil { - panic(err) - } -} - -// Implements Batch. -func (mBatch *cLevelDBBatch) WriteSync() { - err := mBatch.db.db.Write(mBatch.db.woSync, mBatch.batch) - if err != nil { - panic(err) - } -} - -// Implements Batch. -func (mBatch *cLevelDBBatch) Close() { - mBatch.batch.Close() -} - -//---------------------------------------- -// Iterator -// NOTE This is almost identical to db/go_level_db.Iterator -// Before creating a third version, refactor. - -func (db *CLevelDB) Iterator(start, end []byte) Iterator { - itr := db.db.NewIterator(db.ro) - return newCLevelDBIterator(itr, start, end, false) -} - -func (db *CLevelDB) ReverseIterator(start, end []byte) Iterator { - itr := db.db.NewIterator(db.ro) - return newCLevelDBIterator(itr, start, end, true) -} - -var _ Iterator = (*cLevelDBIterator)(nil) - -type cLevelDBIterator struct { - source *levigo.Iterator - start, end []byte - isReverse bool - isInvalid bool -} - -func newCLevelDBIterator(source *levigo.Iterator, start, end []byte, isReverse bool) *cLevelDBIterator { - if isReverse { - if end == nil { - source.SeekToLast() - } else { - source.Seek(end) - if source.Valid() { - eoakey := source.Key() // end or after key - if bytes.Compare(end, eoakey) <= 0 { - source.Prev() - } - } else { - source.SeekToLast() - } - } - } else { - if start == nil { - source.SeekToFirst() - } else { - source.Seek(start) - } - } - return &cLevelDBIterator{ - source: source, - start: start, - end: end, - isReverse: isReverse, - isInvalid: false, - } -} - -func (itr cLevelDBIterator) Domain() ([]byte, []byte) { - return itr.start, itr.end -} - -func (itr cLevelDBIterator) Valid() bool { - - // Once invalid, forever invalid. - if itr.isInvalid { - return false - } - - // Panic on DB error. No way to recover. - itr.assertNoError() - - // If source is invalid, invalid. - if !itr.source.Valid() { - itr.isInvalid = true - return false - } - - // If key is end or past it, invalid. - var start = itr.start - var end = itr.end - var key = itr.source.Key() - if itr.isReverse { - if start != nil && bytes.Compare(key, start) < 0 { - itr.isInvalid = true - return false - } - } else { - if end != nil && bytes.Compare(end, key) <= 0 { - itr.isInvalid = true - return false - } - } - - // It's valid. - return true -} - -func (itr cLevelDBIterator) Key() []byte { - itr.assertNoError() - itr.assertIsValid() - return itr.source.Key() -} - -func (itr cLevelDBIterator) Value() []byte { - itr.assertNoError() - itr.assertIsValid() - return itr.source.Value() -} - -func (itr cLevelDBIterator) Next() { - itr.assertNoError() - itr.assertIsValid() - if itr.isReverse { - itr.source.Prev() - } else { - itr.source.Next() - } -} - -func (itr cLevelDBIterator) Close() { - itr.source.Close() -} - -func (itr cLevelDBIterator) assertNoError() { - if err := itr.source.GetError(); err != nil { - panic(err) - } -} - -func (itr cLevelDBIterator) assertIsValid() { - if !itr.Valid() { - panic("cLevelDBIterator is invalid") - } -} diff --git a/libs/db/c_level_db_test.go b/libs/db/c_level_db_test.go deleted file mode 100644 index eab3dfc41e0..00000000000 --- a/libs/db/c_level_db_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// +build gcc - -package db - -import ( - "bytes" - "fmt" - "os" - "testing" - - "github.com/stretchr/testify/assert" - - cmn "github.com/tendermint/tendermint/libs/common" -) - -func BenchmarkRandomReadsWrites2(b *testing.B) { - b.StopTimer() - - numItems := int64(1000000) - internal := map[int64]int64{} - for i := 0; i < int(numItems); i++ { - internal[int64(i)] = int64(0) - } - db, err := NewCLevelDB(fmt.Sprintf("test_%x", cmn.RandStr(12)), "") - if err != nil { - b.Fatal(err.Error()) - return - } - - fmt.Println("ok, starting") - b.StartTimer() - - for i := 0; i < b.N; i++ { - // Write something - { - idx := (int64(cmn.RandInt()) % numItems) - internal[idx]++ - val := internal[idx] - idxBytes := int642Bytes(int64(idx)) - valBytes := int642Bytes(int64(val)) - //fmt.Printf("Set %X -> %X\n", idxBytes, valBytes) - db.Set( - idxBytes, - valBytes, - ) - } - // Read something - { - idx := (int64(cmn.RandInt()) % numItems) - val := internal[idx] - idxBytes := int642Bytes(int64(idx)) - valBytes := db.Get(idxBytes) - //fmt.Printf("Get %X -> %X\n", idxBytes, valBytes) - if val == 0 { - if !bytes.Equal(valBytes, nil) { - b.Errorf("Expected %v for %v, got %X", - nil, idx, valBytes) - break - } - } else { - if len(valBytes) != 8 { - b.Errorf("Expected length 8 for %v, got %X", - idx, valBytes) - break - } - valGot := bytes2Int64(valBytes) - if val != valGot { - b.Errorf("Expected %v for %v, got %v", - val, idx, valGot) - break - } - } - } - } - - db.Close() -} - -/* -func int642Bytes(i int64) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, uint64(i)) - return buf -} - -func bytes2Int64(buf []byte) int64 { - return int64(binary.BigEndian.Uint64(buf)) -} -*/ - -func TestCLevelDBBackend(t *testing.T) { - name := fmt.Sprintf("test_%x", cmn.RandStr(12)) - // Can't use "" (current directory) or "./" here because levigo.Open returns: - // "Error initializing DB: IO error: test_XXX.db: Invalid argument" - dir := os.TempDir() - db := NewDB(name, LevelDBBackend, dir) - defer cleanupDBDir(dir, name) - - _, ok := db.(*CLevelDB) - assert.True(t, ok) -} diff --git a/libs/db/common_test.go b/libs/db/common_test.go deleted file mode 100644 index 1e27a7cac88..00000000000 --- a/libs/db/common_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package db - -import ( - "fmt" - "io/ioutil" - "sync" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -//---------------------------------------- -// Helper functions. - -func checkValue(t *testing.T, db DB, key []byte, valueWanted []byte) { - valueGot := db.Get(key) - assert.Equal(t, valueWanted, valueGot) -} - -func checkValid(t *testing.T, itr Iterator, expected bool) { - valid := itr.Valid() - require.Equal(t, expected, valid) -} - -func checkNext(t *testing.T, itr Iterator, expected bool) { - itr.Next() - valid := itr.Valid() - require.Equal(t, expected, valid) -} - -func checkNextPanics(t *testing.T, itr Iterator) { - assert.Panics(t, func() { itr.Next() }, "checkNextPanics expected panic but didn't") -} - -func checkDomain(t *testing.T, itr Iterator, start, end []byte) { - ds, de := itr.Domain() - assert.Equal(t, start, ds, "checkDomain domain start incorrect") - assert.Equal(t, end, de, "checkDomain domain end incorrect") -} - -func checkItem(t *testing.T, itr Iterator, key []byte, value []byte) { - k, v := itr.Key(), itr.Value() - assert.Exactly(t, key, k) - assert.Exactly(t, value, v) -} - -func checkInvalid(t *testing.T, itr Iterator) { - checkValid(t, itr, false) - checkKeyPanics(t, itr) - checkValuePanics(t, itr) - checkNextPanics(t, itr) -} - -func checkKeyPanics(t *testing.T, itr Iterator) { - assert.Panics(t, func() { itr.Key() }, "checkKeyPanics expected panic but didn't") -} - -func checkValuePanics(t *testing.T, itr Iterator) { - assert.Panics(t, func() { itr.Value() }, "checkValuePanics expected panic but didn't") -} - -func newTempDB(t *testing.T, backend DBBackendType) (db DB, dbDir string) { - dirname, err := ioutil.TempDir("", "db_common_test") - require.Nil(t, err) - return NewDB("testdb", backend, dirname), dirname -} - -//---------------------------------------- -// mockDB - -// NOTE: not actually goroutine safe. -// If you want something goroutine safe, maybe you just want a MemDB. -type mockDB struct { - mtx sync.Mutex - calls map[string]int -} - -func newMockDB() *mockDB { - return &mockDB{ - calls: make(map[string]int), - } -} - -func (mdb *mockDB) Mutex() *sync.Mutex { - return &(mdb.mtx) -} - -func (mdb *mockDB) Get([]byte) []byte { - mdb.calls["Get"]++ - return nil -} - -func (mdb *mockDB) Has([]byte) bool { - mdb.calls["Has"]++ - return false -} - -func (mdb *mockDB) Set([]byte, []byte) { - mdb.calls["Set"]++ -} - -func (mdb *mockDB) SetSync([]byte, []byte) { - mdb.calls["SetSync"]++ -} - -func (mdb *mockDB) SetNoLock([]byte, []byte) { - mdb.calls["SetNoLock"]++ -} - -func (mdb *mockDB) SetNoLockSync([]byte, []byte) { - mdb.calls["SetNoLockSync"]++ -} - -func (mdb *mockDB) Delete([]byte) { - mdb.calls["Delete"]++ -} - -func (mdb *mockDB) DeleteSync([]byte) { - mdb.calls["DeleteSync"]++ -} - -func (mdb *mockDB) DeleteNoLock([]byte) { - mdb.calls["DeleteNoLock"]++ -} - -func (mdb *mockDB) DeleteNoLockSync([]byte) { - mdb.calls["DeleteNoLockSync"]++ -} - -func (mdb *mockDB) Iterator(start, end []byte) Iterator { - mdb.calls["Iterator"]++ - return &mockIterator{} -} - -func (mdb *mockDB) ReverseIterator(start, end []byte) Iterator { - mdb.calls["ReverseIterator"]++ - return &mockIterator{} -} - -func (mdb *mockDB) Close() { - mdb.calls["Close"]++ -} - -func (mdb *mockDB) NewBatch() Batch { - mdb.calls["NewBatch"]++ - return &memBatch{db: mdb} -} - -func (mdb *mockDB) Print() { - mdb.calls["Print"]++ - fmt.Printf("mockDB{%v}", mdb.Stats()) -} - -func (mdb *mockDB) Stats() map[string]string { - mdb.calls["Stats"]++ - - res := make(map[string]string) - for key, count := range mdb.calls { - res[key] = fmt.Sprintf("%d", count) - } - return res -} - -//---------------------------------------- -// mockIterator - -type mockIterator struct{} - -func (mockIterator) Domain() (start []byte, end []byte) { - return nil, nil -} - -func (mockIterator) Valid() bool { - return false -} - -func (mockIterator) Next() { -} - -func (mockIterator) Key() []byte { - return nil -} - -func (mockIterator) Value() []byte { - return nil -} - -func (mockIterator) Close() { -} diff --git a/libs/db/db.go b/libs/db/db.go deleted file mode 100644 index 8a3975a849a..00000000000 --- a/libs/db/db.go +++ /dev/null @@ -1,54 +0,0 @@ -package db - -import ( - "fmt" - "strings" -) - -//---------------------------------------- -// Main entry - -type DBBackendType string - -const ( - LevelDBBackend DBBackendType = "leveldb" // legacy, defaults to goleveldb unless +gcc - CLevelDBBackend DBBackendType = "cleveldb" - GoLevelDBBackend DBBackendType = "goleveldb" - MemDBBackend DBBackendType = "memdb" - FSDBBackend DBBackendType = "fsdb" // using the filesystem naively -) - -type dbCreator func(name string, dir string) (DB, error) - -var backends = map[DBBackendType]dbCreator{} - -func registerDBCreator(backend DBBackendType, creator dbCreator, force bool) { - _, ok := backends[backend] - if !force && ok { - return - } - backends[backend] = creator -} - -// NewDB creates a new database of type backend with the given name. -// NOTE: function panics if: -// - backend is unknown (not registered) -// - creator function, provided during registration, returns error -func NewDB(name string, backend DBBackendType, dir string) DB { - dbCreator, ok := backends[backend] - if !ok { - keys := make([]string, len(backends)) - i := 0 - for k := range backends { - keys[i] = string(k) - i++ - } - panic(fmt.Sprintf("Unknown db_backend %s, expected either %s", backend, strings.Join(keys, " or "))) - } - - db, err := dbCreator(name, dir) - if err != nil { - panic(fmt.Sprintf("Error initializing DB: %v", err)) - } - return db -} diff --git a/libs/db/db_test.go b/libs/db/db_test.go deleted file mode 100644 index ffa7bb6aadb..00000000000 --- a/libs/db/db_test.go +++ /dev/null @@ -1,206 +0,0 @@ -package db - -import ( - "fmt" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestDBIteratorSingleKey(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - db.SetSync(bz("1"), bz("value_1")) - itr := db.Iterator(nil, nil) - - checkValid(t, itr, true) - checkNext(t, itr, false) - checkValid(t, itr, false) - checkNextPanics(t, itr) - - // Once invalid... - checkInvalid(t, itr) - }) - } -} - -func TestDBIteratorTwoKeys(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - db.SetSync(bz("1"), bz("value_1")) - db.SetSync(bz("2"), bz("value_1")) - - { // Fail by calling Next too much - itr := db.Iterator(nil, nil) - checkValid(t, itr, true) - - checkNext(t, itr, true) - checkValid(t, itr, true) - - checkNext(t, itr, false) - checkValid(t, itr, false) - - checkNextPanics(t, itr) - - // Once invalid... - checkInvalid(t, itr) - } - }) - } -} - -func TestDBIteratorMany(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - keys := make([][]byte, 100) - for i := 0; i < 100; i++ { - keys[i] = []byte{byte(i)} - } - - value := []byte{5} - for _, k := range keys { - db.Set(k, value) - } - - itr := db.Iterator(nil, nil) - defer itr.Close() - for ; itr.Valid(); itr.Next() { - assert.Equal(t, db.Get(itr.Key()), itr.Value()) - } - }) - } -} - -func TestDBIteratorEmpty(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - itr := db.Iterator(nil, nil) - - checkInvalid(t, itr) - }) - } -} - -func TestDBIteratorEmptyBeginAfter(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - itr := db.Iterator(bz("1"), nil) - - checkInvalid(t, itr) - }) - } -} - -func TestDBIteratorNonemptyBeginAfter(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - db.SetSync(bz("1"), bz("value_1")) - itr := db.Iterator(bz("2"), nil) - - checkInvalid(t, itr) - }) - } -} - -func TestDBBatchWrite1(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Delete(bz("3")) - batch.Set(bz("4"), bz("4")) - batch.Write() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 3, mdb.calls["SetNoLock"]) - assert.Equal(t, 0, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLockSync"]) -} - -func TestDBBatchWrite2(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Set(bz("4"), bz("4")) - batch.Delete(bz("3")) - batch.Write() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 3, mdb.calls["SetNoLock"]) - assert.Equal(t, 0, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLockSync"]) -} - -func TestDBBatchWriteSync1(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Delete(bz("3")) - batch.Set(bz("4"), bz("4")) - batch.WriteSync() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 2, mdb.calls["SetNoLock"]) - assert.Equal(t, 1, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLockSync"]) -} - -func TestDBBatchWriteSync2(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Set(bz("4"), bz("4")) - batch.Delete(bz("3")) - batch.WriteSync() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 3, mdb.calls["SetNoLock"]) - assert.Equal(t, 0, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLockSync"]) -} diff --git a/libs/db/debug_db.go b/libs/db/debug_db.go deleted file mode 100644 index 658cd055588..00000000000 --- a/libs/db/debug_db.go +++ /dev/null @@ -1,257 +0,0 @@ -package db - -import ( - "fmt" - "sync" - - cmn "github.com/tendermint/tendermint/libs/common" -) - -//---------------------------------------- -// debugDB - -type debugDB struct { - label string - db DB -} - -// For printing all operationgs to the console for debugging. -func NewDebugDB(label string, db DB) debugDB { - return debugDB{ - label: label, - db: db, - } -} - -// Implements atomicSetDeleter. -func (ddb debugDB) Mutex() *sync.Mutex { return nil } - -// Implements DB. -func (ddb debugDB) Get(key []byte) (value []byte) { - defer func() { - fmt.Printf("%v.Get(%v) %v\n", ddb.label, - cmn.ColoredBytes(key, cmn.Cyan, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - }() - value = ddb.db.Get(key) - return -} - -// Implements DB. -func (ddb debugDB) Has(key []byte) (has bool) { - defer func() { - fmt.Printf("%v.Has(%v) %v\n", ddb.label, - cmn.ColoredBytes(key, cmn.Cyan, cmn.Blue), has) - }() - return ddb.db.Has(key) -} - -// Implements DB. -func (ddb debugDB) Set(key []byte, value []byte) { - fmt.Printf("%v.Set(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.Set(key, value) -} - -// Implements DB. -func (ddb debugDB) SetSync(key []byte, value []byte) { - fmt.Printf("%v.SetSync(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.SetSync(key, value) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) SetNoLock(key []byte, value []byte) { - fmt.Printf("%v.SetNoLock(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.(atomicSetDeleter).SetNoLock(key, value) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) SetNoLockSync(key []byte, value []byte) { - fmt.Printf("%v.SetNoLockSync(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.(atomicSetDeleter).SetNoLockSync(key, value) -} - -// Implements DB. -func (ddb debugDB) Delete(key []byte) { - fmt.Printf("%v.Delete(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.Delete(key) -} - -// Implements DB. -func (ddb debugDB) DeleteSync(key []byte) { - fmt.Printf("%v.DeleteSync(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.DeleteSync(key) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) DeleteNoLock(key []byte) { - fmt.Printf("%v.DeleteNoLock(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.(atomicSetDeleter).DeleteNoLock(key) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) DeleteNoLockSync(key []byte) { - fmt.Printf("%v.DeleteNoLockSync(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.(atomicSetDeleter).DeleteNoLockSync(key) -} - -// Implements DB. -func (ddb debugDB) Iterator(start, end []byte) Iterator { - fmt.Printf("%v.Iterator(%v, %v)\n", ddb.label, - cmn.ColoredBytes(start, cmn.Cyan, cmn.Blue), - cmn.ColoredBytes(end, cmn.Cyan, cmn.Blue)) - return NewDebugIterator(ddb.label, ddb.db.Iterator(start, end)) -} - -// Implements DB. -func (ddb debugDB) ReverseIterator(start, end []byte) Iterator { - fmt.Printf("%v.ReverseIterator(%v, %v)\n", ddb.label, - cmn.ColoredBytes(start, cmn.Cyan, cmn.Blue), - cmn.ColoredBytes(end, cmn.Cyan, cmn.Blue)) - return NewDebugIterator(ddb.label, ddb.db.ReverseIterator(start, end)) -} - -// Implements DB. -// Panics if the underlying db is not an -// atomicSetDeleter. -func (ddb debugDB) NewBatch() Batch { - fmt.Printf("%v.NewBatch()\n", ddb.label) - return NewDebugBatch(ddb.label, ddb.db.NewBatch()) -} - -// Implements DB. -func (ddb debugDB) Close() { - fmt.Printf("%v.Close()\n", ddb.label) - ddb.db.Close() -} - -// Implements DB. -func (ddb debugDB) Print() { - ddb.db.Print() -} - -// Implements DB. -func (ddb debugDB) Stats() map[string]string { - return ddb.db.Stats() -} - -//---------------------------------------- -// debugIterator - -type debugIterator struct { - label string - itr Iterator -} - -// For printing all operationgs to the console for debugging. -func NewDebugIterator(label string, itr Iterator) debugIterator { - return debugIterator{ - label: label, - itr: itr, - } -} - -// Implements Iterator. -func (ditr debugIterator) Domain() (start []byte, end []byte) { - defer func() { - fmt.Printf("%v.itr.Domain() (%X,%X)\n", ditr.label, start, end) - }() - start, end = ditr.itr.Domain() - return -} - -// Implements Iterator. -func (ditr debugIterator) Valid() (ok bool) { - defer func() { - fmt.Printf("%v.itr.Valid() %v\n", ditr.label, ok) - }() - ok = ditr.itr.Valid() - return -} - -// Implements Iterator. -func (ditr debugIterator) Next() { - fmt.Printf("%v.itr.Next()\n", ditr.label) - ditr.itr.Next() -} - -// Implements Iterator. -func (ditr debugIterator) Key() (key []byte) { - key = ditr.itr.Key() - fmt.Printf("%v.itr.Key() %v\n", ditr.label, - cmn.ColoredBytes(key, cmn.Cyan, cmn.Blue)) - return -} - -// Implements Iterator. -func (ditr debugIterator) Value() (value []byte) { - value = ditr.itr.Value() - fmt.Printf("%v.itr.Value() %v\n", ditr.label, - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - return -} - -// Implements Iterator. -func (ditr debugIterator) Close() { - fmt.Printf("%v.itr.Close()\n", ditr.label) - ditr.itr.Close() -} - -//---------------------------------------- -// debugBatch - -type debugBatch struct { - label string - bch Batch -} - -// For printing all operationgs to the console for debugging. -func NewDebugBatch(label string, bch Batch) debugBatch { - return debugBatch{ - label: label, - bch: bch, - } -} - -// Implements Batch. -func (dbch debugBatch) Set(key, value []byte) { - fmt.Printf("%v.batch.Set(%v, %v)\n", dbch.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - dbch.bch.Set(key, value) -} - -// Implements Batch. -func (dbch debugBatch) Delete(key []byte) { - fmt.Printf("%v.batch.Delete(%v)\n", dbch.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - dbch.bch.Delete(key) -} - -// Implements Batch. -func (dbch debugBatch) Write() { - fmt.Printf("%v.batch.Write()\n", dbch.label) - dbch.bch.Write() -} - -// Implements Batch. -func (dbch debugBatch) WriteSync() { - fmt.Printf("%v.batch.WriteSync()\n", dbch.label) - dbch.bch.WriteSync() -} - -// Implements Batch. -func (dbch debugBatch) Close() { - dbch.bch.Close() -} diff --git a/libs/db/fsdb.go b/libs/db/fsdb.go deleted file mode 100644 index 2d82e7749d0..00000000000 --- a/libs/db/fsdb.go +++ /dev/null @@ -1,270 +0,0 @@ -package db - -import ( - "fmt" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "sort" - "sync" - - "github.com/pkg/errors" - - cmn "github.com/tendermint/tendermint/libs/common" -) - -const ( - keyPerm = os.FileMode(0600) - dirPerm = os.FileMode(0700) -) - -func init() { - registerDBCreator(FSDBBackend, func(name string, dir string) (DB, error) { - dbPath := filepath.Join(dir, name+".db") - return NewFSDB(dbPath), nil - }, false) -} - -var _ DB = (*FSDB)(nil) - -// It's slow. -type FSDB struct { - mtx sync.Mutex - dir string -} - -func NewFSDB(dir string) *FSDB { - err := os.MkdirAll(dir, dirPerm) - if err != nil { - panic(errors.Wrap(err, "Creating FSDB dir "+dir)) - } - database := &FSDB{ - dir: dir, - } - return database -} - -func (db *FSDB) Get(key []byte) []byte { - db.mtx.Lock() - defer db.mtx.Unlock() - key = escapeKey(key) - - path := db.nameToPath(key) - value, err := read(path) - if os.IsNotExist(err) { - return nil - } else if err != nil { - panic(errors.Wrapf(err, "Getting key %s (0x%X)", string(key), key)) - } - return value -} - -func (db *FSDB) Has(key []byte) bool { - db.mtx.Lock() - defer db.mtx.Unlock() - key = escapeKey(key) - - path := db.nameToPath(key) - return cmn.FileExists(path) -} - -func (db *FSDB) Set(key []byte, value []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.SetNoLock(key, value) -} - -func (db *FSDB) SetSync(key []byte, value []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.SetNoLock(key, value) -} - -// NOTE: Implements atomicSetDeleter. -func (db *FSDB) SetNoLock(key []byte, value []byte) { - key = escapeKey(key) - value = nonNilBytes(value) - path := db.nameToPath(key) - err := write(path, value) - if err != nil { - panic(errors.Wrapf(err, "Setting key %s (0x%X)", string(key), key)) - } -} - -func (db *FSDB) Delete(key []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.DeleteNoLock(key) -} - -func (db *FSDB) DeleteSync(key []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.DeleteNoLock(key) -} - -// NOTE: Implements atomicSetDeleter. -func (db *FSDB) DeleteNoLock(key []byte) { - key = escapeKey(key) - path := db.nameToPath(key) - err := remove(path) - if os.IsNotExist(err) { - return - } else if err != nil { - panic(errors.Wrapf(err, "Removing key %s (0x%X)", string(key), key)) - } -} - -func (db *FSDB) Close() { - // Nothing to do. -} - -func (db *FSDB) Print() { - db.mtx.Lock() - defer db.mtx.Unlock() - - panic("FSDB.Print not yet implemented") -} - -func (db *FSDB) Stats() map[string]string { - db.mtx.Lock() - defer db.mtx.Unlock() - - panic("FSDB.Stats not yet implemented") -} - -func (db *FSDB) NewBatch() Batch { - db.mtx.Lock() - defer db.mtx.Unlock() - - // Not sure we would ever want to try... - // It doesn't seem easy for general filesystems. - panic("FSDB.NewBatch not yet implemented") -} - -func (db *FSDB) Mutex() *sync.Mutex { - return &(db.mtx) -} - -func (db *FSDB) Iterator(start, end []byte) Iterator { - return db.MakeIterator(start, end, false) -} - -func (db *FSDB) MakeIterator(start, end []byte, isReversed bool) Iterator { - db.mtx.Lock() - defer db.mtx.Unlock() - - // We need a copy of all of the keys. - // Not the best, but probably not a bottleneck depending. - keys, err := list(db.dir, start, end) - if err != nil { - panic(errors.Wrapf(err, "Listing keys in %s", db.dir)) - } - if isReversed { - sort.Sort(sort.Reverse(sort.StringSlice(keys))) - } else { - sort.Strings(keys) - } - return newMemDBIterator(db, keys, start, end) -} - -func (db *FSDB) ReverseIterator(start, end []byte) Iterator { - return db.MakeIterator(start, end, true) -} - -func (db *FSDB) nameToPath(name []byte) string { - n := url.PathEscape(string(name)) - return filepath.Join(db.dir, n) -} - -// Read some bytes to a file. -// CONTRACT: returns os errors directly without wrapping. -func read(path string) ([]byte, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - d, err := ioutil.ReadAll(f) - if err != nil { - return nil, err - } - return d, nil -} - -// Write some bytes from a file. -// CONTRACT: returns os errors directly without wrapping. -func write(path string, d []byte) error { - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, keyPerm) - if err != nil { - return err - } - defer f.Close() - // fInfo, err := f.Stat() - // if err != nil { - // return err - // } - // if fInfo.Mode() != keyPerm { - // return tmerrors.NewErrPermissionsChanged(f.Name(), keyPerm, fInfo.Mode()) - // } - _, err = f.Write(d) - if err != nil { - return err - } - err = f.Sync() - return err -} - -// Remove a file. -// CONTRACT: returns os errors directly without wrapping. -func remove(path string) error { - return os.Remove(path) -} - -// List keys in a directory, stripping of escape sequences and dir portions. -// CONTRACT: returns os errors directly without wrapping. -func list(dirPath string, start, end []byte) ([]string, error) { - dir, err := os.Open(dirPath) - if err != nil { - return nil, err - } - defer dir.Close() - - names, err := dir.Readdirnames(0) - if err != nil { - return nil, err - } - var keys []string - for _, name := range names { - n, err := url.PathUnescape(name) - if err != nil { - return nil, fmt.Errorf("Failed to unescape %s while listing", name) - } - key := unescapeKey([]byte(n)) - if IsKeyInDomain(key, start, end) { - keys = append(keys, string(key)) - } - } - return keys, nil -} - -// To support empty or nil keys, while the file system doesn't allow empty -// filenames. -func escapeKey(key []byte) []byte { - return []byte("k_" + string(key)) -} -func unescapeKey(escKey []byte) []byte { - if len(escKey) < 2 { - panic(fmt.Sprintf("Invalid esc key: %x", escKey)) - } - if string(escKey[:2]) != "k_" { - panic(fmt.Sprintf("Invalid esc key: %x", escKey)) - } - return escKey[2:] -} diff --git a/libs/db/go_level_db.go b/libs/db/go_level_db.go deleted file mode 100644 index 9a4358f600e..00000000000 --- a/libs/db/go_level_db.go +++ /dev/null @@ -1,336 +0,0 @@ -package db - -import ( - "bytes" - "fmt" - "path/filepath" - - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/errors" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/opt" - - cmn "github.com/tendermint/tendermint/libs/common" -) - -func init() { - dbCreator := func(name string, dir string) (DB, error) { - return NewGoLevelDB(name, dir) - } - registerDBCreator(LevelDBBackend, dbCreator, false) - registerDBCreator(GoLevelDBBackend, dbCreator, false) -} - -var _ DB = (*GoLevelDB)(nil) - -type GoLevelDB struct { - db *leveldb.DB -} - -func NewGoLevelDB(name string, dir string) (*GoLevelDB, error) { - return NewGoLevelDBWithOpts(name, dir, nil) -} - -func NewGoLevelDBWithOpts(name string, dir string, o *opt.Options) (*GoLevelDB, error) { - dbPath := filepath.Join(dir, name+".db") - db, err := leveldb.OpenFile(dbPath, o) - if err != nil { - return nil, err - } - database := &GoLevelDB{ - db: db, - } - return database, nil -} - -// Implements DB. -func (db *GoLevelDB) Get(key []byte) []byte { - key = nonNilBytes(key) - res, err := db.db.Get(key, nil) - if err != nil { - if err == errors.ErrNotFound { - return nil - } - panic(err) - } - return res -} - -// Implements DB. -func (db *GoLevelDB) Has(key []byte) bool { - return db.Get(key) != nil -} - -// Implements DB. -func (db *GoLevelDB) Set(key []byte, value []byte) { - key = nonNilBytes(key) - value = nonNilBytes(value) - err := db.db.Put(key, value, nil) - if err != nil { - cmn.PanicCrisis(err) - } -} - -// Implements DB. -func (db *GoLevelDB) SetSync(key []byte, value []byte) { - key = nonNilBytes(key) - value = nonNilBytes(value) - err := db.db.Put(key, value, &opt.WriteOptions{Sync: true}) - if err != nil { - cmn.PanicCrisis(err) - } -} - -// Implements DB. -func (db *GoLevelDB) Delete(key []byte) { - key = nonNilBytes(key) - err := db.db.Delete(key, nil) - if err != nil { - cmn.PanicCrisis(err) - } -} - -// Implements DB. -func (db *GoLevelDB) DeleteSync(key []byte) { - key = nonNilBytes(key) - err := db.db.Delete(key, &opt.WriteOptions{Sync: true}) - if err != nil { - cmn.PanicCrisis(err) - } -} - -func (db *GoLevelDB) DB() *leveldb.DB { - return db.db -} - -// Implements DB. -func (db *GoLevelDB) Close() { - db.db.Close() -} - -// Implements DB. -func (db *GoLevelDB) Print() { - str, _ := db.db.GetProperty("leveldb.stats") - fmt.Printf("%v\n", str) - - itr := db.db.NewIterator(nil, nil) - for itr.Next() { - key := itr.Key() - value := itr.Value() - fmt.Printf("[%X]:\t[%X]\n", key, value) - } -} - -// Implements DB. -func (db *GoLevelDB) Stats() map[string]string { - keys := []string{ - "leveldb.num-files-at-level{n}", - "leveldb.stats", - "leveldb.sstables", - "leveldb.blockpool", - "leveldb.cachedblock", - "leveldb.openedtables", - "leveldb.alivesnaps", - "leveldb.aliveiters", - } - - stats := make(map[string]string) - for _, key := range keys { - str, err := db.db.GetProperty(key) - if err == nil { - stats[key] = str - } - } - return stats -} - -//---------------------------------------- -// Batch - -// Implements DB. -func (db *GoLevelDB) NewBatch() Batch { - batch := new(leveldb.Batch) - return &goLevelDBBatch{db, batch} -} - -type goLevelDBBatch struct { - db *GoLevelDB - batch *leveldb.Batch -} - -// Implements Batch. -func (mBatch *goLevelDBBatch) Set(key, value []byte) { - mBatch.batch.Put(key, value) -} - -// Implements Batch. -func (mBatch *goLevelDBBatch) Delete(key []byte) { - mBatch.batch.Delete(key) -} - -// Implements Batch. -func (mBatch *goLevelDBBatch) Write() { - err := mBatch.db.db.Write(mBatch.batch, &opt.WriteOptions{Sync: false}) - if err != nil { - panic(err) - } -} - -// Implements Batch. -func (mBatch *goLevelDBBatch) WriteSync() { - err := mBatch.db.db.Write(mBatch.batch, &opt.WriteOptions{Sync: true}) - if err != nil { - panic(err) - } -} - -// Implements Batch. -// Close is no-op for goLevelDBBatch. -func (mBatch *goLevelDBBatch) Close() {} - -//---------------------------------------- -// Iterator -// NOTE This is almost identical to db/c_level_db.Iterator -// Before creating a third version, refactor. - -// Implements DB. -func (db *GoLevelDB) Iterator(start, end []byte) Iterator { - itr := db.db.NewIterator(nil, nil) - return newGoLevelDBIterator(itr, start, end, false) -} - -// Implements DB. -func (db *GoLevelDB) ReverseIterator(start, end []byte) Iterator { - itr := db.db.NewIterator(nil, nil) - return newGoLevelDBIterator(itr, start, end, true) -} - -type goLevelDBIterator struct { - source iterator.Iterator - start []byte - end []byte - isReverse bool - isInvalid bool -} - -var _ Iterator = (*goLevelDBIterator)(nil) - -func newGoLevelDBIterator(source iterator.Iterator, start, end []byte, isReverse bool) *goLevelDBIterator { - if isReverse { - if end == nil { - source.Last() - } else { - valid := source.Seek(end) - if valid { - eoakey := source.Key() // end or after key - if bytes.Compare(end, eoakey) <= 0 { - source.Prev() - } - } else { - source.Last() - } - } - } else { - if start == nil { - source.First() - } else { - source.Seek(start) - } - } - return &goLevelDBIterator{ - source: source, - start: start, - end: end, - isReverse: isReverse, - isInvalid: false, - } -} - -// Implements Iterator. -func (itr *goLevelDBIterator) Domain() ([]byte, []byte) { - return itr.start, itr.end -} - -// Implements Iterator. -func (itr *goLevelDBIterator) Valid() bool { - - // Once invalid, forever invalid. - if itr.isInvalid { - return false - } - - // Panic on DB error. No way to recover. - itr.assertNoError() - - // If source is invalid, invalid. - if !itr.source.Valid() { - itr.isInvalid = true - return false - } - - // If key is end or past it, invalid. - var start = itr.start - var end = itr.end - var key = itr.source.Key() - - if itr.isReverse { - if start != nil && bytes.Compare(key, start) < 0 { - itr.isInvalid = true - return false - } - } else { - if end != nil && bytes.Compare(end, key) <= 0 { - itr.isInvalid = true - return false - } - } - - // Valid - return true -} - -// Implements Iterator. -func (itr *goLevelDBIterator) Key() []byte { - // Key returns a copy of the current key. - // See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88 - itr.assertNoError() - itr.assertIsValid() - return cp(itr.source.Key()) -} - -// Implements Iterator. -func (itr *goLevelDBIterator) Value() []byte { - // Value returns a copy of the current value. - // See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88 - itr.assertNoError() - itr.assertIsValid() - return cp(itr.source.Value()) -} - -// Implements Iterator. -func (itr *goLevelDBIterator) Next() { - itr.assertNoError() - itr.assertIsValid() - if itr.isReverse { - itr.source.Prev() - } else { - itr.source.Next() - } -} - -// Implements Iterator. -func (itr *goLevelDBIterator) Close() { - itr.source.Release() -} - -func (itr *goLevelDBIterator) assertNoError() { - if err := itr.source.Error(); err != nil { - panic(err) - } -} - -func (itr goLevelDBIterator) assertIsValid() { - if !itr.Valid() { - panic("goLevelDBIterator is invalid") - } -} diff --git a/libs/db/go_level_db_test.go b/libs/db/go_level_db_test.go deleted file mode 100644 index c24eec3c8bb..00000000000 --- a/libs/db/go_level_db_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package db - -import ( - "bytes" - "encoding/binary" - "fmt" - "os" - "testing" - - "github.com/syndtr/goleveldb/leveldb/opt" - - "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tendermint/libs/common" -) - -func TestNewGoLevelDB(t *testing.T) { - name := fmt.Sprintf("test_%x", cmn.RandStr(12)) - // Test write locks - db, err := NewGoLevelDB(name, "") - require.Nil(t, err) - defer os.RemoveAll("./" + name + ".db") - _, err = NewGoLevelDB(name, "") - require.NotNil(t, err) - db.Close() // Close the db to release the lock - - // Open the db twice in a row to test read-only locks - ro1, err := NewGoLevelDBWithOpts(name, "", &opt.Options{ReadOnly: true}) - defer ro1.Close() - require.Nil(t, err) - ro2, err := NewGoLevelDBWithOpts(name, "", &opt.Options{ReadOnly: true}) - defer ro2.Close() - require.Nil(t, err) -} - -func BenchmarkRandomReadsWrites(b *testing.B) { - b.StopTimer() - - numItems := int64(1000000) - internal := map[int64]int64{} - for i := 0; i < int(numItems); i++ { - internal[int64(i)] = int64(0) - } - db, err := NewGoLevelDB(fmt.Sprintf("test_%x", cmn.RandStr(12)), "") - if err != nil { - b.Fatal(err.Error()) - return - } - - fmt.Println("ok, starting") - b.StartTimer() - - for i := 0; i < b.N; i++ { - // Write something - { - idx := (int64(cmn.RandInt()) % numItems) - internal[idx]++ - val := internal[idx] - idxBytes := int642Bytes(int64(idx)) - valBytes := int642Bytes(int64(val)) - //fmt.Printf("Set %X -> %X\n", idxBytes, valBytes) - db.Set( - idxBytes, - valBytes, - ) - } - // Read something - { - idx := (int64(cmn.RandInt()) % numItems) - val := internal[idx] - idxBytes := int642Bytes(int64(idx)) - valBytes := db.Get(idxBytes) - //fmt.Printf("Get %X -> %X\n", idxBytes, valBytes) - if val == 0 { - if !bytes.Equal(valBytes, nil) { - b.Errorf("Expected %v for %v, got %X", - nil, idx, valBytes) - break - } - } else { - if len(valBytes) != 8 { - b.Errorf("Expected length 8 for %v, got %X", - idx, valBytes) - break - } - valGot := bytes2Int64(valBytes) - if val != valGot { - b.Errorf("Expected %v for %v, got %v", - val, idx, valGot) - break - } - } - } - } - - db.Close() -} - -func int642Bytes(i int64) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, uint64(i)) - return buf -} - -func bytes2Int64(buf []byte) int64 { - return int64(binary.BigEndian.Uint64(buf)) -} diff --git a/libs/db/mem_batch.go b/libs/db/mem_batch.go deleted file mode 100644 index ebba43f5458..00000000000 --- a/libs/db/mem_batch.go +++ /dev/null @@ -1,76 +0,0 @@ -package db - -import ( - "sync" -) - -type atomicSetDeleter interface { - Mutex() *sync.Mutex - SetNoLock(key, value []byte) - SetNoLockSync(key, value []byte) - DeleteNoLock(key []byte) - DeleteNoLockSync(key []byte) -} - -type memBatch struct { - db atomicSetDeleter - ops []operation -} - -type opType int - -const ( - opTypeSet opType = 1 - opTypeDelete opType = 2 -) - -type operation struct { - opType - key []byte - value []byte -} - -func (mBatch *memBatch) Set(key, value []byte) { - mBatch.ops = append(mBatch.ops, operation{opTypeSet, key, value}) -} - -func (mBatch *memBatch) Delete(key []byte) { - mBatch.ops = append(mBatch.ops, operation{opTypeDelete, key, nil}) -} - -func (mBatch *memBatch) Write() { - mBatch.write(false) -} - -func (mBatch *memBatch) WriteSync() { - mBatch.write(true) -} - -func (mBatch *memBatch) Close() { - mBatch.ops = nil -} - -func (mBatch *memBatch) write(doSync bool) { - if mtx := mBatch.db.Mutex(); mtx != nil { - mtx.Lock() - defer mtx.Unlock() - } - - for i, op := range mBatch.ops { - if doSync && i == (len(mBatch.ops)-1) { - switch op.opType { - case opTypeSet: - mBatch.db.SetNoLockSync(op.key, op.value) - case opTypeDelete: - mBatch.db.DeleteNoLockSync(op.key) - } - break // we're done. - } - switch op.opType { - case opTypeSet: - mBatch.db.SetNoLock(op.key, op.value) - case opTypeDelete: - mBatch.db.DeleteNoLock(op.key) - } - } -} diff --git a/libs/db/mem_db.go b/libs/db/mem_db.go deleted file mode 100644 index ff516bc7d96..00000000000 --- a/libs/db/mem_db.go +++ /dev/null @@ -1,255 +0,0 @@ -package db - -import ( - "fmt" - "sort" - "sync" -) - -func init() { - registerDBCreator(MemDBBackend, func(name string, dir string) (DB, error) { - return NewMemDB(), nil - }, false) -} - -var _ DB = (*MemDB)(nil) - -type MemDB struct { - mtx sync.Mutex - db map[string][]byte -} - -func NewMemDB() *MemDB { - database := &MemDB{ - db: make(map[string][]byte), - } - return database -} - -// Implements atomicSetDeleter. -func (db *MemDB) Mutex() *sync.Mutex { - return &(db.mtx) -} - -// Implements DB. -func (db *MemDB) Get(key []byte) []byte { - db.mtx.Lock() - defer db.mtx.Unlock() - key = nonNilBytes(key) - - value := db.db[string(key)] - return value -} - -// Implements DB. -func (db *MemDB) Has(key []byte) bool { - db.mtx.Lock() - defer db.mtx.Unlock() - key = nonNilBytes(key) - - _, ok := db.db[string(key)] - return ok -} - -// Implements DB. -func (db *MemDB) Set(key []byte, value []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.SetNoLock(key, value) -} - -// Implements DB. -func (db *MemDB) SetSync(key []byte, value []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.SetNoLock(key, value) -} - -// Implements atomicSetDeleter. -func (db *MemDB) SetNoLock(key []byte, value []byte) { - db.SetNoLockSync(key, value) -} - -// Implements atomicSetDeleter. -func (db *MemDB) SetNoLockSync(key []byte, value []byte) { - key = nonNilBytes(key) - value = nonNilBytes(value) - - db.db[string(key)] = value -} - -// Implements DB. -func (db *MemDB) Delete(key []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.DeleteNoLock(key) -} - -// Implements DB. -func (db *MemDB) DeleteSync(key []byte) { - db.mtx.Lock() - defer db.mtx.Unlock() - - db.DeleteNoLock(key) -} - -// Implements atomicSetDeleter. -func (db *MemDB) DeleteNoLock(key []byte) { - db.DeleteNoLockSync(key) -} - -// Implements atomicSetDeleter. -func (db *MemDB) DeleteNoLockSync(key []byte) { - key = nonNilBytes(key) - - delete(db.db, string(key)) -} - -// Implements DB. -func (db *MemDB) Close() { - // Close is a noop since for an in-memory - // database, we don't have a destination - // to flush contents to nor do we want - // any data loss on invoking Close() - // See the discussion in https://github.com/tendermint/tendermint/libs/pull/56 -} - -// Implements DB. -func (db *MemDB) Print() { - db.mtx.Lock() - defer db.mtx.Unlock() - - for key, value := range db.db { - fmt.Printf("[%X]:\t[%X]\n", []byte(key), value) - } -} - -// Implements DB. -func (db *MemDB) Stats() map[string]string { - db.mtx.Lock() - defer db.mtx.Unlock() - - stats := make(map[string]string) - stats["database.type"] = "memDB" - stats["database.size"] = fmt.Sprintf("%d", len(db.db)) - return stats -} - -// Implements DB. -func (db *MemDB) NewBatch() Batch { - db.mtx.Lock() - defer db.mtx.Unlock() - - return &memBatch{db, nil} -} - -//---------------------------------------- -// Iterator - -// Implements DB. -func (db *MemDB) Iterator(start, end []byte) Iterator { - db.mtx.Lock() - defer db.mtx.Unlock() - - keys := db.getSortedKeys(start, end, false) - return newMemDBIterator(db, keys, start, end) -} - -// Implements DB. -func (db *MemDB) ReverseIterator(start, end []byte) Iterator { - db.mtx.Lock() - defer db.mtx.Unlock() - - keys := db.getSortedKeys(start, end, true) - return newMemDBIterator(db, keys, start, end) -} - -// We need a copy of all of the keys. -// Not the best, but probably not a bottleneck depending. -type memDBIterator struct { - db DB - cur int - keys []string - start []byte - end []byte -} - -var _ Iterator = (*memDBIterator)(nil) - -// Keys is expected to be in reverse order for reverse iterators. -func newMemDBIterator(db DB, keys []string, start, end []byte) *memDBIterator { - return &memDBIterator{ - db: db, - cur: 0, - keys: keys, - start: start, - end: end, - } -} - -// Implements Iterator. -func (itr *memDBIterator) Domain() ([]byte, []byte) { - return itr.start, itr.end -} - -// Implements Iterator. -func (itr *memDBIterator) Valid() bool { - return 0 <= itr.cur && itr.cur < len(itr.keys) -} - -// Implements Iterator. -func (itr *memDBIterator) Next() { - itr.assertIsValid() - itr.cur++ -} - -// Implements Iterator. -func (itr *memDBIterator) Key() []byte { - itr.assertIsValid() - return []byte(itr.keys[itr.cur]) -} - -// Implements Iterator. -func (itr *memDBIterator) Value() []byte { - itr.assertIsValid() - key := []byte(itr.keys[itr.cur]) - return itr.db.Get(key) -} - -// Implements Iterator. -func (itr *memDBIterator) Close() { - itr.keys = nil - itr.db = nil -} - -func (itr *memDBIterator) assertIsValid() { - if !itr.Valid() { - panic("memDBIterator is invalid") - } -} - -//---------------------------------------- -// Misc. - -func (db *MemDB) getSortedKeys(start, end []byte, reverse bool) []string { - keys := []string{} - for key := range db.db { - inDomain := IsKeyInDomain([]byte(key), start, end) - if inDomain { - keys = append(keys, key) - } - } - sort.Strings(keys) - if reverse { - nkeys := len(keys) - for i := 0; i < nkeys/2; i++ { - temp := keys[i] - keys[i] = keys[nkeys-i-1] - keys[nkeys-i-1] = temp - } - } - return keys -} diff --git a/libs/db/prefix_db.go b/libs/db/prefix_db.go deleted file mode 100644 index 0dd06ef9d99..00000000000 --- a/libs/db/prefix_db.go +++ /dev/null @@ -1,336 +0,0 @@ -package db - -import ( - "bytes" - "fmt" - "sync" -) - -// IteratePrefix is a convenience function for iterating over a key domain -// restricted by prefix. -func IteratePrefix(db DB, prefix []byte) Iterator { - var start, end []byte - if len(prefix) == 0 { - start = nil - end = nil - } else { - start = cp(prefix) - end = cpIncr(prefix) - } - return db.Iterator(start, end) -} - -/* -TODO: Make test, maybe rename. -// Like IteratePrefix but the iterator strips the prefix from the keys. -func IteratePrefixStripped(db DB, prefix []byte) Iterator { - start, end := ... - return newPrefixIterator(prefix, start, end, IteratePrefix(db, prefix)) -} -*/ - -//---------------------------------------- -// prefixDB - -type prefixDB struct { - mtx sync.Mutex - prefix []byte - db DB -} - -// NewPrefixDB lets you namespace multiple DBs within a single DB. -func NewPrefixDB(db DB, prefix []byte) *prefixDB { - return &prefixDB{ - prefix: prefix, - db: db, - } -} - -// Implements atomicSetDeleter. -func (pdb *prefixDB) Mutex() *sync.Mutex { - return &(pdb.mtx) -} - -// Implements DB. -func (pdb *prefixDB) Get(key []byte) []byte { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - pkey := pdb.prefixed(key) - value := pdb.db.Get(pkey) - return value -} - -// Implements DB. -func (pdb *prefixDB) Has(key []byte) bool { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - return pdb.db.Has(pdb.prefixed(key)) -} - -// Implements DB. -func (pdb *prefixDB) Set(key []byte, value []byte) { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - pkey := pdb.prefixed(key) - pdb.db.Set(pkey, value) -} - -// Implements DB. -func (pdb *prefixDB) SetSync(key []byte, value []byte) { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - pdb.db.SetSync(pdb.prefixed(key), value) -} - -// Implements DB. -func (pdb *prefixDB) Delete(key []byte) { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - pdb.db.Delete(pdb.prefixed(key)) -} - -// Implements DB. -func (pdb *prefixDB) DeleteSync(key []byte) { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - pdb.db.DeleteSync(pdb.prefixed(key)) -} - -// Implements DB. -func (pdb *prefixDB) Iterator(start, end []byte) Iterator { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - var pstart, pend []byte - pstart = append(cp(pdb.prefix), start...) - if end == nil { - pend = cpIncr(pdb.prefix) - } else { - pend = append(cp(pdb.prefix), end...) - } - return newPrefixIterator( - pdb.prefix, - start, - end, - pdb.db.Iterator( - pstart, - pend, - ), - ) -} - -// Implements DB. -func (pdb *prefixDB) ReverseIterator(start, end []byte) Iterator { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - var pstart, pend []byte - pstart = append(cp(pdb.prefix), start...) - if end == nil { - pend = cpIncr(pdb.prefix) - } else { - pend = append(cp(pdb.prefix), end...) - } - ritr := pdb.db.ReverseIterator(pstart, pend) - return newPrefixIterator( - pdb.prefix, - start, - end, - ritr, - ) -} - -// Implements DB. -// Panics if the underlying DB is not an -// atomicSetDeleter. -func (pdb *prefixDB) NewBatch() Batch { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - return newPrefixBatch(pdb.prefix, pdb.db.NewBatch()) -} - -/* NOTE: Uncomment to use memBatch instead of prefixBatch -// Implements atomicSetDeleter. -func (pdb *prefixDB) SetNoLock(key []byte, value []byte) { - pdb.db.(atomicSetDeleter).SetNoLock(pdb.prefixed(key), value) -} - -// Implements atomicSetDeleter. -func (pdb *prefixDB) SetNoLockSync(key []byte, value []byte) { - pdb.db.(atomicSetDeleter).SetNoLockSync(pdb.prefixed(key), value) -} - -// Implements atomicSetDeleter. -func (pdb *prefixDB) DeleteNoLock(key []byte) { - pdb.db.(atomicSetDeleter).DeleteNoLock(pdb.prefixed(key)) -} - -// Implements atomicSetDeleter. -func (pdb *prefixDB) DeleteNoLockSync(key []byte) { - pdb.db.(atomicSetDeleter).DeleteNoLockSync(pdb.prefixed(key)) -} -*/ - -// Implements DB. -func (pdb *prefixDB) Close() { - pdb.mtx.Lock() - defer pdb.mtx.Unlock() - - pdb.db.Close() -} - -// Implements DB. -func (pdb *prefixDB) Print() { - fmt.Printf("prefix: %X\n", pdb.prefix) - - itr := pdb.Iterator(nil, nil) - defer itr.Close() - for ; itr.Valid(); itr.Next() { - key := itr.Key() - value := itr.Value() - fmt.Printf("[%X]:\t[%X]\n", key, value) - } -} - -// Implements DB. -func (pdb *prefixDB) Stats() map[string]string { - stats := make(map[string]string) - stats["prefixdb.prefix.string"] = string(pdb.prefix) - stats["prefixdb.prefix.hex"] = fmt.Sprintf("%X", pdb.prefix) - source := pdb.db.Stats() - for key, value := range source { - stats["prefixdb.source."+key] = value - } - return stats -} - -func (pdb *prefixDB) prefixed(key []byte) []byte { - return append(cp(pdb.prefix), key...) -} - -//---------------------------------------- -// prefixBatch - -type prefixBatch struct { - prefix []byte - source Batch -} - -func newPrefixBatch(prefix []byte, source Batch) prefixBatch { - return prefixBatch{ - prefix: prefix, - source: source, - } -} - -func (pb prefixBatch) Set(key, value []byte) { - pkey := append(cp(pb.prefix), key...) - pb.source.Set(pkey, value) -} - -func (pb prefixBatch) Delete(key []byte) { - pkey := append(cp(pb.prefix), key...) - pb.source.Delete(pkey) -} - -func (pb prefixBatch) Write() { - pb.source.Write() -} - -func (pb prefixBatch) WriteSync() { - pb.source.WriteSync() -} - -func (pb prefixBatch) Close() { - pb.source.Close() -} - -//---------------------------------------- -// prefixIterator - -var _ Iterator = (*prefixIterator)(nil) - -// Strips prefix while iterating from Iterator. -type prefixIterator struct { - prefix []byte - start []byte - end []byte - source Iterator - valid bool -} - -func newPrefixIterator(prefix, start, end []byte, source Iterator) *prefixIterator { - if !source.Valid() || !bytes.HasPrefix(source.Key(), prefix) { - return &prefixIterator{ - prefix: prefix, - start: start, - end: end, - source: source, - valid: false, - } - } else { - return &prefixIterator{ - prefix: prefix, - start: start, - end: end, - source: source, - valid: true, - } - } -} - -func (itr *prefixIterator) Domain() (start []byte, end []byte) { - return itr.start, itr.end -} - -func (itr *prefixIterator) Valid() bool { - return itr.valid && itr.source.Valid() -} - -func (itr *prefixIterator) Next() { - if !itr.valid { - panic("prefixIterator invalid, cannot call Next()") - } - itr.source.Next() - if !itr.source.Valid() || !bytes.HasPrefix(itr.source.Key(), itr.prefix) { - itr.valid = false - return - } -} - -func (itr *prefixIterator) Key() (key []byte) { - if !itr.valid { - panic("prefixIterator invalid, cannot call Key()") - } - return stripPrefix(itr.source.Key(), itr.prefix) -} - -func (itr *prefixIterator) Value() (value []byte) { - if !itr.valid { - panic("prefixIterator invalid, cannot call Value()") - } - return itr.source.Value() -} - -func (itr *prefixIterator) Close() { - itr.source.Close() -} - -//---------------------------------------- - -func stripPrefix(key []byte, prefix []byte) (stripped []byte) { - if len(key) < len(prefix) { - panic("should not happen") - } - if !bytes.Equal(key[:len(prefix)], prefix) { - panic("should not happne") - } - return key[len(prefix):] -} diff --git a/libs/db/prefix_db_test.go b/libs/db/prefix_db_test.go deleted file mode 100644 index e3e37c7d12c..00000000000 --- a/libs/db/prefix_db_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package db - -import "testing" - -func mockDBWithStuff() DB { - db := NewMemDB() - // Under "key" prefix - db.Set(bz("key"), bz("value")) - db.Set(bz("key1"), bz("value1")) - db.Set(bz("key2"), bz("value2")) - db.Set(bz("key3"), bz("value3")) - db.Set(bz("something"), bz("else")) - db.Set(bz(""), bz("")) - db.Set(bz("k"), bz("val")) - db.Set(bz("ke"), bz("valu")) - db.Set(bz("kee"), bz("valuu")) - return db -} - -func TestPrefixDBSimple(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - checkValue(t, pdb, bz("key"), nil) - checkValue(t, pdb, bz(""), bz("value")) - checkValue(t, pdb, bz("key1"), nil) - checkValue(t, pdb, bz("1"), bz("value1")) - checkValue(t, pdb, bz("key2"), nil) - checkValue(t, pdb, bz("2"), bz("value2")) - checkValue(t, pdb, bz("key3"), nil) - checkValue(t, pdb, bz("3"), bz("value3")) - checkValue(t, pdb, bz("something"), nil) - checkValue(t, pdb, bz("k"), nil) - checkValue(t, pdb, bz("ke"), nil) - checkValue(t, pdb, bz("kee"), nil) -} - -func TestPrefixDBIterator1(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.Iterator(nil, nil) - checkDomain(t, itr, nil, nil) - checkItem(t, itr, bz(""), bz("value")) - checkNext(t, itr, true) - checkItem(t, itr, bz("1"), bz("value1")) - checkNext(t, itr, true) - checkItem(t, itr, bz("2"), bz("value2")) - checkNext(t, itr, true) - checkItem(t, itr, bz("3"), bz("value3")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBIterator2(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.Iterator(nil, bz("")) - checkDomain(t, itr, nil, bz("")) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBIterator3(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.Iterator(bz(""), nil) - checkDomain(t, itr, bz(""), nil) - checkItem(t, itr, bz(""), bz("value")) - checkNext(t, itr, true) - checkItem(t, itr, bz("1"), bz("value1")) - checkNext(t, itr, true) - checkItem(t, itr, bz("2"), bz("value2")) - checkNext(t, itr, true) - checkItem(t, itr, bz("3"), bz("value3")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBIterator4(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.Iterator(bz(""), bz("")) - checkDomain(t, itr, bz(""), bz("")) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator1(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(nil, nil) - checkDomain(t, itr, nil, nil) - checkItem(t, itr, bz("3"), bz("value3")) - checkNext(t, itr, true) - checkItem(t, itr, bz("2"), bz("value2")) - checkNext(t, itr, true) - checkItem(t, itr, bz("1"), bz("value1")) - checkNext(t, itr, true) - checkItem(t, itr, bz(""), bz("value")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator2(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(bz(""), nil) - checkDomain(t, itr, bz(""), nil) - checkItem(t, itr, bz("3"), bz("value3")) - checkNext(t, itr, true) - checkItem(t, itr, bz("2"), bz("value2")) - checkNext(t, itr, true) - checkItem(t, itr, bz("1"), bz("value1")) - checkNext(t, itr, true) - checkItem(t, itr, bz(""), bz("value")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator3(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(nil, bz("")) - checkDomain(t, itr, nil, bz("")) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator4(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(bz(""), bz("")) - checkDomain(t, itr, bz(""), bz("")) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator5(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(bz("1"), nil) - checkDomain(t, itr, bz("1"), nil) - checkItem(t, itr, bz("3"), bz("value3")) - checkNext(t, itr, true) - checkItem(t, itr, bz("2"), bz("value2")) - checkNext(t, itr, true) - checkItem(t, itr, bz("1"), bz("value1")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator6(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(bz("2"), nil) - checkDomain(t, itr, bz("2"), nil) - checkItem(t, itr, bz("3"), bz("value3")) - checkNext(t, itr, true) - checkItem(t, itr, bz("2"), bz("value2")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} - -func TestPrefixDBReverseIterator7(t *testing.T) { - db := mockDBWithStuff() - pdb := NewPrefixDB(db, bz("key")) - - itr := pdb.ReverseIterator(nil, bz("2")) - checkDomain(t, itr, nil, bz("2")) - checkItem(t, itr, bz("1"), bz("value1")) - checkNext(t, itr, true) - checkItem(t, itr, bz(""), bz("value")) - checkNext(t, itr, false) - checkInvalid(t, itr) - itr.Close() -} diff --git a/libs/db/remotedb/doc.go b/libs/db/remotedb/doc.go deleted file mode 100644 index 07c95a56a88..00000000000 --- a/libs/db/remotedb/doc.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -remotedb is a package for connecting to distributed Tendermint db.DB -instances. The purpose is to detach difficult deployments such as -CLevelDB that requires gcc or perhaps for databases that require -custom configurations such as extra disk space. It also eases -the burden and cost of deployment of dependencies for databases -to be used by Tendermint developers. Most importantly it is built -over the high performant gRPC transport. - -remotedb's RemoteDB implements db.DB so can be used normally -like other databases. One just has to explicitly connect to the -remote database with a client setup such as: - - client, err := remotedb.NewInsecure(addr) - // Make sure to invoke InitRemote! - if err := client.InitRemote(&remotedb.Init{Name: "test-remote-db", Type: "leveldb"}); err != nil { - log.Fatalf("Failed to initialize the remote db") - } - - client.Set(key1, value) - gv1 := client.SetSync(k2, v2) - - client.Delete(k1) - gv2 := client.Get(k1) - - for itr := client.Iterator(k1, k9); itr.Valid(); itr.Next() { - ik, iv := itr.Key(), itr.Value() - ds, de := itr.Domain() - } - - stats := client.Stats() - - if !client.Has(dk1) { - client.SetSync(dk1, dv1) - } -*/ -package remotedb diff --git a/libs/db/remotedb/grpcdb/client.go b/libs/db/remotedb/grpcdb/client.go deleted file mode 100644 index e11b7839bbe..00000000000 --- a/libs/db/remotedb/grpcdb/client.go +++ /dev/null @@ -1,30 +0,0 @@ -package grpcdb - -import ( - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - - protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto" -) - -// Security defines how the client will talk to the gRPC server. -type Security uint - -const ( - Insecure Security = iota - Secure -) - -// NewClient creates a gRPC client connected to the bound gRPC server at serverAddr. -// Use kind to set the level of security to either Secure or Insecure. -func NewClient(serverAddr, serverCert string) (protodb.DBClient, error) { - creds, err := credentials.NewClientTLSFromFile(serverCert, "") - if err != nil { - return nil, err - } - cc, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds)) - if err != nil { - return nil, err - } - return protodb.NewDBClient(cc), nil -} diff --git a/libs/db/remotedb/grpcdb/doc.go b/libs/db/remotedb/grpcdb/doc.go deleted file mode 100644 index 0d8e380ce63..00000000000 --- a/libs/db/remotedb/grpcdb/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -grpcdb is the distribution of Tendermint's db.DB instances using -the gRPC transport to decouple local db.DB usages from applications, -to using them over a network in a highly performant manner. - -grpcdb allows users to initialize a database's server like -they would locally and invoke the respective methods of db.DB. - -Most users shouldn't use this package, but should instead use -remotedb. Only the lower level users and database server deployers -should use it, for functionality such as: - - ln, err := net.Listen("tcp", "0.0.0.0:0") - srv := grpcdb.NewServer() - defer srv.Stop() - go func() { - if err := srv.Serve(ln); err != nil { - t.Fatalf("BindServer: %v", err) - } - }() - -or - addr := ":8998" - cert := "server.crt" - key := "server.key" - go func() { - if err := grpcdb.ListenAndServe(addr, cert, key); err != nil { - log.Fatalf("BindServer: %v", err) - } - }() -*/ -package grpcdb diff --git a/libs/db/remotedb/grpcdb/example_test.go b/libs/db/remotedb/grpcdb/example_test.go deleted file mode 100644 index eba0d6914cf..00000000000 --- a/libs/db/remotedb/grpcdb/example_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package grpcdb_test - -import ( - "bytes" - "context" - "log" - - grpcdb "github.com/tendermint/tendermint/libs/db/remotedb/grpcdb" - protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto" -) - -func Example() { - addr := ":8998" - cert := "server.crt" - key := "server.key" - go func() { - if err := grpcdb.ListenAndServe(addr, cert, key); err != nil { - log.Fatalf("BindServer: %v", err) - } - }() - - client, err := grpcdb.NewClient(addr, cert) - if err != nil { - log.Fatalf("Failed to create grpcDB client: %v", err) - } - - ctx := context.Background() - // 1. Initialize the DB - in := &protodb.Init{ - Type: "leveldb", - Name: "grpc-uno-test", - Dir: ".", - } - if _, err := client.Init(ctx, in); err != nil { - log.Fatalf("Init error: %v", err) - } - - // 2. Now it can be used! - query1 := &protodb.Entity{Key: []byte("Project"), Value: []byte("Tmlibs-on-gRPC")} - if _, err := client.SetSync(ctx, query1); err != nil { - log.Fatalf("SetSync err: %v", err) - } - - query2 := &protodb.Entity{Key: []byte("Project")} - read, err := client.Get(ctx, query2) - if err != nil { - log.Fatalf("Get err: %v", err) - } - if g, w := read.Value, []byte("Tmlibs-on-gRPC"); !bytes.Equal(g, w) { - log.Fatalf("got= (%q ==> % X)\nwant=(%q ==> % X)", g, g, w, w) - } -} diff --git a/libs/db/remotedb/grpcdb/server.go b/libs/db/remotedb/grpcdb/server.go deleted file mode 100644 index a032292b3d4..00000000000 --- a/libs/db/remotedb/grpcdb/server.go +++ /dev/null @@ -1,200 +0,0 @@ -package grpcdb - -import ( - "context" - "net" - "sync" - "time" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - - "github.com/tendermint/tendermint/libs/db" - protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto" -) - -// ListenAndServe is a blocking function that sets up a gRPC based -// server at the address supplied, with the gRPC options passed in. -// Normally in usage, invoke it in a goroutine like you would for http.ListenAndServe. -func ListenAndServe(addr, cert, key string, opts ...grpc.ServerOption) error { - ln, err := net.Listen("tcp", addr) - if err != nil { - return err - } - srv, err := NewServer(cert, key, opts...) - if err != nil { - return err - } - return srv.Serve(ln) -} - -func NewServer(cert, key string, opts ...grpc.ServerOption) (*grpc.Server, error) { - creds, err := credentials.NewServerTLSFromFile(cert, key) - if err != nil { - return nil, err - } - opts = append(opts, grpc.Creds(creds)) - srv := grpc.NewServer(opts...) - protodb.RegisterDBServer(srv, new(server)) - return srv, nil -} - -type server struct { - mu sync.Mutex - db db.DB -} - -var _ protodb.DBServer = (*server)(nil) - -// Init initializes the server's database. Only one type of database -// can be initialized per server. -// -// Dir is the directory on the file system in which the DB will be stored(if backed by disk) (TODO: remove) -// -// Name is representative filesystem entry's basepath -// -// Type can be either one of: -// * cleveldb (if built with gcc enabled) -// * fsdb -// * memdB -// * leveldb -// See https://godoc.org/github.com/tendermint/tendermint/libs/db#DBBackendType -func (s *server) Init(ctx context.Context, in *protodb.Init) (*protodb.Entity, error) { - s.mu.Lock() - defer s.mu.Unlock() - - s.db = db.NewDB(in.Name, db.DBBackendType(in.Type), in.Dir) - return &protodb.Entity{CreatedAt: time.Now().Unix()}, nil -} - -func (s *server) Delete(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) { - s.db.Delete(in.Key) - return nothing, nil -} - -var nothing = new(protodb.Nothing) - -func (s *server) DeleteSync(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) { - s.db.DeleteSync(in.Key) - return nothing, nil -} - -func (s *server) Get(ctx context.Context, in *protodb.Entity) (*protodb.Entity, error) { - value := s.db.Get(in.Key) - return &protodb.Entity{Value: value}, nil -} - -func (s *server) GetStream(ds protodb.DB_GetStreamServer) error { - // Receive routine - responsesChan := make(chan *protodb.Entity) - go func() { - defer close(responsesChan) - ctx := context.Background() - for { - in, err := ds.Recv() - if err != nil { - responsesChan <- &protodb.Entity{Err: err.Error()} - return - } - out, err := s.Get(ctx, in) - if err != nil { - if out == nil { - out = new(protodb.Entity) - out.Key = in.Key - } - out.Err = err.Error() - responsesChan <- out - return - } - - // Otherwise continue on - responsesChan <- out - } - }() - - // Send routine, block until we return - for out := range responsesChan { - if err := ds.Send(out); err != nil { - return err - } - } - return nil -} - -func (s *server) Has(ctx context.Context, in *protodb.Entity) (*protodb.Entity, error) { - exists := s.db.Has(in.Key) - return &protodb.Entity{Exists: exists}, nil -} - -func (s *server) Set(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) { - s.db.Set(in.Key, in.Value) - return nothing, nil -} - -func (s *server) SetSync(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) { - s.db.SetSync(in.Key, in.Value) - return nothing, nil -} - -func (s *server) Iterator(query *protodb.Entity, dis protodb.DB_IteratorServer) error { - it := s.db.Iterator(query.Start, query.End) - defer it.Close() - return s.handleIterator(it, dis.Send) -} - -func (s *server) handleIterator(it db.Iterator, sendFunc func(*protodb.Iterator) error) error { - for it.Valid() { - start, end := it.Domain() - out := &protodb.Iterator{ - Domain: &protodb.Domain{Start: start, End: end}, - Valid: it.Valid(), - Key: it.Key(), - Value: it.Value(), - } - if err := sendFunc(out); err != nil { - return err - } - - // Finally move the iterator forward - it.Next() - } - return nil -} - -func (s *server) ReverseIterator(query *protodb.Entity, dis protodb.DB_ReverseIteratorServer) error { - it := s.db.ReverseIterator(query.Start, query.End) - defer it.Close() - return s.handleIterator(it, dis.Send) -} - -func (s *server) Stats(context.Context, *protodb.Nothing) (*protodb.Stats, error) { - stats := s.db.Stats() - return &protodb.Stats{Data: stats, TimeAt: time.Now().Unix()}, nil -} - -func (s *server) BatchWrite(c context.Context, b *protodb.Batch) (*protodb.Nothing, error) { - return s.batchWrite(c, b, false) -} - -func (s *server) BatchWriteSync(c context.Context, b *protodb.Batch) (*protodb.Nothing, error) { - return s.batchWrite(c, b, true) -} - -func (s *server) batchWrite(c context.Context, b *protodb.Batch, sync bool) (*protodb.Nothing, error) { - bat := s.db.NewBatch() - defer bat.Close() - for _, op := range b.Ops { - switch op.Type { - case protodb.Operation_SET: - bat.Set(op.Entity.Key, op.Entity.Value) - case protodb.Operation_DELETE: - bat.Delete(op.Entity.Key) - } - } - if sync { - bat.WriteSync() - } else { - bat.Write() - } - return nothing, nil -} diff --git a/libs/db/remotedb/proto/defs.pb.go b/libs/db/remotedb/proto/defs.pb.go deleted file mode 100644 index 4d9f0b27205..00000000000 --- a/libs/db/remotedb/proto/defs.pb.go +++ /dev/null @@ -1,914 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: defs.proto - -/* -Package protodb is a generated protocol buffer package. - -It is generated from these files: - defs.proto - -It has these top-level messages: - Batch - Operation - Entity - Nothing - Domain - Iterator - Stats - Init -*/ -package protodb - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type Operation_Type int32 - -const ( - Operation_SET Operation_Type = 0 - Operation_DELETE Operation_Type = 1 -) - -var Operation_Type_name = map[int32]string{ - 0: "SET", - 1: "DELETE", -} -var Operation_Type_value = map[string]int32{ - "SET": 0, - "DELETE": 1, -} - -func (x Operation_Type) String() string { - return proto.EnumName(Operation_Type_name, int32(x)) -} -func (Operation_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } - -type Batch struct { - Ops []*Operation `protobuf:"bytes,1,rep,name=ops" json:"ops,omitempty"` -} - -func (m *Batch) Reset() { *m = Batch{} } -func (m *Batch) String() string { return proto.CompactTextString(m) } -func (*Batch) ProtoMessage() {} -func (*Batch) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *Batch) GetOps() []*Operation { - if m != nil { - return m.Ops - } - return nil -} - -type Operation struct { - Entity *Entity `protobuf:"bytes,1,opt,name=entity" json:"entity,omitempty"` - Type Operation_Type `protobuf:"varint,2,opt,name=type,enum=protodb.Operation_Type" json:"type,omitempty"` -} - -func (m *Operation) Reset() { *m = Operation{} } -func (m *Operation) String() string { return proto.CompactTextString(m) } -func (*Operation) ProtoMessage() {} -func (*Operation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *Operation) GetEntity() *Entity { - if m != nil { - return m.Entity - } - return nil -} - -func (m *Operation) GetType() Operation_Type { - if m != nil { - return m.Type - } - return Operation_SET -} - -type Entity struct { - Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` - Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` - Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - Exists bool `protobuf:"varint,4,opt,name=exists" json:"exists,omitempty"` - Start []byte `protobuf:"bytes,5,opt,name=start,proto3" json:"start,omitempty"` - End []byte `protobuf:"bytes,6,opt,name=end,proto3" json:"end,omitempty"` - Err string `protobuf:"bytes,7,opt,name=err" json:"err,omitempty"` - CreatedAt int64 `protobuf:"varint,8,opt,name=created_at,json=createdAt" json:"created_at,omitempty"` -} - -func (m *Entity) Reset() { *m = Entity{} } -func (m *Entity) String() string { return proto.CompactTextString(m) } -func (*Entity) ProtoMessage() {} -func (*Entity) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } - -func (m *Entity) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -func (m *Entity) GetKey() []byte { - if m != nil { - return m.Key - } - return nil -} - -func (m *Entity) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -func (m *Entity) GetExists() bool { - if m != nil { - return m.Exists - } - return false -} - -func (m *Entity) GetStart() []byte { - if m != nil { - return m.Start - } - return nil -} - -func (m *Entity) GetEnd() []byte { - if m != nil { - return m.End - } - return nil -} - -func (m *Entity) GetErr() string { - if m != nil { - return m.Err - } - return "" -} - -func (m *Entity) GetCreatedAt() int64 { - if m != nil { - return m.CreatedAt - } - return 0 -} - -type Nothing struct { -} - -func (m *Nothing) Reset() { *m = Nothing{} } -func (m *Nothing) String() string { return proto.CompactTextString(m) } -func (*Nothing) ProtoMessage() {} -func (*Nothing) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -type Domain struct { - Start []byte `protobuf:"bytes,1,opt,name=start,proto3" json:"start,omitempty"` - End []byte `protobuf:"bytes,2,opt,name=end,proto3" json:"end,omitempty"` -} - -func (m *Domain) Reset() { *m = Domain{} } -func (m *Domain) String() string { return proto.CompactTextString(m) } -func (*Domain) ProtoMessage() {} -func (*Domain) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } - -func (m *Domain) GetStart() []byte { - if m != nil { - return m.Start - } - return nil -} - -func (m *Domain) GetEnd() []byte { - if m != nil { - return m.End - } - return nil -} - -type Iterator struct { - Domain *Domain `protobuf:"bytes,1,opt,name=domain" json:"domain,omitempty"` - Valid bool `protobuf:"varint,2,opt,name=valid" json:"valid,omitempty"` - Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` - Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` -} - -func (m *Iterator) Reset() { *m = Iterator{} } -func (m *Iterator) String() string { return proto.CompactTextString(m) } -func (*Iterator) ProtoMessage() {} -func (*Iterator) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } - -func (m *Iterator) GetDomain() *Domain { - if m != nil { - return m.Domain - } - return nil -} - -func (m *Iterator) GetValid() bool { - if m != nil { - return m.Valid - } - return false -} - -func (m *Iterator) GetKey() []byte { - if m != nil { - return m.Key - } - return nil -} - -func (m *Iterator) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -type Stats struct { - Data map[string]string `protobuf:"bytes,1,rep,name=data" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - TimeAt int64 `protobuf:"varint,2,opt,name=time_at,json=timeAt" json:"time_at,omitempty"` -} - -func (m *Stats) Reset() { *m = Stats{} } -func (m *Stats) String() string { return proto.CompactTextString(m) } -func (*Stats) ProtoMessage() {} -func (*Stats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } - -func (m *Stats) GetData() map[string]string { - if m != nil { - return m.Data - } - return nil -} - -func (m *Stats) GetTimeAt() int64 { - if m != nil { - return m.TimeAt - } - return 0 -} - -type Init struct { - Type string `protobuf:"bytes,1,opt,name=Type" json:"Type,omitempty"` - Name string `protobuf:"bytes,2,opt,name=Name" json:"Name,omitempty"` - Dir string `protobuf:"bytes,3,opt,name=Dir" json:"Dir,omitempty"` -} - -func (m *Init) Reset() { *m = Init{} } -func (m *Init) String() string { return proto.CompactTextString(m) } -func (*Init) ProtoMessage() {} -func (*Init) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } - -func (m *Init) GetType() string { - if m != nil { - return m.Type - } - return "" -} - -func (m *Init) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Init) GetDir() string { - if m != nil { - return m.Dir - } - return "" -} - -func init() { - proto.RegisterType((*Batch)(nil), "protodb.Batch") - proto.RegisterType((*Operation)(nil), "protodb.Operation") - proto.RegisterType((*Entity)(nil), "protodb.Entity") - proto.RegisterType((*Nothing)(nil), "protodb.Nothing") - proto.RegisterType((*Domain)(nil), "protodb.Domain") - proto.RegisterType((*Iterator)(nil), "protodb.Iterator") - proto.RegisterType((*Stats)(nil), "protodb.Stats") - proto.RegisterType((*Init)(nil), "protodb.Init") - proto.RegisterEnum("protodb.Operation_Type", Operation_Type_name, Operation_Type_value) -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for DB service - -type DBClient interface { - Init(ctx context.Context, in *Init, opts ...grpc.CallOption) (*Entity, error) - Get(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Entity, error) - GetStream(ctx context.Context, opts ...grpc.CallOption) (DB_GetStreamClient, error) - Has(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Entity, error) - Set(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) - SetSync(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) - Delete(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) - DeleteSync(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) - Iterator(ctx context.Context, in *Entity, opts ...grpc.CallOption) (DB_IteratorClient, error) - ReverseIterator(ctx context.Context, in *Entity, opts ...grpc.CallOption) (DB_ReverseIteratorClient, error) - // rpc print(Nothing) returns (Entity) {} - Stats(ctx context.Context, in *Nothing, opts ...grpc.CallOption) (*Stats, error) - BatchWrite(ctx context.Context, in *Batch, opts ...grpc.CallOption) (*Nothing, error) - BatchWriteSync(ctx context.Context, in *Batch, opts ...grpc.CallOption) (*Nothing, error) -} - -type dBClient struct { - cc *grpc.ClientConn -} - -func NewDBClient(cc *grpc.ClientConn) DBClient { - return &dBClient{cc} -} - -func (c *dBClient) Init(ctx context.Context, in *Init, opts ...grpc.CallOption) (*Entity, error) { - out := new(Entity) - err := grpc.Invoke(ctx, "/protodb.DB/init", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) Get(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Entity, error) { - out := new(Entity) - err := grpc.Invoke(ctx, "/protodb.DB/get", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) GetStream(ctx context.Context, opts ...grpc.CallOption) (DB_GetStreamClient, error) { - stream, err := grpc.NewClientStream(ctx, &_DB_serviceDesc.Streams[0], c.cc, "/protodb.DB/getStream", opts...) - if err != nil { - return nil, err - } - x := &dBGetStreamClient{stream} - return x, nil -} - -type DB_GetStreamClient interface { - Send(*Entity) error - Recv() (*Entity, error) - grpc.ClientStream -} - -type dBGetStreamClient struct { - grpc.ClientStream -} - -func (x *dBGetStreamClient) Send(m *Entity) error { - return x.ClientStream.SendMsg(m) -} - -func (x *dBGetStreamClient) Recv() (*Entity, error) { - m := new(Entity) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *dBClient) Has(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Entity, error) { - out := new(Entity) - err := grpc.Invoke(ctx, "/protodb.DB/has", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) Set(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) { - out := new(Nothing) - err := grpc.Invoke(ctx, "/protodb.DB/set", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) SetSync(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) { - out := new(Nothing) - err := grpc.Invoke(ctx, "/protodb.DB/setSync", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) Delete(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) { - out := new(Nothing) - err := grpc.Invoke(ctx, "/protodb.DB/delete", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) DeleteSync(ctx context.Context, in *Entity, opts ...grpc.CallOption) (*Nothing, error) { - out := new(Nothing) - err := grpc.Invoke(ctx, "/protodb.DB/deleteSync", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) Iterator(ctx context.Context, in *Entity, opts ...grpc.CallOption) (DB_IteratorClient, error) { - stream, err := grpc.NewClientStream(ctx, &_DB_serviceDesc.Streams[1], c.cc, "/protodb.DB/iterator", opts...) - if err != nil { - return nil, err - } - x := &dBIteratorClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type DB_IteratorClient interface { - Recv() (*Iterator, error) - grpc.ClientStream -} - -type dBIteratorClient struct { - grpc.ClientStream -} - -func (x *dBIteratorClient) Recv() (*Iterator, error) { - m := new(Iterator) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *dBClient) ReverseIterator(ctx context.Context, in *Entity, opts ...grpc.CallOption) (DB_ReverseIteratorClient, error) { - stream, err := grpc.NewClientStream(ctx, &_DB_serviceDesc.Streams[2], c.cc, "/protodb.DB/reverseIterator", opts...) - if err != nil { - return nil, err - } - x := &dBReverseIteratorClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type DB_ReverseIteratorClient interface { - Recv() (*Iterator, error) - grpc.ClientStream -} - -type dBReverseIteratorClient struct { - grpc.ClientStream -} - -func (x *dBReverseIteratorClient) Recv() (*Iterator, error) { - m := new(Iterator) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *dBClient) Stats(ctx context.Context, in *Nothing, opts ...grpc.CallOption) (*Stats, error) { - out := new(Stats) - err := grpc.Invoke(ctx, "/protodb.DB/stats", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) BatchWrite(ctx context.Context, in *Batch, opts ...grpc.CallOption) (*Nothing, error) { - out := new(Nothing) - err := grpc.Invoke(ctx, "/protodb.DB/batchWrite", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *dBClient) BatchWriteSync(ctx context.Context, in *Batch, opts ...grpc.CallOption) (*Nothing, error) { - out := new(Nothing) - err := grpc.Invoke(ctx, "/protodb.DB/batchWriteSync", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for DB service - -type DBServer interface { - Init(context.Context, *Init) (*Entity, error) - Get(context.Context, *Entity) (*Entity, error) - GetStream(DB_GetStreamServer) error - Has(context.Context, *Entity) (*Entity, error) - Set(context.Context, *Entity) (*Nothing, error) - SetSync(context.Context, *Entity) (*Nothing, error) - Delete(context.Context, *Entity) (*Nothing, error) - DeleteSync(context.Context, *Entity) (*Nothing, error) - Iterator(*Entity, DB_IteratorServer) error - ReverseIterator(*Entity, DB_ReverseIteratorServer) error - // rpc print(Nothing) returns (Entity) {} - Stats(context.Context, *Nothing) (*Stats, error) - BatchWrite(context.Context, *Batch) (*Nothing, error) - BatchWriteSync(context.Context, *Batch) (*Nothing, error) -} - -func RegisterDBServer(s *grpc.Server, srv DBServer) { - s.RegisterService(&_DB_serviceDesc, srv) -} - -func _DB_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Init) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).Init(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/Init", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).Init(ctx, req.(*Init)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Entity) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).Get(ctx, req.(*Entity)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_GetStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DBServer).GetStream(&dBGetStreamServer{stream}) -} - -type DB_GetStreamServer interface { - Send(*Entity) error - Recv() (*Entity, error) - grpc.ServerStream -} - -type dBGetStreamServer struct { - grpc.ServerStream -} - -func (x *dBGetStreamServer) Send(m *Entity) error { - return x.ServerStream.SendMsg(m) -} - -func (x *dBGetStreamServer) Recv() (*Entity, error) { - m := new(Entity) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func _DB_Has_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Entity) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).Has(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/Has", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).Has(ctx, req.(*Entity)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_Set_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Entity) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).Set(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/Set", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).Set(ctx, req.(*Entity)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_SetSync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Entity) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).SetSync(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/SetSync", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).SetSync(ctx, req.(*Entity)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Entity) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).Delete(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/Delete", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).Delete(ctx, req.(*Entity)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_DeleteSync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Entity) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).DeleteSync(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/DeleteSync", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).DeleteSync(ctx, req.(*Entity)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_Iterator_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(Entity) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(DBServer).Iterator(m, &dBIteratorServer{stream}) -} - -type DB_IteratorServer interface { - Send(*Iterator) error - grpc.ServerStream -} - -type dBIteratorServer struct { - grpc.ServerStream -} - -func (x *dBIteratorServer) Send(m *Iterator) error { - return x.ServerStream.SendMsg(m) -} - -func _DB_ReverseIterator_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(Entity) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(DBServer).ReverseIterator(m, &dBReverseIteratorServer{stream}) -} - -type DB_ReverseIteratorServer interface { - Send(*Iterator) error - grpc.ServerStream -} - -type dBReverseIteratorServer struct { - grpc.ServerStream -} - -func (x *dBReverseIteratorServer) Send(m *Iterator) error { - return x.ServerStream.SendMsg(m) -} - -func _DB_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Nothing) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).Stats(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/Stats", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).Stats(ctx, req.(*Nothing)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_BatchWrite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Batch) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).BatchWrite(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/BatchWrite", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).BatchWrite(ctx, req.(*Batch)) - } - return interceptor(ctx, in, info, handler) -} - -func _DB_BatchWriteSync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Batch) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DBServer).BatchWriteSync(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/protodb.DB/BatchWriteSync", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DBServer).BatchWriteSync(ctx, req.(*Batch)) - } - return interceptor(ctx, in, info, handler) -} - -var _DB_serviceDesc = grpc.ServiceDesc{ - ServiceName: "protodb.DB", - HandlerType: (*DBServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "init", - Handler: _DB_Init_Handler, - }, - { - MethodName: "get", - Handler: _DB_Get_Handler, - }, - { - MethodName: "has", - Handler: _DB_Has_Handler, - }, - { - MethodName: "set", - Handler: _DB_Set_Handler, - }, - { - MethodName: "setSync", - Handler: _DB_SetSync_Handler, - }, - { - MethodName: "delete", - Handler: _DB_Delete_Handler, - }, - { - MethodName: "deleteSync", - Handler: _DB_DeleteSync_Handler, - }, - { - MethodName: "stats", - Handler: _DB_Stats_Handler, - }, - { - MethodName: "batchWrite", - Handler: _DB_BatchWrite_Handler, - }, - { - MethodName: "batchWriteSync", - Handler: _DB_BatchWriteSync_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "getStream", - Handler: _DB_GetStream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "iterator", - Handler: _DB_Iterator_Handler, - ServerStreams: true, - }, - { - StreamName: "reverseIterator", - Handler: _DB_ReverseIterator_Handler, - ServerStreams: true, - }, - }, - Metadata: "defs.proto", -} - -func init() { proto.RegisterFile("defs.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 606 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4f, 0x6f, 0xd3, 0x4e, - 0x10, 0xcd, 0xda, 0x8e, 0x13, 0x4f, 0x7f, 0xbf, 0x34, 0x8c, 0x10, 0xb5, 0x8a, 0x90, 0x22, 0x0b, - 0x09, 0x43, 0x69, 0x14, 0x52, 0x24, 0xfe, 0x9c, 0x68, 0x95, 0x1c, 0x2a, 0xa1, 0x22, 0x39, 0x95, - 0x38, 0xa2, 0x6d, 0x3d, 0x34, 0x2b, 0x1a, 0x3b, 0xac, 0x87, 0x8a, 0x5c, 0xb8, 0xf2, 0x79, 0xf8, - 0x7c, 0x5c, 0xd0, 0xae, 0x1d, 0x87, 0x36, 0x39, 0x84, 0x53, 0x76, 0x66, 0xde, 0x7b, 0xb3, 0xf3, - 0x32, 0x5e, 0x80, 0x94, 0x3e, 0x17, 0xfd, 0xb9, 0xce, 0x39, 0xc7, 0x96, 0xfd, 0x49, 0x2f, 0xa2, - 0x43, 0x68, 0x9e, 0x48, 0xbe, 0x9c, 0xe2, 0x63, 0x70, 0xf3, 0x79, 0x11, 0x8a, 0x9e, 0x1b, 0xef, - 0x0c, 0xb1, 0x5f, 0xd5, 0xfb, 0x1f, 0xe6, 0xa4, 0x25, 0xab, 0x3c, 0x4b, 0x4c, 0x39, 0xfa, 0x01, - 0x41, 0x9d, 0xc1, 0x27, 0xe0, 0x53, 0xc6, 0x8a, 0x17, 0xa1, 0xe8, 0x89, 0x78, 0x67, 0xb8, 0x5b, - 0xb3, 0xc6, 0x36, 0x9d, 0x54, 0x65, 0x3c, 0x00, 0x8f, 0x17, 0x73, 0x0a, 0x9d, 0x9e, 0x88, 0x3b, - 0xc3, 0xbd, 0x75, 0xf1, 0xfe, 0xf9, 0x62, 0x4e, 0x89, 0x05, 0x45, 0x0f, 0xc1, 0x33, 0x11, 0xb6, - 0xc0, 0x9d, 0x8c, 0xcf, 0xbb, 0x0d, 0x04, 0xf0, 0x47, 0xe3, 0xf7, 0xe3, 0xf3, 0x71, 0x57, 0x44, - 0xbf, 0x04, 0xf8, 0xa5, 0x38, 0x76, 0xc0, 0x51, 0xa9, 0xed, 0xdc, 0x4c, 0x1c, 0x95, 0x62, 0x17, - 0xdc, 0x2f, 0xb4, 0xb0, 0x3d, 0xfe, 0x4b, 0xcc, 0x11, 0xef, 0x43, 0xf3, 0x46, 0x5e, 0x7f, 0xa3, - 0xd0, 0xb5, 0xb9, 0x32, 0xc0, 0x07, 0xe0, 0xd3, 0x77, 0x55, 0x70, 0x11, 0x7a, 0x3d, 0x11, 0xb7, - 0x93, 0x2a, 0x32, 0xe8, 0x82, 0xa5, 0xe6, 0xb0, 0x59, 0xa2, 0x6d, 0x60, 0x54, 0x29, 0x4b, 0x43, - 0xbf, 0x54, 0xa5, 0xcc, 0xf6, 0x21, 0xad, 0xc3, 0x56, 0x4f, 0xc4, 0x41, 0x62, 0x8e, 0xf8, 0x08, - 0xe0, 0x52, 0x93, 0x64, 0x4a, 0x3f, 0x49, 0x0e, 0xdb, 0x3d, 0x11, 0xbb, 0x49, 0x50, 0x65, 0x8e, - 0x39, 0x0a, 0xa0, 0x75, 0x96, 0xf3, 0x54, 0x65, 0x57, 0xd1, 0x00, 0xfc, 0x51, 0x3e, 0x93, 0x2a, - 0x5b, 0x75, 0x13, 0x1b, 0xba, 0x39, 0x75, 0xb7, 0xe8, 0x2b, 0xb4, 0x4f, 0xd9, 0xb8, 0x94, 0x6b, - 0xe3, 0x77, 0x6a, 0xd9, 0x6b, 0x7e, 0x97, 0xa2, 0x49, 0x55, 0xae, 0x06, 0x57, 0xa5, 0x50, 0x3b, - 0x29, 0x83, 0xa5, 0x41, 0xee, 0x06, 0x83, 0xbc, 0xbf, 0x0c, 0x8a, 0x7e, 0x0a, 0x68, 0x4e, 0x58, - 0x72, 0x81, 0xcf, 0xc1, 0x4b, 0x25, 0xcb, 0x6a, 0x29, 0xc2, 0xba, 0x9d, 0xad, 0xf6, 0x47, 0x92, - 0xe5, 0x38, 0x63, 0xbd, 0x48, 0x2c, 0x0a, 0xf7, 0xa0, 0xc5, 0x6a, 0x46, 0xc6, 0x03, 0xc7, 0x7a, - 0xe0, 0x9b, 0xf0, 0x98, 0xf7, 0x5f, 0x41, 0x50, 0x63, 0x97, 0xb7, 0x10, 0xa5, 0x7d, 0xb7, 0x6e, - 0xe1, 0xd8, 0x5c, 0x19, 0xbc, 0x75, 0x5e, 0x8b, 0xe8, 0x1d, 0x78, 0xa7, 0x99, 0x62, 0xc4, 0x72, - 0x25, 0x2a, 0x52, 0xb9, 0x1e, 0x08, 0xde, 0x99, 0x9c, 0x2d, 0x49, 0xf6, 0x6c, 0xb4, 0x47, 0x4a, - 0xdb, 0x09, 0x83, 0xc4, 0x1c, 0x87, 0xbf, 0x3d, 0x70, 0x46, 0x27, 0x18, 0x83, 0xa7, 0x8c, 0xd0, - 0xff, 0xf5, 0x08, 0x46, 0x77, 0xff, 0xee, 0xc2, 0x46, 0x0d, 0x7c, 0x0a, 0xee, 0x15, 0x31, 0xde, - 0xad, 0x6c, 0x82, 0x1e, 0x41, 0x70, 0x45, 0x3c, 0x61, 0x4d, 0x72, 0xb6, 0x0d, 0x21, 0x16, 0x03, - 0x61, 0xf4, 0xa7, 0xb2, 0xd8, 0x4a, 0xff, 0x19, 0xb8, 0xc5, 0xa6, 0xab, 0x74, 0xeb, 0xc4, 0x72, - 0xad, 0x1a, 0xd8, 0x87, 0x56, 0x41, 0x3c, 0x59, 0x64, 0x97, 0xdb, 0xe1, 0x0f, 0xc1, 0x4f, 0xe9, - 0x9a, 0x98, 0xb6, 0x83, 0xbf, 0x30, 0x8f, 0x87, 0x81, 0x6f, 0xdf, 0x61, 0x08, 0x6d, 0xb5, 0x5c, - 0xdc, 0x35, 0xc2, 0xbd, 0xd5, 0xff, 0x50, 0x61, 0xa2, 0xc6, 0x40, 0xe0, 0x1b, 0xd8, 0xd5, 0x74, - 0x43, 0xba, 0xa0, 0xd3, 0x7f, 0xa5, 0x1e, 0xd8, 0xef, 0x89, 0x0b, 0x5c, 0xbb, 0xcb, 0x7e, 0xe7, - 0xf6, 0xde, 0x46, 0x0d, 0x1c, 0x00, 0x5c, 0x98, 0x47, 0xef, 0xa3, 0x56, 0x4c, 0xb8, 0xaa, 0xdb, - 0x97, 0x70, 0xe3, 0x34, 0x2f, 0xa1, 0xb3, 0x62, 0x58, 0x13, 0xb6, 0x60, 0x5d, 0xf8, 0x36, 0x75, - 0xf4, 0x27, 0x00, 0x00, 0xff, 0xff, 0x95, 0xf4, 0xe3, 0x82, 0x7a, 0x05, 0x00, 0x00, -} diff --git a/libs/db/remotedb/proto/defs.proto b/libs/db/remotedb/proto/defs.proto deleted file mode 100644 index 70471f2347e..00000000000 --- a/libs/db/remotedb/proto/defs.proto +++ /dev/null @@ -1,71 +0,0 @@ -syntax = "proto3"; - -package protodb; - -message Batch { - repeated Operation ops = 1; -} - -message Operation { - Entity entity = 1; - enum Type { - SET = 0; - DELETE = 1; - } - Type type = 2; -} - -message Entity { - int32 id = 1; - bytes key = 2; - bytes value = 3; - bool exists = 4; - bytes start = 5; - bytes end = 6; - string err = 7; - int64 created_at = 8; -} - -message Nothing { -} - -message Domain { - bytes start = 1; - bytes end = 2; -} - -message Iterator { - Domain domain = 1; - bool valid = 2; - bytes key = 3; - bytes value = 4; -} - -message Stats { - map data = 1; - int64 time_at = 2; -} - -message Init { - string Type = 1; - string Name = 2; - string Dir = 3; -} - -service DB { - rpc init(Init) returns (Entity) {} - rpc get(Entity) returns (Entity) {} - rpc getStream(stream Entity) returns (stream Entity) {} - - rpc has(Entity) returns (Entity) {} - rpc set(Entity) returns (Nothing) {} - rpc setSync(Entity) returns (Nothing) {} - rpc delete(Entity) returns (Nothing) {} - rpc deleteSync(Entity) returns (Nothing) {} - rpc iterator(Entity) returns (stream Iterator) {} - rpc reverseIterator(Entity) returns (stream Iterator) {} - // rpc print(Nothing) returns (Entity) {} - rpc stats(Nothing) returns (Stats) {} - rpc batchWrite(Batch) returns (Nothing) {} - rpc batchWriteSync(Batch) returns (Nothing) {} -} diff --git a/libs/db/remotedb/remotedb.go b/libs/db/remotedb/remotedb.go deleted file mode 100644 index c70d54b9ecb..00000000000 --- a/libs/db/remotedb/remotedb.go +++ /dev/null @@ -1,266 +0,0 @@ -package remotedb - -import ( - "context" - "fmt" - - "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/db/remotedb/grpcdb" - protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto" -) - -type RemoteDB struct { - ctx context.Context - dc protodb.DBClient -} - -func NewRemoteDB(serverAddr string, serverKey string) (*RemoteDB, error) { - return newRemoteDB(grpcdb.NewClient(serverAddr, serverKey)) -} - -func newRemoteDB(gdc protodb.DBClient, err error) (*RemoteDB, error) { - if err != nil { - return nil, err - } - return &RemoteDB{dc: gdc, ctx: context.Background()}, nil -} - -type Init struct { - Dir string - Name string - Type string -} - -func (rd *RemoteDB) InitRemote(in *Init) error { - _, err := rd.dc.Init(rd.ctx, &protodb.Init{Dir: in.Dir, Type: in.Type, Name: in.Name}) - return err -} - -var _ db.DB = (*RemoteDB)(nil) - -// Close is a noop currently -func (rd *RemoteDB) Close() { -} - -func (rd *RemoteDB) Delete(key []byte) { - if _, err := rd.dc.Delete(rd.ctx, &protodb.Entity{Key: key}); err != nil { - panic(fmt.Sprintf("RemoteDB.Delete: %v", err)) - } -} - -func (rd *RemoteDB) DeleteSync(key []byte) { - if _, err := rd.dc.DeleteSync(rd.ctx, &protodb.Entity{Key: key}); err != nil { - panic(fmt.Sprintf("RemoteDB.DeleteSync: %v", err)) - } -} - -func (rd *RemoteDB) Set(key, value []byte) { - if _, err := rd.dc.Set(rd.ctx, &protodb.Entity{Key: key, Value: value}); err != nil { - panic(fmt.Sprintf("RemoteDB.Set: %v", err)) - } -} - -func (rd *RemoteDB) SetSync(key, value []byte) { - if _, err := rd.dc.SetSync(rd.ctx, &protodb.Entity{Key: key, Value: value}); err != nil { - panic(fmt.Sprintf("RemoteDB.SetSync: %v", err)) - } -} - -func (rd *RemoteDB) Get(key []byte) []byte { - res, err := rd.dc.Get(rd.ctx, &protodb.Entity{Key: key}) - if err != nil { - panic(fmt.Sprintf("RemoteDB.Get error: %v", err)) - } - return res.Value -} - -func (rd *RemoteDB) Has(key []byte) bool { - res, err := rd.dc.Has(rd.ctx, &protodb.Entity{Key: key}) - if err != nil { - panic(fmt.Sprintf("RemoteDB.Has error: %v", err)) - } - return res.Exists -} - -func (rd *RemoteDB) ReverseIterator(start, end []byte) db.Iterator { - dic, err := rd.dc.ReverseIterator(rd.ctx, &protodb.Entity{Start: start, End: end}) - if err != nil { - panic(fmt.Sprintf("RemoteDB.Iterator error: %v", err)) - } - return makeReverseIterator(dic) -} - -func (rd *RemoteDB) NewBatch() db.Batch { - return &batch{ - db: rd, - ops: nil, - } -} - -// TODO: Implement Print when db.DB implements a method -// to print to a string and not db.Print to stdout. -func (rd *RemoteDB) Print() { - panic("Unimplemented") -} - -func (rd *RemoteDB) Stats() map[string]string { - stats, err := rd.dc.Stats(rd.ctx, &protodb.Nothing{}) - if err != nil { - panic(fmt.Sprintf("RemoteDB.Stats error: %v", err)) - } - if stats == nil { - return nil - } - return stats.Data -} - -func (rd *RemoteDB) Iterator(start, end []byte) db.Iterator { - dic, err := rd.dc.Iterator(rd.ctx, &protodb.Entity{Start: start, End: end}) - if err != nil { - panic(fmt.Sprintf("RemoteDB.Iterator error: %v", err)) - } - return makeIterator(dic) -} - -func makeIterator(dic protodb.DB_IteratorClient) db.Iterator { - return &iterator{dic: dic} -} - -func makeReverseIterator(dric protodb.DB_ReverseIteratorClient) db.Iterator { - return &reverseIterator{dric: dric} -} - -type reverseIterator struct { - dric protodb.DB_ReverseIteratorClient - cur *protodb.Iterator -} - -var _ db.Iterator = (*iterator)(nil) - -func (rItr *reverseIterator) Valid() bool { - return rItr.cur != nil && rItr.cur.Valid -} - -func (rItr *reverseIterator) Domain() (start, end []byte) { - if rItr.cur == nil || rItr.cur.Domain == nil { - return nil, nil - } - return rItr.cur.Domain.Start, rItr.cur.Domain.End -} - -// Next advances the current reverseIterator -func (rItr *reverseIterator) Next() { - var err error - rItr.cur, err = rItr.dric.Recv() - if err != nil { - panic(fmt.Sprintf("RemoteDB.ReverseIterator.Next error: %v", err)) - } -} - -func (rItr *reverseIterator) Key() []byte { - if rItr.cur == nil { - return nil - } - return rItr.cur.Key -} - -func (rItr *reverseIterator) Value() []byte { - if rItr.cur == nil { - return nil - } - return rItr.cur.Value -} - -func (rItr *reverseIterator) Close() { -} - -// iterator implements the db.Iterator by retrieving -// streamed iterators from the remote backend as -// needed. It is NOT safe for concurrent usage, -// matching the behavior of other iterators. -type iterator struct { - dic protodb.DB_IteratorClient - cur *protodb.Iterator -} - -var _ db.Iterator = (*iterator)(nil) - -func (itr *iterator) Valid() bool { - return itr.cur != nil && itr.cur.Valid -} - -func (itr *iterator) Domain() (start, end []byte) { - if itr.cur == nil || itr.cur.Domain == nil { - return nil, nil - } - return itr.cur.Domain.Start, itr.cur.Domain.End -} - -// Next advances the current iterator -func (itr *iterator) Next() { - var err error - itr.cur, err = itr.dic.Recv() - if err != nil { - panic(fmt.Sprintf("RemoteDB.Iterator.Next error: %v", err)) - } -} - -func (itr *iterator) Key() []byte { - if itr.cur == nil { - return nil - } - return itr.cur.Key -} - -func (itr *iterator) Value() []byte { - if itr.cur == nil { - return nil - } - return itr.cur.Value -} - -func (itr *iterator) Close() { - err := itr.dic.CloseSend() - if err != nil { - panic(fmt.Sprintf("Error closing iterator: %v", err)) - } -} - -type batch struct { - db *RemoteDB - ops []*protodb.Operation -} - -var _ db.Batch = (*batch)(nil) - -func (bat *batch) Set(key, value []byte) { - op := &protodb.Operation{ - Entity: &protodb.Entity{Key: key, Value: value}, - Type: protodb.Operation_SET, - } - bat.ops = append(bat.ops, op) -} - -func (bat *batch) Delete(key []byte) { - op := &protodb.Operation{ - Entity: &protodb.Entity{Key: key}, - Type: protodb.Operation_DELETE, - } - bat.ops = append(bat.ops, op) -} - -func (bat *batch) Write() { - if _, err := bat.db.dc.BatchWrite(bat.db.ctx, &protodb.Batch{Ops: bat.ops}); err != nil { - panic(fmt.Sprintf("RemoteDB.BatchWrite: %v", err)) - } -} - -func (bat *batch) WriteSync() { - if _, err := bat.db.dc.BatchWriteSync(bat.db.ctx, &protodb.Batch{Ops: bat.ops}); err != nil { - panic(fmt.Sprintf("RemoteDB.BatchWriteSync: %v", err)) - } -} - -func (bat *batch) Close() { - bat.ops = nil -} diff --git a/libs/db/remotedb/remotedb_test.go b/libs/db/remotedb/remotedb_test.go deleted file mode 100644 index 0e7319971b4..00000000000 --- a/libs/db/remotedb/remotedb_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package remotedb_test - -import ( - "net" - "os" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/libs/db/remotedb" - "github.com/tendermint/tendermint/libs/db/remotedb/grpcdb" -) - -func TestRemoteDB(t *testing.T) { - cert := "test.crt" - key := "test.key" - ln, err := net.Listen("tcp", "0.0.0.0:0") - require.Nil(t, err, "expecting a port to have been assigned on which we can listen") - srv, err := grpcdb.NewServer(cert, key) - require.Nil(t, err) - defer srv.Stop() - go func() { - if err := srv.Serve(ln); err != nil { - t.Fatalf("BindServer: %v", err) - } - }() - - client, err := remotedb.NewRemoteDB(ln.Addr().String(), cert) - require.Nil(t, err, "expecting a successful client creation") - dbName := "test-remote-db" - require.Nil(t, client.InitRemote(&remotedb.Init{Name: dbName, Type: "leveldb"})) - defer func() { - err := os.RemoveAll(dbName + ".db") - if err != nil { - panic(err) - } - }() - - k1 := []byte("key-1") - v1 := client.Get(k1) - require.Equal(t, 0, len(v1), "expecting no key1 to have been stored, got %X (%s)", v1, v1) - vv1 := []byte("value-1") - client.Set(k1, vv1) - gv1 := client.Get(k1) - require.Equal(t, gv1, vv1) - - // Simple iteration - itr := client.Iterator(nil, nil) - itr.Next() - require.Equal(t, itr.Key(), []byte("key-1")) - require.Equal(t, itr.Value(), []byte("value-1")) - require.Panics(t, itr.Next) - itr.Close() - - // Set some more keys - k2 := []byte("key-2") - v2 := []byte("value-2") - client.SetSync(k2, v2) - has := client.Has(k2) - require.True(t, has) - gv2 := client.Get(k2) - require.Equal(t, gv2, v2) - - // More iteration - itr = client.Iterator(nil, nil) - itr.Next() - require.Equal(t, itr.Key(), []byte("key-1")) - require.Equal(t, itr.Value(), []byte("value-1")) - itr.Next() - require.Equal(t, itr.Key(), []byte("key-2")) - require.Equal(t, itr.Value(), []byte("value-2")) - require.Panics(t, itr.Next) - itr.Close() - - // Deletion - client.Delete(k1) - client.DeleteSync(k2) - gv1 = client.Get(k1) - gv2 = client.Get(k2) - require.Equal(t, len(gv2), 0, "after deletion, not expecting the key to exist anymore") - require.Equal(t, len(gv1), 0, "after deletion, not expecting the key to exist anymore") - - // Batch tests - set - k3 := []byte("key-3") - k4 := []byte("key-4") - k5 := []byte("key-5") - v3 := []byte("value-3") - v4 := []byte("value-4") - v5 := []byte("value-5") - bat := client.NewBatch() - bat.Set(k3, v3) - bat.Set(k4, v4) - rv3 := client.Get(k3) - require.Equal(t, 0, len(rv3), "expecting no k3 to have been stored") - rv4 := client.Get(k4) - require.Equal(t, 0, len(rv4), "expecting no k4 to have been stored") - bat.Write() - rv3 = client.Get(k3) - require.Equal(t, rv3, v3, "expecting k3 to have been stored") - rv4 = client.Get(k4) - require.Equal(t, rv4, v4, "expecting k4 to have been stored") - - // Batch tests - deletion - bat = client.NewBatch() - bat.Delete(k4) - bat.Delete(k3) - bat.WriteSync() - rv3 = client.Get(k3) - require.Equal(t, 0, len(rv3), "expecting k3 to have been deleted") - rv4 = client.Get(k4) - require.Equal(t, 0, len(rv4), "expecting k4 to have been deleted") - - // Batch tests - set and delete - bat = client.NewBatch() - bat.Set(k4, v4) - bat.Set(k5, v5) - bat.Delete(k4) - bat.WriteSync() - rv4 = client.Get(k4) - require.Equal(t, 0, len(rv4), "expecting k4 to have been deleted") - rv5 := client.Get(k5) - require.Equal(t, rv5, v5, "expecting k5 to have been stored") -} diff --git a/libs/db/remotedb/test.crt b/libs/db/remotedb/test.crt deleted file mode 100644 index bdc8a0f29e7..00000000000 --- a/libs/db/remotedb/test.crt +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEQTCCAimgAwIBAgIRANqF1HD19i/uvQ3n62TAKTwwDQYJKoZIhvcNAQELBQAw -GTEXMBUGA1UEAxMOdGVuZGVybWludC5jb20wHhcNMTgwNzAyMDMwNzMyWhcNMjAw -MTAyMDMwNzMwWjANMQswCQYDVQQDEwI6OjCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAOuWUMCSzYJmvKU1vsouDTe7OxnPWO3oV0FjSH8vKYoi2zpZQX35 -dQDPtLDF2/v/ANZJ5pzMJR8yMMtEQ4tWxKuGzJw1ZgTgHtASPbj/M5fDnDO7Hqg4 -D09eLTkZAUfiBf6BzDyQIHn22CUexhaS70TbIT9AOAoOsGXMZz9d+iImKIm+gbzf -pR52LNbBGesHWGjwIuGF4InstIMsKSwGv2DctzhWI+i/m5Goi3rd1V8z/lzUbsf1 -0uXqQcSfTyv3ee6YiCWj2W8vcdc5H+B6KzSlGjAR4sRcHTHOQJYO9BgA9evQ3qsJ -Pp00iez13RdheJWPtbfUqQy4gdpu8HFeZx8CAwEAAaOBjzCBjDAOBgNVHQ8BAf8E -BAMCA7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRc -XBo+bJILrLcJiGkTWeMPpXb1TDAfBgNVHSMEGDAWgBQqk1Xu65Ww7EBCROw4KLGw -KuToaDAbBgNVHREEFDAShxAAAAAAAAAAAAAAAAAAAAAAMA0GCSqGSIb3DQEBCwUA -A4ICAQAbGsIMhL8clczNmhGl9xZhmyNz6FbLq6g163x9LTgfvwHPt+7urthtd++O -uy4Ut8zFurh/yk7eooPlzf8jO7QUJBAFVy4vj8IcsvpWbFa7cuEOIulbjIzyAm/v -lgy7vUQ6xrWn8x8O9K1ww9z7wugwCyl22BD0wSHZKclJz++AwpL6vUVOD76IIuJO -+S6bE6z26/0ndpundh2AkA++2eIleD6ygnTeTl0PWu6aGoCggBmos50f8KgYHZF/ -OZVef203kDls9xCaOiMzaU91VsgLqq/gNcT+2cBd5r3IZTY3C8Rve6EEHS+/4zxf -PKlmiLN7lU9GFZogKecYzY+zPT7OArY7OVFnGTo4qdhdmxnXzHsI+anMCjxLOgEJ -381hyplQGPQOouEupCBxFcwa7oMYoGu20+1nLWYEqFcIXCeyH+s77MyteJSsseqL -xivG5PT+jKJn9hrnFb39bBmht9Vsa+Th6vk953zi5wCSe1j2wXsxFaENDq6BQZOK -f86Kp86M2elYnv3lJ3j2DE2ZTMpw+PA5ThYUnB+HVqYeeB2Y3ErRS8P1FOp1LBE8 -+eTz7yXQO5OM2wdYhNNL1zDri/41fHXi9b6337PZVqc39GM+N74x/O4Q7xEBiWgQ -T0dT8SNwf55kv63MeZh63ImxFV0FNRkCteYLcJMle3ohIY4zyQ== ------END CERTIFICATE----- diff --git a/libs/db/remotedb/test.key b/libs/db/remotedb/test.key deleted file mode 100644 index 14d2855847d..00000000000 --- a/libs/db/remotedb/test.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpgIBAAKCAQEA65ZQwJLNgma8pTW+yi4NN7s7Gc9Y7ehXQWNIfy8piiLbOllB -ffl1AM+0sMXb+/8A1knmnMwlHzIwy0RDi1bEq4bMnDVmBOAe0BI9uP8zl8OcM7se -qDgPT14tORkBR+IF/oHMPJAgefbYJR7GFpLvRNshP0A4Cg6wZcxnP136IiYoib6B -vN+lHnYs1sEZ6wdYaPAi4YXgiey0gywpLAa/YNy3OFYj6L+bkaiLet3VXzP+XNRu -x/XS5epBxJ9PK/d57piIJaPZby9x1zkf4HorNKUaMBHixFwdMc5Alg70GAD169De -qwk+nTSJ7PXdF2F4lY+1t9SpDLiB2m7wcV5nHwIDAQABAoIBAQCB2/ilPgaUE8d2 -ldqWHa5hgw4/2uCdO04ll/GVUczm/PG1BxAnvYL2MIfcTSRGkrjGZjP9SDZKLONi -mD1XKDv+hK5yiKi0lUnGzddCC0JILKYEieeLOGOQD0yERblEA13kfW20EIomUJ+y -TnVIajQD03pPIDoDqTco1fQvpMDFYw5Q//UhH7VBC261GO1akvhT2Gqdb4aKLaYQ -iDW9IEButL5cRKIJuRxToB/JbmPVEF7xIZtm0sf9dtYVOlBQLeID0uHXgaci0enc -de6GMajmj7NFqc36ypb+Ct18fqEwQBYD+TSQdKs7/lMsAXwRjd5HW4RbYiMZyYnf -Dxgh7QVBAoGBAP9aLLIUcIG7+gk1x7xd+8wRhfo+dhsungeCluSigI9AsfDr6dpR -G9/0lEJH56noZZKQueACTmj7shmRB40xFFLc8w0IDRZCnofsl+Z15k9K84uFPA3W -hdZH9nMieU/mRKdcUYK7pHGqbicHTaJQ5ydZ+xb2E+zYQHOzYpQacHv/AoGBAOwv -TjDZSiassnAPYmmfcHtkUF4gf7PTpiZfH0hXHGAb0mJX4cXAoktAeDeHSi2tz3LW -dAc0ReP8Pdf3uSNv7wkJ1KpNRxAhU5bhnDFmjRc7gMZknVOU+az2M+4yGOn/SOiJ -I6uMHgQDS/VsI+N583n6gbGxVHbQfr9TOc4bLpThAoGBAKin0JmWMnEdzRnEMbZS -hPrWIB2Wn794XNws/qjoQ+1aF60+xGhz5etXyYy1nWd1nZDekkZIf62LgKiuR8ST -xA6u7MGQrcQkID06oWGQQZvhr1ZZm76wEBnl0ftdq66AMpwvt46XjReeL78LbdVl -hidRoSwbQDHQ61EADH4xsFXVAoGBAISXqhXSZsZ/fU1b1avmTod3MYcmR4r07vnr -vOwnu05ZUCrVm3IhSvtkHhlOYl5yjVuy+UByICp1mWJ9N/qlBFTWqAVTjOmJTBwQ -XFd/cwXv6cN3CLu7js+DCHRYu5PiNVQWaWgNKWynTSViqGM0O3PnJphTLU/mjMFs -P69toyEBAoGBALh9YsqxHdYdS5WK9chzDfGlaTQ79jwN+gEzQuP1ooLF0JkMgh5W -//2C6kCrgBsGTm1gfHAjEfC04ZDZLFbKLm56YVKUGL6JJNapm6e5kfiZGjbRKWAg -ViCeRS2qQnVbH74GfHyimeTPDI9cJMiJfDDTPbfosqWSsPEcg2jfsySJ ------END RSA PRIVATE KEY----- diff --git a/libs/db/types.go b/libs/db/types.go deleted file mode 100644 index 30f8afd189c..00000000000 --- a/libs/db/types.go +++ /dev/null @@ -1,136 +0,0 @@ -package db - -// DBs are goroutine safe. -type DB interface { - - // Get returns nil iff key doesn't exist. - // A nil key is interpreted as an empty byteslice. - // CONTRACT: key, value readonly []byte - Get([]byte) []byte - - // Has checks if a key exists. - // A nil key is interpreted as an empty byteslice. - // CONTRACT: key, value readonly []byte - Has(key []byte) bool - - // Set sets the key. - // A nil key is interpreted as an empty byteslice. - // CONTRACT: key, value readonly []byte - Set([]byte, []byte) - SetSync([]byte, []byte) - - // Delete deletes the key. - // A nil key is interpreted as an empty byteslice. - // CONTRACT: key readonly []byte - Delete([]byte) - DeleteSync([]byte) - - // Iterate over a domain of keys in ascending order. End is exclusive. - // Start must be less than end, or the Iterator is invalid. - // A nil start is interpreted as an empty byteslice. - // If end is nil, iterates up to the last item (inclusive). - // CONTRACT: No writes may happen within a domain while an iterator exists over it. - // CONTRACT: start, end readonly []byte - Iterator(start, end []byte) Iterator - - // Iterate over a domain of keys in descending order. End is exclusive. - // Start must be less than end, or the Iterator is invalid. - // If start is nil, iterates up to the first/least item (inclusive). - // If end is nil, iterates from the last/greatest item (inclusive). - // CONTRACT: No writes may happen within a domain while an iterator exists over it. - // CONTRACT: start, end readonly []byte - ReverseIterator(start, end []byte) Iterator - - // Closes the connection. - Close() - - // Creates a batch for atomic updates. - NewBatch() Batch - - // For debugging - Print() - - // Stats returns a map of property values for all keys and the size of the cache. - Stats() map[string]string -} - -//---------------------------------------- -// Batch - -// Batch Close must be called when the program no longer needs the object. -type Batch interface { - SetDeleter - Write() - WriteSync() - Close() -} - -type SetDeleter interface { - Set(key, value []byte) // CONTRACT: key, value readonly []byte - Delete(key []byte) // CONTRACT: key readonly []byte -} - -//---------------------------------------- -// Iterator - -/* - Usage: - - var itr Iterator = ... - defer itr.Close() - - for ; itr.Valid(); itr.Next() { - k, v := itr.Key(); itr.Value() - // ... - } -*/ -type Iterator interface { - - // The start & end (exclusive) limits to iterate over. - // If end < start, then the Iterator goes in reverse order. - // - // A domain of ([]byte{12, 13}, []byte{12, 14}) will iterate - // over anything with the prefix []byte{12, 13}. - // - // The smallest key is the empty byte array []byte{} - see BeginningKey(). - // The largest key is the nil byte array []byte(nil) - see EndingKey(). - // CONTRACT: start, end readonly []byte - Domain() (start []byte, end []byte) - - // Valid returns whether the current position is valid. - // Once invalid, an Iterator is forever invalid. - Valid() bool - - // Next moves the iterator to the next sequential key in the database, as - // defined by order of iteration. - // - // If Valid returns false, this method will panic. - Next() - - // Key returns the key of the cursor. - // If Valid returns false, this method will panic. - // CONTRACT: key readonly []byte - Key() (key []byte) - - // Value returns the value of the cursor. - // If Valid returns false, this method will panic. - // CONTRACT: value readonly []byte - Value() (value []byte) - - // Close releases the Iterator. - Close() -} - -// For testing convenience. -func bz(s string) []byte { - return []byte(s) -} - -// We defensively turn nil keys or values into []byte{} for -// most operations. -func nonNilBytes(bz []byte) []byte { - if bz == nil { - return []byte{} - } - return bz -} diff --git a/libs/db/util.go b/libs/db/util.go deleted file mode 100644 index e927c354894..00000000000 --- a/libs/db/util.go +++ /dev/null @@ -1,45 +0,0 @@ -package db - -import ( - "bytes" -) - -func cp(bz []byte) (ret []byte) { - ret = make([]byte, len(bz)) - copy(ret, bz) - return ret -} - -// Returns a slice of the same length (big endian) -// except incremented by one. -// Returns nil on overflow (e.g. if bz bytes are all 0xFF) -// CONTRACT: len(bz) > 0 -func cpIncr(bz []byte) (ret []byte) { - if len(bz) == 0 { - panic("cpIncr expects non-zero bz length") - } - ret = cp(bz) - for i := len(bz) - 1; i >= 0; i-- { - if ret[i] < byte(0xFF) { - ret[i]++ - return - } - ret[i] = byte(0x00) - if i == 0 { - // Overflow - return nil - } - } - return nil -} - -// See DB interface documentation for more information. -func IsKeyInDomain(key, start, end []byte) bool { - if bytes.Compare(key, start) < 0 { - return false - } - if end != nil && bytes.Compare(end, key) <= 0 { - return false - } - return true -} diff --git a/libs/db/util_test.go b/libs/db/util_test.go deleted file mode 100644 index 07f9dd23eec..00000000000 --- a/libs/db/util_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package db - -import ( - "fmt" - "os" - "testing" -) - -// Empty iterator for empty db. -func TestPrefixIteratorNoMatchNil(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - itr := IteratePrefix(db, []byte("2")) - - checkInvalid(t, itr) - }) - } -} - -// Empty iterator for db populated after iterator created. -func TestPrefixIteratorNoMatch1(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - itr := IteratePrefix(db, []byte("2")) - db.SetSync(bz("1"), bz("value_1")) - - checkInvalid(t, itr) - }) - } -} - -// Empty iterator for prefix starting after db entry. -func TestPrefixIteratorNoMatch2(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - db.SetSync(bz("3"), bz("value_3")) - itr := IteratePrefix(db, []byte("4")) - - checkInvalid(t, itr) - }) - } -} - -// Iterator with single val for db with single val, starting from that val. -func TestPrefixIteratorMatch1(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - db.SetSync(bz("2"), bz("value_2")) - itr := IteratePrefix(db, bz("2")) - - checkValid(t, itr, true) - checkItem(t, itr, bz("2"), bz("value_2")) - checkNext(t, itr, false) - - // Once invalid... - checkInvalid(t, itr) - }) - } -} - -// Iterator with prefix iterates over everything with same prefix. -func TestPrefixIteratorMatches1N(t *testing.T) { - for backend := range backends { - t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { - db, dir := newTempDB(t, backend) - defer os.RemoveAll(dir) - - // prefixed - db.SetSync(bz("a/1"), bz("value_1")) - db.SetSync(bz("a/3"), bz("value_3")) - - // not - db.SetSync(bz("b/3"), bz("value_3")) - db.SetSync(bz("a-3"), bz("value_3")) - db.SetSync(bz("a.3"), bz("value_3")) - db.SetSync(bz("abcdefg"), bz("value_3")) - itr := IteratePrefix(db, bz("a/")) - - checkValid(t, itr, true) - checkItem(t, itr, bz("a/1"), bz("value_1")) - checkNext(t, itr, true) - checkItem(t, itr, bz("a/3"), bz("value_3")) - - // Bad! - checkNext(t, itr, false) - - //Once invalid... - checkInvalid(t, itr) - }) - } -} diff --git a/lite/dbprovider.go b/lite/dbprovider.go index 9a3636d57d6..64e0d8fde40 100644 --- a/lite/dbprovider.go +++ b/lite/dbprovider.go @@ -7,10 +7,10 @@ import ( amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - dbm "github.com/tendermint/tendermint/libs/db" log "github.com/tendermint/tendermint/libs/log" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) var _ PersistentProvider = (*DBProvider)(nil) diff --git a/lite/dynamic_verifier_test.go b/lite/dynamic_verifier_test.go index fcb97ffa5f9..96dfb00e86e 100644 --- a/lite/dynamic_verifier_test.go +++ b/lite/dynamic_verifier_test.go @@ -2,15 +2,14 @@ package lite import ( "fmt" - "github.com/tendermint/tendermint/types" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tendermint/libs/db" log "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) func TestInquirerValidPath(t *testing.T) { diff --git a/lite/provider_test.go b/lite/provider_test.go index 94b467de812..cd596a8b939 100644 --- a/lite/provider_test.go +++ b/lite/provider_test.go @@ -6,11 +6,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tendermint/libs/db" log "github.com/tendermint/tendermint/libs/log" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) // missingProvider doesn't store anything, always a miss. diff --git a/lite/proxy/verifier.go b/lite/proxy/verifier.go index b7c11f18ef6..2119a7aeeb1 100644 --- a/lite/proxy/verifier.go +++ b/lite/proxy/verifier.go @@ -2,10 +2,10 @@ package proxy import ( cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" log "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/lite" lclient "github.com/tendermint/tendermint/lite/client" + dbm "github.com/tendermint/tm-db" ) func NewVerifier(chainID, rootDir string, client lclient.SignStatusClient, logger log.Logger, cacheSize int) (*lite.DynamicVerifier, error) { @@ -14,7 +14,7 @@ func NewVerifier(chainID, rootDir string, client lclient.SignStatusClient, logge logger.Info("lite/proxy/NewVerifier()...", "chainID", chainID, "rootDir", rootDir, "client", client) memProvider := lite.NewDBProvider("trusted.mem", dbm.NewMemDB()).SetLimit(cacheSize) - lvlProvider := lite.NewDBProvider("trusted.lvl", dbm.NewDB("trust-base", dbm.LevelDBBackend, rootDir)) + lvlProvider := lite.NewDBProvider("trusted.lvl", dbm.NewDB("trust-base", dbm.GoLevelDBBackend, rootDir)) trust := lite.NewMultiProvider( memProvider, lvlProvider, diff --git a/node/node.go b/node/node.go index cb55c6dd315..689dd129f8a 100644 --- a/node/node.go +++ b/node/node.go @@ -14,7 +14,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/cors" - amino "github.com/tendermint/go-amino" abci "github.com/tendermint/tendermint/abci/types" bc "github.com/tendermint/tendermint/blockchain" @@ -23,7 +22,6 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/evidence" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" @@ -33,7 +31,7 @@ import ( rpccore "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" grpccore "github.com/tendermint/tendermint/rpc/grpc" - "github.com/tendermint/tendermint/rpc/lib/server" + rpcserver "github.com/tendermint/tendermint/rpc/lib/server" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/state/txindex/kv" @@ -41,6 +39,7 @@ import ( "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" "github.com/tendermint/tendermint/version" + dbm "github.com/tendermint/tm-db" ) //------------------------------------------------------------------------------ diff --git a/p2p/trust/store.go b/p2p/trust/store.go index d6b4c049d51..3e8fd16ef53 100644 --- a/p2p/trust/store.go +++ b/p2p/trust/store.go @@ -10,7 +10,7 @@ import ( "time" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" + dbm "github.com/tendermint/tm-db" ) const defaultStorePeriodicSaveInterval = 1 * time.Minute diff --git a/p2p/trust/store_test.go b/p2p/trust/store_test.go index e1bea863615..d6498d82310 100644 --- a/p2p/trust/store_test.go +++ b/p2p/trust/store_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" ) func TestTrustMetricStoreSaveLoad(t *testing.T) { diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 23649544372..653ec7b892f 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -3,7 +3,6 @@ package core import ( "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" @@ -12,6 +11,7 @@ import ( sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) const ( diff --git a/state/execution.go b/state/execution.go index 6c04beee089..92317a9d6aa 100644 --- a/state/execution.go +++ b/state/execution.go @@ -7,18 +7,17 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) const ( - HaltTagKey = "halt_blockchain" - HaltTagValue = "true" + HaltTagKey = "halt_blockchain" + HaltTagValue = "true" UpgradeFailureTagKey = "upgrade_failure" - ) //----------------------------------------------------------------------------- @@ -285,8 +284,7 @@ func execBlockOnProxyApp( return nil, err } - - if tag, ok := abci.GetTagByKey(abciResponses.EndBlock.Tags, UpgradeFailureTagKey); ok{ + if tag, ok := abci.GetTagByKey(abciResponses.EndBlock.Tags, UpgradeFailureTagKey); ok { return nil, fmt.Errorf(string(tag.Value)) } @@ -299,9 +297,9 @@ func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorS state := LoadState(stateDB) // For replaying blocks, load history validator set - if block.Height > 1 && block.Height != state.LastBlockHeight + 1 { + if block.Height > 1 && block.Height != state.LastBlockHeight+1 { var err error - lastValSet, err = LoadValidators(stateDB, block.Height - 1) + lastValSet, err = LoadValidators(stateDB, block.Height-1) if err != nil { panic(fmt.Sprintf("failed to load validatorset at heith %d", state.LastBlockHeight)) } diff --git a/state/execution_test.go b/state/execution_test.go index 21df1ee56e7..f1cc1b65347 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -8,18 +8,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - tmtime "github.com/tendermint/tendermint/types/time" - "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) var ( diff --git a/state/state_test.go b/state/state_test.go index 2ca5f8b2108..b7485600c58 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -7,15 +7,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - - cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) // setupTestCase does setup common to all test cases. diff --git a/state/store.go b/state/store.go index 13b57b9de2a..e082d58c575 100644 --- a/state/store.go +++ b/state/store.go @@ -5,8 +5,8 @@ import ( 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/types" + dbm "github.com/tendermint/tm-db" ) //------------------------------------------------------------------------ diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index 52ae396bf96..03f3304c98f 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -9,9 +9,9 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) func TestTxFilter(t *testing.T) { diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 5df9c985f01..84c031f110e 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -10,7 +10,7 @@ import ( "github.com/pkg/errors" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" + dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/libs/pubsub/query" "github.com/tendermint/tendermint/state/txindex" diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index 0f206514648..20128f3655b 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" - db "github.com/tendermint/tendermint/libs/db" + db "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/libs/pubsub/query" "github.com/tendermint/tendermint/state/txindex" diff --git a/state/validation.go b/state/validation.go index 4072290987d..9dc8232c040 100644 --- a/state/validation.go +++ b/state/validation.go @@ -2,14 +2,14 @@ package state import ( "bytes" + "encoding/hex" "errors" "fmt" - "encoding/hex" "strconv" "github.com/tendermint/tendermint/crypto" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) //----------------------------------------------------- From 1a49a6a50582dd6d48b5d4a552d77b4580c460bf Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Tue, 27 Aug 2019 18:37:48 +0800 Subject: [PATCH 07/31] config DisconnectWaitPeriod --- node/node.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/node/node.go b/node/node.go index 61933d56df9..f30535b3ca6 100644 --- a/node/node.go +++ b/node/node.go @@ -479,6 +479,12 @@ func NewNode(config *cfg.Config, &pex.PEXReactorConfig{ Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "), SeedMode: config.P2P.SeedMode, + // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000 + // blocks assuming 10s blocks ~ 28 hours. + // TODO (melekes): make it dynamic based on the actual block latencies + // from the live network. + // https://github.com/tendermint/tendermint/issues/3523 + SeedDisconnectWaitPeriod: 28 * time.Hour, }) pexReactor.SetLogger(logger.With("module", "pex")) sw.AddReactor("PEX", pexReactor) From cd550022e97e678099b07985a742aa236f664927 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 28 Aug 2019 10:41:32 +0800 Subject: [PATCH 08/31] fix PersistentPeers init and add net_test --- node/node.go | 13 ++++---- rpc/core/net.go | 14 ++++----- rpc/core/net_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 rpc/core/net_test.go diff --git a/node/node.go b/node/node.go index f30535b3ca6..4bad8ecde57 100644 --- a/node/node.go +++ b/node/node.go @@ -455,6 +455,11 @@ func NewNode(config *cfg.Config, p2pLogger.Info("P2P Node ID", "ID", nodeKey.ID(), "file", config.NodeKeyFile()) + err = sw.AddPersistentPeers(splitAndTrimEmpty(config.P2P.PersistentPeers, ",", " ")) + if err != nil { + return nil, errors.Wrap(err, "could not add peers from persistent_peers field") + } + // Optionally, start the pex reactor // // TODO: @@ -577,12 +582,8 @@ func (n *Node) OnStart() error { } // Always connect to persistent peers - if n.config.P2P.PersistentPeers != "" { - err = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " ")) - if err != nil { - return err - } - } + // parsing errors are handled above by AddPersistentPeers + _ = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " ")) return nil } diff --git a/rpc/core/net.go b/rpc/core/net.go index 6dba1583ec0..521469e5ba7 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -71,10 +71,8 @@ func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { if len(seeds) == 0 { return &ctypes.ResultDialSeeds{}, errors.New("No seeds provided") } - // starts go routines to dial each peer after random delays - logger.Info("DialSeeds", "addrBook", addrBook, "seeds", seeds) - err := p2pPeers.DialPeersAsync(seeds) - if err != nil { + logger.Info("DialSeeds", "seeds", seeds) + if err := p2pPeers.DialPeersAsync(seeds); err != nil { return &ctypes.ResultDialSeeds{}, err } return &ctypes.ResultDialSeeds{"Dialing seeds in progress. See /net_info for details"}, nil @@ -84,12 +82,12 @@ func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, if len(peers) == 0 { return &ctypes.ResultDialPeers{}, errors.New("No peers provided") } - // starts go routines to dial each peer after random delays - logger.Info("DialPeers", "addrBook", addrBook, "peers", peers, "persistent", persistent) - err := p2pPeers.DialPeersAsync(peers) - if err != nil { + logger.Info("DialPeers", "peers", peers, "persistent", persistent) + if err := p2pPeers.AddPersistentPeers(peers); err != nil { return &ctypes.ResultDialPeers{}, err } + // parsing errors are handled above by AddPersistentPeers + _ = p2pPeers.DialPeersAsync(peers) return &ctypes.ResultDialPeers{"Dialing peers in progress. See /net_info for details"}, nil } diff --git a/rpc/core/net_test.go b/rpc/core/net_test.go new file mode 100644 index 00000000000..5593e73de7f --- /dev/null +++ b/rpc/core/net_test.go @@ -0,0 +1,71 @@ +package core + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p" +) + +func TestUnsafeDialSeeds(t *testing.T) { + sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", + func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + logger = log.TestingLogger() + p2pPeers = sw + + testCases := []struct { + seeds []string + isErr bool + }{ + {[]string{}, true}, + {[]string{"d51fb70907db1c6c2d5237e78379b25cf1a37ab4@127.0.0.1:41198"}, false}, + {[]string{"127.0.0.1:41198"}, true}, + } + + for _, tc := range testCases { + res, err := UnsafeDialSeeds(tc.seeds) + if tc.isErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, res) + } + } +} + +func TestUnsafeDialPeers(t *testing.T) { + sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", + func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + logger = log.TestingLogger() + p2pPeers = sw + + testCases := []struct { + peers []string + isErr bool + }{ + {[]string{}, true}, + {[]string{"d51fb70907db1c6c2d5237e78379b25cf1a37ab4@127.0.0.1:41198"}, false}, + {[]string{"127.0.0.1:41198"}, true}, + } + + for _, tc := range testCases { + res, err := UnsafeDialPeers(tc.peers, false) + if tc.isErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, res) + } + } +} From 565bf7704e1fd3985837ed4c8d27fbde97ac8389 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 28 Aug 2019 10:49:59 +0800 Subject: [PATCH 09/31] peer state init too late --- consensus/reactor.go | 25 +++++++++++++++++-------- evidence/reactor.go | 5 ----- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/consensus/reactor.go b/consensus/reactor.go index 1f508319d2e..c8718c9cdad 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -155,15 +155,24 @@ func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor { } } -// AddPeer implements Reactor +// InitPeer implements Reactor by creating a state for the peer. +func (conR *ConsensusReactor) InitPeer(peer p2p.Peer) p2p.Peer { + peerState := NewPeerState(peer).SetLogger(conR.Logger) + peer.Set(types.PeerStateKey, peerState) + return peer +} + +// AddPeer implements Reactor by spawning multiple gossiping goroutines for the +// peer. func (conR *ConsensusReactor) AddPeer(peer p2p.Peer) { if !conR.IsRunning() { return } - // Create peerState for peer - peerState := NewPeerState(peer).SetLogger(conR.Logger) - peer.Set(types.PeerStateKey, peerState) + peerState, ok := peer.Get(types.PeerStateKey).(*PeerState) + if !ok { + panic(fmt.Sprintf("peer %v has no state", peer)) + } // Begin routines for this peer. go conR.gossipDataRoutine(peer, peerState) @@ -177,7 +186,7 @@ func (conR *ConsensusReactor) AddPeer(peer p2p.Peer) { } } -// RemovePeer implements Reactor +// RemovePeer is a noop. func (conR *ConsensusReactor) RemovePeer(peer p2p.Peer, reason interface{}) { if !conR.IsRunning() { return @@ -438,9 +447,9 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) { func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) { nrsMsg = &NewRoundStepMessage{ - Height: rs.Height, - Round: rs.Round, - Step: rs.Step, + Height: rs.Height, + Round: rs.Round, + Step: rs.Step, SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), LastCommitRound: rs.LastCommit.Round(), } diff --git a/evidence/reactor.go b/evidence/reactor.go index 6bb45e68922..2b83c324555 100644 --- a/evidence/reactor.go +++ b/evidence/reactor.go @@ -60,11 +60,6 @@ func (evR *EvidenceReactor) AddPeer(peer p2p.Peer) { go evR.broadcastEvidenceRoutine(peer) } -// RemovePeer implements Reactor. -func (evR *EvidenceReactor) RemovePeer(peer p2p.Peer, reason interface{}) { - // nothing to do -} - // Receive implements Reactor. // It adds any received evidence to the evpool. func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { From f3f657a7b88fb42d9a1b629825c1fd98ddcf5637 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 28 Aug 2019 16:53:49 +0800 Subject: [PATCH 10/31] improve error msg --- crypto/multisig/multisignature.go | 15 +++-- crypto/multisig/threshold_pubkey_test.go | 74 ++++++++++++++++++------ 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/crypto/multisig/multisignature.go b/crypto/multisig/multisignature.go index 0d1796890cb..1e3bef4e1f4 100644 --- a/crypto/multisig/multisignature.go +++ b/crypto/multisig/multisignature.go @@ -1,7 +1,8 @@ package multisig import ( - "errors" + "fmt" + "strings" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/multisig/bitarray" @@ -53,13 +54,19 @@ func (mSig *Multisignature) AddSignature(sig []byte, index int) { mSig.Sigs[newSigIndex] = sig } -// AddSignatureFromPubKey adds a signature to the multisig, -// at the index in keys corresponding to the provided pubkey. +// AddSignatureFromPubKey adds a signature to the multisig, at the index in +// keys corresponding to the provided pubkey. func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.PubKey, keys []crypto.PubKey) error { index := getIndex(pubkey, keys) if index == -1 { - return errors.New("provided key didn't exist in pubkeys") + keysStr := make([]string, len(keys)) + for i, k := range keys { + keysStr[i] = fmt.Sprintf("%X", k.Bytes()) + } + + return fmt.Errorf("provided key %X doesn't exist in pubkeys: \n%s", pubkey.Bytes(), strings.Join(keysStr, "\n")) } + mSig.AddSignature(sig, index) return nil } diff --git a/crypto/multisig/threshold_pubkey_test.go b/crypto/multisig/threshold_pubkey_test.go index 2d2632abd53..d1d7e803cfe 100644 --- a/crypto/multisig/threshold_pubkey_test.go +++ b/crypto/multisig/threshold_pubkey_test.go @@ -36,30 +36,68 @@ func TestThresholdMultisigValidCases(t *testing.T) { for tcIndex, tc := range cases { multisigKey := NewPubKeyMultisigThreshold(tc.k, tc.pubkeys) multisignature := NewMultisig(len(tc.pubkeys)) + for i := 0; i < tc.k-1; i++ { signingIndex := tc.signingIndices[i] - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.False(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig passed when i < k, tc %d, i %d", tcIndex, i) - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.Equal(t, i+1, len(multisignature.Sigs), - "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.False( + t, + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig passed when i < k, tc %d, i %d", tcIndex, i, + ) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.Equal( + t, + i+1, + len(multisignature.Sigs), + "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, + ) } - require.False(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig passed with k - 1 sigs, tc %d", tcIndex) - multisignature.AddSignatureFromPubKey(tc.signatures[tc.signingIndices[tc.k]], tc.pubkeys[tc.signingIndices[tc.k]], tc.pubkeys) - require.True(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig failed after k good signatures, tc %d", tcIndex) + + require.False( + t, + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig passed with k - 1 sigs, tc %d", tcIndex, + ) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[tc.signingIndices[tc.k]], tc.pubkeys[tc.signingIndices[tc.k]], tc.pubkeys), + ) + require.True( + t, + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig failed after k good signatures, tc %d", tcIndex, + ) + for i := tc.k + 1; i < len(tc.signingIndices); i++ { signingIndex := tc.signingIndices[i] - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.Equal(t, tc.passAfterKSignatures[i-tc.k-1], - multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i) - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.Equal(t, i+1, len(multisignature.Sigs), - "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.Equal( + t, + tc.passAfterKSignatures[i-tc.k-1], + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i, + ) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.Equal( + t, + i+1, + len(multisignature.Sigs), + "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, + ) } } } From f7d6873a3c6d64db1e556da2856f59fee4b7d4a0 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 28 Aug 2019 18:03:46 +0800 Subject: [PATCH 11/31] fix add empty self node's address --- node/node.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/node/node.go b/node/node.go index 689dd129f8a..f717be1719b 100644 --- a/node/node.go +++ b/node/node.go @@ -454,6 +454,10 @@ func NewNode(config *cfg.Config, p2pLogger.Info("P2P Node ID", "ID", nodeKey.ID(), "file", config.NodeKeyFile()) + addrBook, err := createAddrBookAndSetOnSwitch(config, sw, p2pLogger, nodeKey) + if err != nil { + return nil, errors.Wrap(err, "could not create addrbook") + } // Optionally, start the pex reactor // // TODO: @@ -466,12 +470,6 @@ func NewNode(config *cfg.Config, // // If PEX is on, it should handle dialing the seeds. Otherwise the switch does it. // Note we currently use the addrBook regardless at least for AddOurAddress - addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) - - // Add ourselves to addrbook to prevent dialing ourselves - addrBook.AddOurAddress(sw.NetAddress()) - - addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) if config.P2P.PexReactor { // TODO persistent peers ? so we can have their DNS addrs saved pexReactor := pex.NewPEXReactor(addrBook, @@ -520,6 +518,33 @@ func NewNode(config *cfg.Config, return node, nil } +func createAddrBookAndSetOnSwitch(config *cfg.Config, sw *p2p.Switch, + p2pLogger log.Logger, nodeKey *p2p.NodeKey) (pex.AddrBook, error) { + + addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) + addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) + + // Add ourselves to addrbook to prevent dialing ourselves + if config.P2P.ExternalAddress != "" { + addr, err := p2p.NewNetAddressString(p2p.IDAddressString(nodeKey.ID(), config.P2P.ExternalAddress)) + if err != nil { + return nil, errors.Wrap(err, "p2p.external_address is incorrect") + } + addrBook.AddOurAddress(addr) + } + if config.P2P.ListenAddress != "" { + addr, err := p2p.NewNetAddressString(p2p.IDAddressString(nodeKey.ID(), config.P2P.ListenAddress)) + if err != nil { + return nil, errors.Wrap(err, "p2p.laddr is incorrect") + } + addrBook.AddOurAddress(addr) + } + + sw.SetAddrBook(addrBook) + + return addrBook, nil +} + // OnStart starts the Node. It implements cmn.Service. func (n *Node) OnStart() error { now := tmtime.Now() From 4c48fe75f870fb56472ab3fb8e79eab2ba76eb90 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 28 Aug 2019 18:19:42 +0800 Subject: [PATCH 12/31] exit if SwitchToConsensus fails --- consensus/reactor.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/consensus/reactor.go b/consensus/reactor.go index 1f508319d2e..9d6ff30850a 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -116,8 +116,13 @@ func (conR *ConsensusReactor) SwitchToConsensus(state sm.State, blocksSynced int } err := conR.conS.Start() if err != nil { - conR.Logger.Error("Error starting conS", "err", err) - return + panic(fmt.Sprintf(`Failed to start consensus state: %v + +conS: +%+v + +conR: +%+v`, err, conR.conS, conR)) } } @@ -438,9 +443,9 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) { func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) { nrsMsg = &NewRoundStepMessage{ - Height: rs.Height, - Round: rs.Round, - Step: rs.Step, + Height: rs.Height, + Round: rs.Round, + Step: rs.Step, SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), LastCommitRound: rs.LastCommit.Round(), } From 53ee51ad1818d891e53168850249a5c38610bb20 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Fri, 30 Aug 2019 16:40:15 +0800 Subject: [PATCH 13/31] improve tx search --- rpc/core/tx.go | 5 +- state/txindex/indexer.go | 7 +- state/txindex/kv/kv.go | 196 ++++++++++++++++++++---------- state/txindex/kv/kv_bench_test.go | 67 ++++++++++ state/txindex/null/null.go | 4 +- 5 files changed, 210 insertions(+), 69 deletions(-) create mode 100644 state/txindex/kv/kv_bench_test.go diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 1aa884fce2b..188f85cf112 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -3,13 +3,12 @@ package core import ( "fmt" + "github.com/pkg/errors" cmn "github.com/tendermint/tendermint/libs/common" - tmquery "github.com/tendermint/tendermint/libs/pubsub/query" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/state/txindex/null" "github.com/tendermint/tendermint/types" - "github.com/pkg/errors" ) // Tx allows you to query the transaction results. `nil` could mean the @@ -208,7 +207,7 @@ func TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSear var proof types.TxProof for i := 0; i < len(apiResults); i++ { h := hashes[skipCount+i] - r, err := txIndexer.Get(h) + r, err := txIndexer.Get(h.Hash) if err != nil { return nil, errors.Wrapf(err, "failed to get Tx{%X}", h) } diff --git a/state/txindex/indexer.go b/state/txindex/indexer.go index 19e6103fe54..9cd920233c5 100644 --- a/state/txindex/indexer.go +++ b/state/txindex/indexer.go @@ -20,7 +20,12 @@ type TxIndexer interface { Get(hash []byte) (*types.TxResult, error) // Search allows you to query for transactions. - Search(q *query.Query) ([][]byte, error) + Search(q *query.Query) ([]TxHash, error) +} + +type TxHash struct { + Index string + Hash []byte } //---------------------------------------------------- diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 84c031f110e..df45de6b7fc 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -4,17 +4,17 @@ import ( "bytes" "encoding/hex" "fmt" + "sort" "strconv" "strings" "time" "github.com/pkg/errors" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tm-db" - "github.com/tendermint/tendermint/libs/pubsub/query" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) const ( @@ -74,7 +74,10 @@ func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { return txResult, nil } -// AddBatch indexes a batch of transactions using the given list of tags. +// AddBatch indexes a batch of transactions using the given list of events. Each +// key that indexed from the tx's events is a composite of the event type and +// the respective attribute's key delimited by a "." (eg. "account.number"). +// Any event with an empty type is not indexed. func (txi *TxIndex) AddBatch(b *txindex.Batch) error { storeBatch := txi.store.NewBatch() defer storeBatch.Close() @@ -106,7 +109,10 @@ func (txi *TxIndex) AddBatch(b *txindex.Batch) error { return nil } -// Index indexes a single transaction using the given list of tags. +// Index indexes a single transaction using the given list of events. Each key +// that indexed from the tx's events is a composite of the event type and the +// respective attribute's key delimited by a "." (eg. "account.number"). +// Any event with an empty type is not indexed. func (txi *TxIndex) Index(result *types.TxResult) error { b := txi.store.NewBatch() defer b.Close() @@ -130,9 +136,10 @@ func (txi *TxIndex) Index(result *types.TxResult) error { if err != nil { return err } - b.Set(hash, rawBytes) + b.Set(hash, rawBytes) b.Write() + return nil } @@ -142,9 +149,9 @@ func (txi *TxIndex) Index(result *types.TxResult) error { // result for it (2) for range queries it is better for the client to provide // both lower and upper bounds, so we are not performing a full scan. Results // from querying indexes are then intersected and returned to the caller. -func (txi *TxIndex) Search(q *query.Query) ([][]byte, error) { - var hashes [][]byte +func (txi *TxIndex) Search(q *query.Query) ([]txindex.TxHash, error) { var hashesInitialized bool + filteredHashes := make(map[string][]byte) // get a list of conditions (like "tx.height > 5") conditions := q.Conditions() @@ -154,11 +161,8 @@ func (txi *TxIndex) Search(q *query.Query) ([][]byte, error) { if err != nil { return nil, errors.Wrap(err, "error during searching for a hash in the query") } else if ok { - res, err := txi.Get(hash) - if res == nil { - return hashes, nil - } - return hashes, errors.Wrap(err, "error while retrieving the result") + txHash := txindex.TxHash{Hash: hash} + return []txindex.TxHash{txHash}, errors.Wrap(err, "error while retrieving the result") } // conditions to skip because they're handled before "everything else" @@ -173,10 +177,16 @@ func (txi *TxIndex) Search(q *query.Query) ([][]byte, error) { for _, r := range ranges { if !hashesInitialized { - hashes = txi.matchRange(r, startKey(r.key)) + filteredHashes = txi.matchRange(r, startKey(r.key), filteredHashes, true) hashesInitialized = true + + // Ignore any remaining conditions if the first condition resulted + // in no matches (assuming implicit AND operand). + if len(filteredHashes) == 0 { + break + } } else { - hashes = intersect(hashes, txi.matchRange(r, startKey(r.key))) + filteredHashes = txi.matchRange(r, startKey(r.key), filteredHashes, false) } } } @@ -191,32 +201,30 @@ func (txi *TxIndex) Search(q *query.Query) ([][]byte, error) { } if !hashesInitialized { - hashes = txi.match(c, startKeyForCondition(c, height)) + filteredHashes = txi.match(c, startKeyForCondition(c, height), filteredHashes, true) hashesInitialized = true + + // Ignore any remaining conditions if the first condition resulted + // in no matches (assuming implicit AND operand). + if len(filteredHashes) == 0 { + break + } } else { - hashes = intersect(hashes, txi.match(c, startKeyForCondition(c, height))) + filteredHashes = txi.match(c, startKeyForCondition(c, height), filteredHashes, false) } } - //results := make([]*types.TxResult, len(hashes)) - //i := 0 - //for _, h := range hashes { - // results[i], err = txi.Get(h) - // if err != nil { - // return nil, errors.Wrapf(err, "failed to get Tx{%X}", h) - // } - // i++ - //} + indexHashList := make([]txindex.TxHash, 0, len(filteredHashes)) + for key, value := range filteredHashes { + indexHashList = append(indexHashList, txindex.TxHash{Index: key, Hash: value}) + } // sort by height & index by default - //sort.Slice(results, func(i, j int) bool { - // if results[i].Height == results[j].Height { - // return results[i].Index < results[j].Index - // } - // return results[i].Height < results[j].Height - //}) - - return hashes, nil + sort.Slice(indexHashList, func(i, j int) bool { + return indexHashList[i].Index < indexHashList[j].Index + }) + + return indexHashList, nil } func lookForHash(conditions []query.Condition) (hash []byte, err error, ok bool) { @@ -333,63 +341,115 @@ func isRangeOperation(op query.Operator) bool { } } -func (txi *TxIndex) match(c query.Condition, startKeyBz []byte) (hashes [][]byte) { +// match returns all matching txs by hash that meet a given condition and start +// key. An already filtered result (filteredHashes) is provided such that any +// non-intersecting matches are removed. +// +// NOTE: filteredHashes may be empty if no previous condition has matched. +func (txi *TxIndex) match(c query.Condition, startKeyBz []byte, filteredHashes map[string][]byte, firstRun bool) map[string][]byte { + // A previous match was attempted but resulted in no matches, so we return + // no matches (assuming AND operand). + if !firstRun && len(filteredHashes) == 0 { + return filteredHashes + } + + tmpHashes := make(map[string][]byte) + if c.Op == query.OpEqual { it := dbm.IteratePrefix(txi.store, startKeyBz) defer it.Close() + for ; it.Valid(); it.Next() { - hashes = append(hashes, it.Value()) + tmpHashes[ConvertKey2Index(it.Key())] = it.Value() } + } else if c.Op == query.OpContains { // XXX: startKey does not apply here. - // For example, if startKey = "account.owner/an/" and search query = "accoutn.owner CONTAINS an" + // For example, if startKey = "account.owner/an/" and search query = "account.owner CONTAINS an" // we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/" it := dbm.IteratePrefix(txi.store, startKey(c.Tag)) defer it.Close() + for ; it.Valid(); it.Next() { if !isTagKey(it.Key()) { continue } + if strings.Contains(extractValueFromKey(it.Key()), c.Operand.(string)) { - hashes = append(hashes, it.Value()) + tmpHashes[ConvertKey2Index(it.Key())] = it.Value() } } } else { panic("other operators should be handled already") } - return + + if len(tmpHashes) == 0 || firstRun { + // Either: + // + // 1. Regardless if a previous match was attempted, which may have had + // results, but no match was found for the current condition, then we + // return no matches (assuming AND operand). + // + // 2. A previous match was not attempted, so we return all results. + return tmpHashes + } + + // Remove/reduce matches in filteredHashes that were not found in this + // match (tmpHashes). + for k := range filteredHashes { + if tmpHashes[k] == nil { + delete(filteredHashes, k) + } + } + + return filteredHashes } -func (txi *TxIndex) matchRange(r queryRange, startKey []byte) (hashes [][]byte) { - // create a map to prevent duplicates - hashesMap := make(map[string][]byte) +// matchRange returns all matching txs by hash that meet a given queryRange and +// start key. An already filtered result (filteredHashes) is provided such that +// any non-intersecting matches are removed. +// +// NOTE: filteredHashes may be empty if no previous condition has matched. +func (txi *TxIndex) matchRange(r queryRange, startKey []byte, filteredHashes map[string][]byte, firstRun bool) map[string][]byte { + // A previous match was attempted but resulted in no matches, so we return + // no matches (assuming AND operand). + if !firstRun && len(filteredHashes) == 0 { + return filteredHashes + } + tmpHashes := make(map[string][]byte) lowerBound := r.lowerBoundValue() upperBound := r.upperBoundValue() it := dbm.IteratePrefix(txi.store, startKey) defer it.Close() + LOOP: for ; it.Valid(); it.Next() { if !isTagKey(it.Key()) { continue } + switch r.AnyBound().(type) { case int64: v, err := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64) if err != nil { continue LOOP } + include := true if lowerBound != nil && v < lowerBound.(int64) { include = false } + if upperBound != nil && v > upperBound.(int64) { include = false } + if include { - hashesMap[fmt.Sprintf("%X", it.Value())] = it.Value() + tmpHashes[string(it.Value())] = it.Value() } + // XXX: passing time in a ABCI Tags is not yet implemented // case time.Time: // v := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64) @@ -398,13 +458,27 @@ LOOP: // } } } - hashes = make([][]byte, len(hashesMap)) - i := 0 - for _, h := range hashesMap { - hashes[i] = h - i++ + + if len(tmpHashes) == 0 || firstRun { + // Either: + // + // 1. Regardless if a previous match was attempted, which may have had + // results, but no match was found for the current condition, then we + // return no matches (assuming AND operand). + // + // 2. A previous match was not attempted, so we return all results. + return tmpHashes } - return + + // Remove/reduce matches in filteredHashes that were not found in this + // match (tmpHashes). + for k := range filteredHashes { + if tmpHashes[k] == nil { + delete(filteredHashes, k) + } + } + + return filteredHashes } /////////////////////////////////////////////////////////////////////////////// @@ -437,6 +511,17 @@ func keyForHeight(result *types.TxResult) []byte { )) } +func ConvertKey2Index(key []byte) string { + ss := strings.Split(string(key), tagKeySeparator) + if len(ss) == 4 { + height, _ := strconv.ParseUint(ss[2], 10, 64) + index, _ := strconv.ParseUint(ss[3], 10, 32) + + return fmt.Sprintf("%08d%s%04d", height, tagKeySeparator, index) + } + return string(key) +} + func startKeyForCondition(c query.Condition, height int64) []byte { if height > 0 { return startKey(c.Tag, c.Operand, height) @@ -451,18 +536,3 @@ func startKey(fields ...interface{}) []byte { } return b.Bytes() } - -/////////////////////////////////////////////////////////////////////////////// -// Utils - -func intersect(as, bs [][]byte) [][]byte { - i := make([][]byte, 0, cmn.MinInt(len(as), len(bs))) - for _, a := range as { - for _, b := range bs { - if bytes.Equal(a, b) { - i = append(i, a) - } - } - } - return i -} diff --git a/state/txindex/kv/kv_bench_test.go b/state/txindex/kv/kv_bench_test.go new file mode 100644 index 00000000000..d41b563a99b --- /dev/null +++ b/state/txindex/kv/kv_bench_test.go @@ -0,0 +1,67 @@ +package kv + +import ( + "crypto/rand" + "fmt" + "io/ioutil" + "testing" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/pubsub/query" + "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" +) + +func BenchmarkTxSearch(b *testing.B) { + dbDir, err := ioutil.TempDir("", "benchmark_tx_search_test") + if err != nil { + b.Errorf("failed to create temporary directory: %s", err) + } + + db, err := dbm.NewGoLevelDB("benchmark_tx_search_test", dbDir) + if err != nil { + b.Errorf("failed to create database: %s", err) + } + + allowedTags := []string{"address", "amount"} + indexer := NewTxIndex(db, IndexTags(allowedTags)) + + for i := 0; i < 100000; i++ { + tags := []cmn.KVPair{ + {Key: []byte("address"), Value: []byte(fmt.Sprintf("address_%d", i%10))}, + {Key: []byte("amount"), Value: []byte("50")}, + } + + txBz := make([]byte, 8) + if _, err := rand.Read(txBz); err != nil { + b.Errorf("failed produce random bytes: %s", err) + } + + txResult := &types.TxResult{ + Height: int64(i), + Index: 0, + Tx: types.Tx(string(txBz)), + Result: abci.ResponseDeliverTx{ + Data: []byte{0}, + Code: abci.CodeTypeOK, + Log: "", + Tags: tags, + }, + } + + if err := indexer.Index(txResult); err != nil { + b.Errorf("failed to index tx: %s", err) + } + } + + txQuery := query.MustParse("address = 'address_4' AND amount = '50'") + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + if _, err := indexer.Search(txQuery); err != nil { + b.Errorf("failed to query for txs: %s", err) + } + } +} diff --git a/state/txindex/null/null.go b/state/txindex/null/null.go index 2cdd06b8444..2b9022b2244 100644 --- a/state/txindex/null/null.go +++ b/state/txindex/null/null.go @@ -28,6 +28,6 @@ func (txi *TxIndex) Index(result *types.TxResult) error { return nil } -func (txi *TxIndex) Search(q *query.Query) ([][]byte, error) { - return [][]byte{}, nil +func (txi *TxIndex) Search(q *query.Query) ([]txindex.TxHash, error) { + return []txindex.TxHash{}, nil } From f874a66e1e393d29ff6cd694ad3f9a6f03e4b4b5 Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Sun, 1 Sep 2019 18:15:31 +0800 Subject: [PATCH 14/31] fix page validate --- rpc/core/pipe.go | 21 ++++++++++++++------- rpc/core/pipe_test.go | 43 ++++++++++++++++++++++++------------------- rpc/core/tx.go | 5 ++++- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 653ec7b892f..3974937d7b7 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -1,6 +1,8 @@ package core import ( + "fmt" + "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/log" @@ -133,19 +135,24 @@ func SetEventBus(b *types.EventBus) { eventBus = b } -func validatePage(page, perPage, totalCount int) int { +func validatePage(page, perPage, totalCount int) (int, error) { if perPage < 1 { - return 1 + panic(fmt.Sprintf("zero or negative perPage: %d", perPage)) + } + + if page == 0 { + return 1, nil // default } pages := ((totalCount - 1) / perPage) + 1 - if page < 1 { - page = 1 - } else if page > pages { - page = pages + if pages == 0 { + pages = 1 // one page (even if it's empty) + } + if page < 0 || page > pages { + return 1, fmt.Errorf("page should be within [0, %d] range, given %d", pages, page) } - return page + return page, nil } func validatePerPage(perPage int) int { diff --git a/rpc/core/pipe_test.go b/rpc/core/pipe_test.go index ff5c114f8c2..93aff3e58e3 100644 --- a/rpc/core/pipe_test.go +++ b/rpc/core/pipe_test.go @@ -14,40 +14,45 @@ func TestPaginationPage(t *testing.T) { perPage int page int newPage int + expErr bool }{ - {0, 0, 1, 1}, + {0, 10, 1, 1, false}, - {0, 10, 0, 1}, - {0, 10, 1, 1}, - {0, 10, 2, 1}, + {0, 10, 0, 1, false}, + {0, 10, 1, 1, false}, + {0, 10, 2, 0, true}, - {5, 10, -1, 1}, - {5, 10, 0, 1}, - {5, 10, 1, 1}, - {5, 10, 2, 1}, - {5, 10, 2, 1}, + {5, 10, -1, 0, true}, + {5, 10, 0, 1, false}, + {5, 10, 1, 1, false}, + {5, 10, 2, 0, true}, + {5, 10, 2, 0, true}, - {5, 5, 1, 1}, - {5, 5, 2, 1}, - {5, 5, 3, 1}, + {5, 5, 1, 1, false}, + {5, 5, 2, 0, true}, + {5, 5, 3, 0, true}, - {5, 3, 2, 2}, - {5, 3, 3, 2}, + {5, 3, 2, 2, false}, + {5, 3, 3, 0, true}, - {5, 2, 2, 2}, - {5, 2, 3, 3}, - {5, 2, 4, 3}, + {5, 2, 2, 2, false}, + {5, 2, 3, 3, false}, + {5, 2, 4, 0, true}, } for _, c := range cases { - p := validatePage(c.page, c.perPage, c.totalCount) + p, err := validatePage(c.page, c.perPage, c.totalCount) + if c.expErr { + assert.Error(t, err) + continue + } + assert.Equal(t, c.newPage, p, fmt.Sprintf("%v", c)) } } func TestPaginationPerPage(t *testing.T) { - cases := []struct { totalCount int perPage int diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 188f85cf112..e33d5793f9d 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -200,7 +200,10 @@ func TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSear totalCount := len(hashes) perPage = validatePerPage(perPage) - page = validatePage(page, perPage, totalCount) + page, err = validatePage(page, perPage, totalCount) + if err != nil { + return nil, err + } skipCount := validateSkipCount(page, perPage) apiResults := make([]*ctypes.ResultTx, cmn.MinInt(perPage, totalCount-skipCount)) From 728f1e9eaa31c1c581a718a3a114071a298dac2f Mon Sep 17 00:00:00 2001 From: chengwenxi Date: Wed, 4 Sep 2019 11:08:01 +0800 Subject: [PATCH 15/31] fix txs index sort --- state/txindex/kv/kv.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index df45de6b7fc..87d131132a4 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -2,6 +2,7 @@ package kv import ( "bytes" + "encoding/binary" "encoding/hex" "fmt" "sort" @@ -514,10 +515,14 @@ func keyForHeight(result *types.TxResult) []byte { func ConvertKey2Index(key []byte) string { ss := strings.Split(string(key), tagKeySeparator) if len(ss) == 4 { - height, _ := strconv.ParseUint(ss[2], 10, 64) + height, _ := strconv.ParseInt(ss[2], 10, 64) index, _ := strconv.ParseUint(ss[3], 10, 32) + indexBytes := make([]byte, 8+4) - return fmt.Sprintf("%08d%s%04d", height, tagKeySeparator, index) + binary.BigEndian.PutUint64(indexBytes[0:8], uint64(height)) + binary.BigEndian.PutUint32(indexBytes[8:12], uint32(index)) + + return string(indexBytes) } return string(key) } From b51aa654fa464da843f9b1cb0ab4c9170db81e1e Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Wed, 2 Oct 2019 16:31:49 +0800 Subject: [PATCH 16/31] Fix for panic in signature verification if a peer sends a nil public key --- p2p/conn/secret_connection.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index a4489f47536..ab11af8fe76 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -133,6 +133,11 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* } remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig + + if remPubKey == nil { + return nil, errors.New("peer sent a nil public key") + } + if !remPubKey.VerifyBytes(challenge[:], remSignature) { return nil, errors.New("Challenge verification failed") } From 2a9beecc71deffbec96b5c2b10d496e0dd247d06 Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Mon, 14 Oct 2019 15:55:32 +0800 Subject: [PATCH 17/31] p2p: only allow ed25519 pubkeys when connecting --- crypto/multisig/threshold_pubkey.go | 5 +++ go.mod | 1 - go.sum | 13 +++++--- p2p/conn/secret_connection.go | 11 ++++--- p2p/conn/secret_connection_test.go | 47 +++++++++++++++++++++++++++++ p2p/transport.go | 18 +++++++++++ 6 files changed, 85 insertions(+), 10 deletions(-) diff --git a/crypto/multisig/threshold_pubkey.go b/crypto/multisig/threshold_pubkey.go index 234d420f1d2..36e2dc2dd97 100644 --- a/crypto/multisig/threshold_pubkey.go +++ b/crypto/multisig/threshold_pubkey.go @@ -21,6 +21,11 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey { if len(pubkeys) < k { panic("threshold k of n multisignature: len(pubkeys) < k") } + for _, pubkey := range pubkeys { + if pubkey == nil { + panic("nil pubkey") + } + } return PubKeyMultisigThreshold{uint(k), pubkeys} } diff --git a/go.mod b/go.mod index 80a4a1a01f9..c441e6fc1d6 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,6 @@ require ( github.com/tendermint/btcd v0.1.1 github.com/tendermint/go-amino v0.14.1 github.com/tendermint/tm-db v0.1.1 - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20190628185345-da137c7871d7 google.golang.org/grpc v1.22.0 ) diff --git a/go.sum b/go.sum index 762b7f3bee3..8d4fc8443f1 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= @@ -34,14 +35,17 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -54,12 +58,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= -github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M= -github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -103,6 +105,7 @@ github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6o github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -130,7 +133,9 @@ google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index ab11af8fe76..9ef0d1cd6e2 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -7,7 +7,8 @@ import ( "crypto/sha256" "crypto/subtle" "encoding/binary" - "errors" + "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/ed25519" "io" "math" "net" @@ -134,12 +135,12 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig - if remPubKey == nil { - return nil, errors.New("peer sent a nil public key") + if _, ok := remPubKey.(ed25519.PubKeyEd25519); !ok { + return nil, errors.Errorf("expected ed25519 pubkey, got %T", remPubKey) } if !remPubKey.VerifyBytes(challenge[:], remSignature) { - return nil, errors.New("Challenge verification failed") + return nil, errors.New("challenge verification failed") } // We've authorized. @@ -222,7 +223,7 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) { defer pool.Put(frame) _, err = sc.recvAead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil) if err != nil { - return n, errors.New("Failed to decrypt SecretConnection") + return n, errors.New("failed to decrypt SecretConnection") } incrNonce(sc.recvNonce) // end decryption diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 9ab9695a363..4a4c2a3847e 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -5,6 +5,8 @@ import ( "encoding/hex" "flag" "fmt" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" "io" "log" "net" @@ -364,6 +366,51 @@ func TestDeriveSecretsAndChallengeGolden(t *testing.T) { } } +type privKeyWithNilPubKey struct { + orig crypto.PrivKey +} + +func (pk privKeyWithNilPubKey) Bytes() []byte { return pk.orig.Bytes() } +func (pk privKeyWithNilPubKey) Sign(msg []byte) ([]byte, error) { return pk.orig.Sign(msg) } +func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } +func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } + +func TestNilPubkey(t *testing.T) { + var fooConn, barConn = makeKVStoreConnPair() + var fooPrvKey = ed25519.GenPrivKey() + var barPrvKey = privKeyWithNilPubKey{ed25519.GenPrivKey()} + + go func() { + _, err := MakeSecretConnection(barConn, barPrvKey) + assert.NoError(t, err) + }() + + assert.NotPanics(t, func() { + _, err := MakeSecretConnection(fooConn, fooPrvKey) + if assert.Error(t, err) { + assert.Equal(t, "expected ed25519 pubkey, got ", err.Error()) + } + }) +} + +func TestNonEd25519Pubkey(t *testing.T) { + var fooConn, barConn = makeKVStoreConnPair() + var fooPrvKey = ed25519.GenPrivKey() + var barPrvKey = secp256k1.GenPrivKey() + + go func() { + _, err := MakeSecretConnection(barConn, barPrvKey) + assert.NoError(t, err) + }() + + assert.NotPanics(t, func() { + _, err := MakeSecretConnection(fooConn, fooPrvKey) + if assert.Error(t, err) { + assert.Equal(t, "expected ed25519 pubkey, got secp256k1.PubKeySecp256k1", err.Error()) + } + }) +} + // Creates the data for a test vector file. // The file format is: // Hex(diffie_hellman_secret), loc_is_least, Hex(recvSecret), Hex(sendSecret), Hex(challenge) diff --git a/p2p/transport.go b/p2p/transport.go index 8d6ea236ec9..06cd3e4a4ba 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -3,6 +3,7 @@ package p2p import ( "context" "fmt" + "github.com/pkg/errors" "net" "time" @@ -270,6 +271,23 @@ func (mt *MultiplexTransport) acceptPeers() { // // [0] https://en.wikipedia.org/wiki/Head-of-line_blocking go func(c net.Conn) { + defer func() { + if r := recover(); r != nil { + err := ErrRejected{ + conn: c, + err: errors.Errorf("recovered from panic: %v", r), + isAuthFailure: true, + } + select { + case mt.acceptc <- accept{err: err}: + case <-mt.closec: + // Give up if the transport was closed. + _ = c.Close() + return + } + } + }() + var ( nodeInfo NodeInfo secretConn *conn.SecretConnection From 5e475d19e96abe89ce1e3bb91c8577d9c24a493f Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Wed, 2 Oct 2019 16:31:49 +0800 Subject: [PATCH 18/31] update --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index c441e6fc1d6..80a4a1a01f9 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/tendermint/btcd v0.1.1 github.com/tendermint/go-amino v0.14.1 github.com/tendermint/tm-db v0.1.1 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20190628185345-da137c7871d7 google.golang.org/grpc v1.22.0 ) From edca015c8c40730fa542cdf9ee7e59fad4af0582 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 16 Oct 2019 15:26:36 -0500 Subject: [PATCH 19/31] cs: panic only when WAL#WriteSync fails - modify WAL#Write and WAL#WriteSync to return an error --- consensus/replay_test.go | 19 ++++++------ consensus/state.go | 10 +++++-- consensus/wal.go | 61 ++++++++++++++++++++++++-------------- consensus/wal_generator.go | 12 ++++---- 4 files changed, 64 insertions(+), 38 deletions(-) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index cfd9ba64487..3d6423983d0 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -210,15 +210,15 @@ func (e ReachedHeightToStopError) Error() string { // Write simulate WAL's crashing by sending an error to the panicCh and then // exiting the cs.receiveRoutine. -func (w *crashingWAL) Write(m WALMessage) { +func (w *crashingWAL) Write(m WALMessage) error { if endMsg, ok := m.(EndHeightMessage); ok { if endMsg.Height == w.heightToStop { w.panicCh <- ReachedHeightToStopError{endMsg.Height} runtime.Goexit() - } else { - w.next.Write(m) + return nil } - return + + return w.next.Write(m) } if w.msgIndex > w.lastPanicedForMsgIndex { @@ -226,14 +226,15 @@ func (w *crashingWAL) Write(m WALMessage) { _, file, line, _ := runtime.Caller(1) w.panicCh <- WALWriteError{fmt.Sprintf("failed to write %T to WAL (fileline: %s:%d)", m, file, line)} runtime.Goexit() - } else { - w.msgIndex++ - w.next.Write(m) + return nil } + + w.msgIndex++ + return w.next.Write(m) } -func (w *crashingWAL) WriteSync(m WALMessage) { - w.Write(m) +func (w *crashingWAL) WriteSync(m WALMessage) error { + return w.Write(m) } func (w *crashingWAL) Group() *auto.Group { return w.next.Group() } diff --git a/consensus/state.go b/consensus/state.go index 58bcb97622c..6e0f3b8fecd 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -636,7 +636,10 @@ func (cs *ConsensusState) receiveRoutine(maxSteps int) { // may generate internal events (votes, complete proposals, 2/3 majorities) cs.handleMsg(mi) case mi = <-cs.internalMsgQueue: - cs.wal.WriteSync(mi) // NOTE: fsync + err := cs.wal.WriteSync(mi) // NOTE: fsync + if err != nil { + panic(fmt.Sprintf("Failed to write %v msg to consensus wal due to %v. Check your FS and restart the node", mi, err)) + } if _, ok := mi.Msg.(*VoteMessage); ok { // we actually want to simulate failing during @@ -1318,7 +1321,10 @@ func (cs *ConsensusState) finalizeCommit(height int64) { // Either way, the ConsensusState should not be resumed until we // successfully call ApplyBlock (ie. later here, or in Handshake after // restart). - cs.wal.WriteSync(EndHeightMessage{height}) // NOTE: fsync + me := EndHeightMessage{height} + if err := cs.wal.WriteSync(me); err != nil { // NOTE: fsync + panic(fmt.Sprintf("Failed to write %v msg to consensus wal due to %v. Check your FS and restart the node", me, err)) + } fail.Fail() // XXX diff --git a/consensus/wal.go b/consensus/wal.go index 26428a4c626..3b2e7ee88c8 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -26,8 +26,9 @@ const ( //-------------------------------------------------------- // types and functions for savings consensus messages +// TimedWALMessage wraps WALMessage and adds Time for debugging purposes. type TimedWALMessage struct { - Time time.Time `json:"time"` // for debugging purposes + Time time.Time `json:"time"` Msg WALMessage `json:"msg"` } @@ -52,20 +53,22 @@ func RegisterWALMessages(cdc *amino.Codec) { // WAL is an interface for any write-ahead logger. type WAL interface { - Write(WALMessage) - WriteSync(WALMessage) + Write(WALMessage) error + WriteSync(WALMessage) error Group() *auto.Group SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) + // service methods Start() error Stop() error Wait() } // Write ahead logger writes msgs to disk before they are processed. -// Can be used for crash-recovery and deterministic replay -// TODO: currently the wal is overwritten during replay catchup -// give it a mode so it's either reading or appending - must read to end to start appending again +// Can be used for crash-recovery and deterministic replay. +// TODO: currently the wal is overwritten during replay catchup, give it a mode +// so it's either reading or appending - must read to end to start appending +// again. type baseWAL struct { cmn.BaseService @@ -130,29 +133,39 @@ func (wal *baseWAL) Wait() { // Write is called in newStep and for each receive on the // peerMsgQueue and the timeoutTicker. // NOTE: does not call fsync() -func (wal *baseWAL) Write(msg WALMessage) { +func (wal *baseWAL) Write(msg WALMessage) error { if wal == nil { - return + return nil } - // Write the wal message if err := wal.enc.Encode(&TimedWALMessage{tmtime.Now(), msg}); err != nil { - panic(fmt.Sprintf("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg)) + wal.Logger.Error("Error writing msg to consensus wal. WARNING: recover may not be possible for the current height", + "err", err, "msg", msg) + return err } + + return nil } // WriteSync is called when we receive a msg from ourselves // so that we write to disk before sending signed messages. // NOTE: calls fsync() -func (wal *baseWAL) WriteSync(msg WALMessage) { +func (wal *baseWAL) WriteSync(msg WALMessage) error { if wal == nil { - return + return nil + } + + if err := wal.Write(msg); err != nil { + return err } - wal.Write(msg) if err := wal.group.Flush(); err != nil { - panic(fmt.Sprintf("Error flushing consensus wal buf to file. Error: %v \n", err)) + wal.Logger.Error("WriteSync failed to flush consensus wal. WARNING: may result in creating alternative proposals / votes for the current height iff the node restarted", + "err", err) + return err } + + return nil } // WALSearchOptions are optional arguments to SearchForEndHeight. @@ -229,12 +242,17 @@ func NewWALEncoder(wr io.Writer) *WALEncoder { return &WALEncoder{wr} } -// Encode writes the custom encoding of v to the stream. +// Encode writes the custom encoding of v to the stream. It returns an error if +// the amino-encoded size of v is greater than 1MB. Any error encountered +// during the write is also returned. func (enc *WALEncoder) Encode(v *TimedWALMessage) error { data := cdc.MustMarshalBinaryBare(v) crc := crc32.Checksum(data, crc32c) length := uint32(len(data)) + if length > maxMsgSizeBytes { + return fmt.Errorf("msg is too big: %d bytes, max: %d bytes", length, maxMsgSizeBytes) + } totalLength := 8 + int(length) msg := make([]byte, totalLength) @@ -243,7 +261,6 @@ func (enc *WALEncoder) Encode(v *TimedWALMessage) error { copy(msg[8:], data) _, err := enc.wr.Write(msg) - return err } @@ -307,15 +324,15 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) { } data := make([]byte, length) - _, err = dec.rd.Read(data) + n, err := dec.rd.Read(data) if err != nil { - return nil, DataCorruptionError{fmt.Errorf("failed to read data: %v", err)} + return nil, DataCorruptionError{fmt.Errorf("failed to read data: %v (read: %d, wanted: %d)", err, n, length)} } // check checksum before decoding data actualCRC := crc32.Checksum(data, crc32c) if actualCRC != crc { - return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)} + return nil, DataCorruptionError{fmt.Errorf("checksums do not match: read: %v, actual: %v", crc, actualCRC)} } var res = new(TimedWALMessage) // nolint: gosimple @@ -329,9 +346,9 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) { type nilWAL struct{} -func (nilWAL) Write(m WALMessage) {} -func (nilWAL) WriteSync(m WALMessage) {} -func (nilWAL) Group() *auto.Group { return nil } +func (nilWAL) Write(m WALMessage) error { return nil } +func (nilWAL) WriteSync(m WALMessage) error { return nil } +func (nilWAL) Group() *auto.Group { return nil } func (nilWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) { return nil, false, nil } diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 912f80b3f99..ace32def13f 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -178,10 +178,10 @@ func newByteBufferWAL(logger log.Logger, enc *WALEncoder, nBlocks int64, signalS // Save writes message to the internal buffer except when heightToStop is // reached, in which case it will signal the caller via signalWhenStopsTo and // skip writing. -func (w *byteBufferWAL) Write(m WALMessage) { +func (w *byteBufferWAL) Write(m WALMessage) error { if w.stopped { w.logger.Debug("WAL already stopped. Not writing message", "msg", m) - return + return nil } if endMsg, ok := m.(EndHeightMessage); ok { @@ -190,7 +190,7 @@ func (w *byteBufferWAL) Write(m WALMessage) { w.logger.Debug("Stopping WAL at height", "height", endMsg.Height) w.signalWhenStopsTo <- struct{}{} w.stopped = true - return + return nil } } @@ -199,10 +199,12 @@ func (w *byteBufferWAL) Write(m WALMessage) { if err != nil { panic(fmt.Sprintf("failed to encode the msg %v", m)) } + + return nil } -func (w *byteBufferWAL) WriteSync(m WALMessage) { - w.Write(m) +func (w *byteBufferWAL) WriteSync(m WALMessage) error { + return w.Write(m) } func (w *byteBufferWAL) Group() *auto.Group { From 66e53a41acd2568a4faf02009041b090e776fd99 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 16 Oct 2019 18:00:39 -0500 Subject: [PATCH 20/31] types: validate Part#Proof add ValidateBasic to crypto/merkle/SimpleProof --- consensus/reactor_test.go | 266 ++++++++++++++++++++++++++++- crypto/merkle/simple_proof.go | 28 +++ crypto/merkle/simple_proof_test.go | 38 +++++ types/part_set.go | 7 +- types/part_set_test.go | 8 + 5 files changed, 343 insertions(+), 4 deletions(-) create mode 100644 crypto/merkle/simple_proof_test.go diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index b3ea0d34cd2..f928b2dc8ec 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -13,17 +13,21 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abcicli "github.com/tendermint/tendermint/abci/client" + + "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" + cstypes "github.com/tendermint/tendermint/consensus/types" + "github.com/tendermint/tendermint/crypto/tmhash" + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) func init() { @@ -565,3 +569,261 @@ func capture() { count := runtime.Stack(trace, true) fmt.Printf("Stack of %d bytes: %s\n", count, trace) } + +//------------------------------------------------------------- +// Ensure basic validation of structs is functioning + +func TestNewRoundStepMessageValidateBasic(t *testing.T) { + testCases := []struct { // nolint: maligned + expectErr bool + messageRound int + messageLastCommitRound int + messageHeight int64 + testName string + messageStep cstypes.RoundStepType + }{ + {false, 0, 0, 0, "Valid Message", 0x01}, + {true, -1, 0, 0, "Invalid Message", 0x01}, + {true, 0, 0, -1, "Invalid Message", 0x01}, + {true, 0, 0, 1, "Invalid Message", 0x00}, + {true, 0, 0, 1, "Invalid Message", 0x00}, + {true, 0, -2, 2, "Invalid Message", 0x01}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := NewRoundStepMessage{ + Height: tc.messageHeight, + Round: tc.messageRound, + Step: tc.messageStep, + LastCommitRound: tc.messageLastCommitRound, + } + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } +} + +func TestNewValidBlockMessageValidateBasic(t *testing.T) { + testBitArray := cmn.NewBitArray(1) + testCases := []struct { + testName string + messageHeight int64 + messageRound int + messageBlockParts *cmn.BitArray + expectErr bool + }{ + {"Valid Message", 0, 0, testBitArray, false}, + {"Invalid Message", -1, 0, testBitArray, true}, + {"Invalid Message", 0, -1, testBitArray, true}, + {"Invalid Message", 0, 0, cmn.NewBitArray(0), true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := NewValidBlockMessage{ + Height: tc.messageHeight, + Round: tc.messageRound, + BlockParts: tc.messageBlockParts, + } + + message.BlockPartsHeader.Total = 1 + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } +} + +func TestProposalPOLMessageValidateBasic(t *testing.T) { + testBitArray := cmn.NewBitArray(1) + testCases := []struct { + testName string + messageHeight int64 + messageProposalPOLRound int + messageProposalPOL *cmn.BitArray + expectErr bool + }{ + {"Valid Message", 0, 0, testBitArray, false}, + {"Invalid Message", -1, 0, testBitArray, true}, + {"Invalid Message", 0, -1, testBitArray, true}, + {"Invalid Message", 0, 0, cmn.NewBitArray(0), true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := ProposalPOLMessage{ + Height: tc.messageHeight, + ProposalPOLRound: tc.messageProposalPOLRound, + ProposalPOL: tc.messageProposalPOL, + } + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } +} + +func TestBlockPartMessageValidateBasic(t *testing.T) { + testPart := new(types.Part) + testPart.Proof.LeafHash = tmhash.Sum([]byte("leaf")) + testCases := []struct { + testName string + messageHeight int64 + messageRound int + messagePart *types.Part + expectErr bool + }{ + {"Valid Message", 0, 0, testPart, false}, + {"Invalid Message", -1, 0, testPart, true}, + {"Invalid Message", 0, -1, testPart, true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := BlockPartMessage{ + Height: tc.messageHeight, + Round: tc.messageRound, + Part: tc.messagePart, + } + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } + + message := BlockPartMessage{Height: 0, Round: 0, Part: new(types.Part)} + message.Part.Index = -1 + + assert.Equal(t, true, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") +} + +func TestHasVoteMessageValidateBasic(t *testing.T) { + const ( + validSignedMsgType types.SignedMsgType = 0x01 + invalidSignedMsgType types.SignedMsgType = 0x03 + ) + + testCases := []struct { // nolint: maligned + expectErr bool + messageRound int + messageIndex int + messageHeight int64 + testName string + messageType types.SignedMsgType + }{ + {false, 0, 0, 0, "Valid Message", validSignedMsgType}, + {true, -1, 0, 0, "Invalid Message", validSignedMsgType}, + {true, 0, -1, 0, "Invalid Message", validSignedMsgType}, + {true, 0, 0, 0, "Invalid Message", invalidSignedMsgType}, + {true, 0, 0, -1, "Invalid Message", validSignedMsgType}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := HasVoteMessage{ + Height: tc.messageHeight, + Round: tc.messageRound, + Type: tc.messageType, + Index: tc.messageIndex, + } + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } +} + +func TestVoteSetMaj23MessageValidateBasic(t *testing.T) { + const ( + validSignedMsgType types.SignedMsgType = 0x01 + invalidSignedMsgType types.SignedMsgType = 0x03 + ) + + validBlockID := types.BlockID{} + invalidBlockID := types.BlockID{ + Hash: cmn.HexBytes{}, + PartsHeader: types.PartSetHeader{ + Total: -1, + Hash: cmn.HexBytes{}, + }, + } + + testCases := []struct { // nolint: maligned + expectErr bool + messageRound int + messageHeight int64 + testName string + messageType types.SignedMsgType + messageBlockID types.BlockID + }{ + {false, 0, 0, "Valid Message", validSignedMsgType, validBlockID}, + {true, -1, 0, "Invalid Message", validSignedMsgType, validBlockID}, + {true, 0, -1, "Invalid Message", validSignedMsgType, validBlockID}, + {true, 0, 0, "Invalid Message", invalidSignedMsgType, validBlockID}, + {true, 0, 0, "Invalid Message", validSignedMsgType, invalidBlockID}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := VoteSetMaj23Message{ + Height: tc.messageHeight, + Round: tc.messageRound, + Type: tc.messageType, + BlockID: tc.messageBlockID, + } + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } +} + +func TestVoteSetBitsMessageValidateBasic(t *testing.T) { + const ( + validSignedMsgType types.SignedMsgType = 0x01 + invalidSignedMsgType types.SignedMsgType = 0x03 + ) + + validBlockID := types.BlockID{} + invalidBlockID := types.BlockID{ + Hash: cmn.HexBytes{}, + PartsHeader: types.PartSetHeader{ + Total: -1, + Hash: cmn.HexBytes{}, + }, + } + testBitArray := cmn.NewBitArray(1) + + testCases := []struct { // nolint: maligned + expectErr bool + messageRound int + messageHeight int64 + testName string + messageType types.SignedMsgType + messageBlockID types.BlockID + messageVotes *cmn.BitArray + }{ + {false, 0, 0, "Valid Message", validSignedMsgType, validBlockID, testBitArray}, + {true, -1, 0, "Invalid Message", validSignedMsgType, validBlockID, testBitArray}, + {true, 0, -1, "Invalid Message", validSignedMsgType, validBlockID, testBitArray}, + {true, 0, 0, "Invalid Message", invalidSignedMsgType, validBlockID, testBitArray}, + {true, 0, 0, "Invalid Message", validSignedMsgType, invalidBlockID, testBitArray}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + message := VoteSetBitsMessage{ + Height: tc.messageHeight, + Round: tc.messageRound, + Type: tc.messageType, + // Votes: tc.messageVotes, + BlockID: tc.messageBlockID, + } + + assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + }) + } +} diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index fd6d07b88c9..2a2d41d0a36 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -9,6 +9,11 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" ) +const ( + // given maxMsgSizeBytes in consensus wal is 1MB + maxAunts = 30000 +) + // SimpleProof represents a simple Merkle proof. // NOTE: The convention for proofs is to include leaf hashes but to // exclude the root hash. @@ -109,6 +114,29 @@ func (sp *SimpleProof) StringIndented(indent string) string { indent) } +// ValidateBasic performs basic validation. +// NOTE: it expects LeafHash and Aunts of tmhash.Size size. +func (sp *SimpleProof) ValidateBasic() error { + if sp.Total < 0 { + return errors.New("negative Total") + } + if sp.Index < 0 { + return errors.New("negative Index") + } + if len(sp.LeafHash) != tmhash.Size { + return errors.Errorf("expected LeafHash size to be %d, got %d", tmhash.Size, len(sp.LeafHash)) + } + if len(sp.Aunts) > maxAunts { + return errors.Errorf("expected no more than %d aunts, got %d", maxAunts, len(sp.Aunts)) + } + for i, auntHash := range sp.Aunts { + if len(auntHash) != tmhash.Size { + return errors.Errorf("expected Aunts#%d size to be %d, got %d", i, tmhash.Size, len(auntHash)) + } + } + return nil +} + // Use the leafHash and innerHashes to get the root merkle hash. // If the length of the innerHashes slice isn't exactly correct, the result is nil. // Recursive impl. diff --git a/crypto/merkle/simple_proof_test.go b/crypto/merkle/simple_proof_test.go new file mode 100644 index 00000000000..521bf4a3559 --- /dev/null +++ b/crypto/merkle/simple_proof_test.go @@ -0,0 +1,38 @@ +package merkle + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleProofValidateBasic(t *testing.T) { + testCases := []struct { + testName string + malleateProof func(*SimpleProof) + errStr string + }{ + {"Good", func(sp *SimpleProof) {}, ""}, + {"Negative Total", func(sp *SimpleProof) { sp.Total = -1 }, "negative Total"}, + {"Negative Index", func(sp *SimpleProof) { sp.Index = -1 }, "negative Index"}, + {"Invalid LeafHash", func(sp *SimpleProof) { sp.LeafHash = make([]byte, 10) }, "expected LeafHash size to be 32, got 10"}, + {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, maxAunts+1) }, "expected no more than 30000 aunts, got 30001"}, + {"Invalid Aunt", func(sp *SimpleProof) { sp.Aunts[0] = make([]byte, 10) }, "expected Aunts#0 size to be 32, got 10"}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + _, proofs := SimpleProofsFromByteSlices([][]byte{ + []byte("apple"), + []byte("watermelon"), + []byte("kiwi"), + }) + tc.malleateProof(proofs[0]) + err := proofs[0].ValidateBasic() + if tc.errStr != "" { + assert.Contains(t, err.Error(), tc.errStr) + } + }) + } +} diff --git a/types/part_set.go b/types/part_set.go index a040258d1cb..4232e212a96 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -40,10 +40,13 @@ func (part *Part) Hash() []byte { // ValidateBasic performs basic validation. func (part *Part) ValidateBasic() error { if part.Index < 0 { - return errors.New("Negative Index") + return errors.New("negative Index") } if len(part.Bytes) > BlockPartSizeBytes { - return fmt.Errorf("Too big (max: %d)", BlockPartSizeBytes) + return errors.Errorf("too big: %d bytes, max: %d", len(part.Bytes), BlockPartSizeBytes) + } + if err := part.Proof.ValidateBasic(); err != nil { + return errors.Wrap(err, "wrong Proof") } return nil } diff --git a/types/part_set_test.go b/types/part_set_test.go index daa2fa5c5d5..5c0edaffd4b 100644 --- a/types/part_set_test.go +++ b/types/part_set_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" ) @@ -114,6 +115,13 @@ func TestPartValidateBasic(t *testing.T) { {"Good Part", func(pt *Part) {}, false}, {"Negative index", func(pt *Part) { pt.Index = -1 }, true}, {"Too big part", func(pt *Part) { pt.Bytes = make([]byte, BlockPartSizeBytes+1) }, true}, + {"Too big proof", func(pt *Part) { + pt.Proof = merkle.SimpleProof{ + Total: 1, + Index: 1, + LeafHash: make([]byte, 1024*1024), + } + }, true}, } for _, tc := range testCases { From 9f29a16c56c6bb5c77fbe39e169e785a3253d02b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 16 Oct 2019 19:34:49 -0500 Subject: [PATCH 21/31] cs: limit max bit array size and block parts count --- consensus/reactor.go | 9 +++++++++ types/params.go | 3 +++ types/vote_set.go | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/consensus/reactor.go b/consensus/reactor.go index 1879bb4997a..81eb09cf102 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -1463,6 +1463,9 @@ func (m *NewValidBlockMessage) ValidateBasic() error { m.BlockParts.Size(), m.BlockPartsHeader.Total) } + if m.BlockParts.Size() > types.MaxBlockPartsCount { + return errors.Errorf("BlockParts bit array is too big: %d, max: %d", m.BlockParts.Size(), types.MaxBlockPartsCount) + } return nil } @@ -1509,6 +1512,9 @@ func (m *ProposalPOLMessage) ValidateBasic() error { if m.ProposalPOL.Size() == 0 { return errors.New("Empty ProposalPOL bit array") } + if m.ProposalPOL.Size() > types.MaxVotesCount { + return errors.Errorf("ProposalPOL bit array is too big: %d, max: %d", m.ProposalPOL.Size(), types.MaxVotesCount) + } return nil } @@ -1652,6 +1658,9 @@ func (m *VoteSetBitsMessage) ValidateBasic() error { return fmt.Errorf("Wrong BlockID: %v", err) } // NOTE: Votes.Size() can be zero if the node does not have any + if m.Votes.Size() > types.MaxVotesCount { + return fmt.Errorf("Votes bit array is too big: %d, max: %d", m.Votes.Size(), types.MaxVotesCount) + } return nil } diff --git a/types/params.go b/types/params.go index ec8a8f576bb..f49bf1be057 100644 --- a/types/params.go +++ b/types/params.go @@ -12,6 +12,9 @@ const ( // BlockPartSizeBytes is the size of one block part. BlockPartSizeBytes = 65536 // 64kB + + // MaxBlockPartsCount is the maximum count of block parts. + MaxBlockPartsCount = MaxBlockSizeBytes / BlockPartSizeBytes ) // ConsensusParams contains consensus critical parameters that determine the diff --git a/types/vote_set.go b/types/vote_set.go index 0cf6cbb7f5d..fdaf45ec235 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -11,6 +11,12 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" ) +const ( + // MaxVotesCount is the maximum votes count. Used in ValidateBasic funcs for + // protection against DOS attacks. + MaxVotesCount = 10000 +) + // UNSTABLE // XXX: duplicate of p2p.ID to avoid dependence between packages. // Perhaps we can have a minimal types package containing this (and other things?) From db3ca967d3bf6adf894456560fdea512c5804974 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 16 Oct 2019 20:47:48 -0500 Subject: [PATCH 22/31] cs: test new limits --- consensus/reactor.go | 3 + consensus/reactor_test.go | 152 ++++++++++++++++++++------------------ 2 files changed, 83 insertions(+), 72 deletions(-) diff --git a/consensus/reactor.go b/consensus/reactor.go index 81eb09cf102..cacacb4c5c3 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -1458,6 +1458,9 @@ func (m *NewValidBlockMessage) ValidateBasic() error { if err := m.BlockPartsHeader.ValidateBasic(); err != nil { return fmt.Errorf("Wrong BlockPartsHeader: %v", err) } + if m.BlockParts.Size() == 0 { + return errors.New("Empty BlockParts") + } if m.BlockParts.Size() != m.BlockPartsHeader.Total { return fmt.Errorf("BlockParts bit array size %d not equal to BlockPartsHeader.Total %d", m.BlockParts.Size(), diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index f928b2dc8ec..25e45f3148f 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -606,61 +606,75 @@ func TestNewRoundStepMessageValidateBasic(t *testing.T) { } func TestNewValidBlockMessageValidateBasic(t *testing.T) { - testBitArray := cmn.NewBitArray(1) testCases := []struct { - testName string - messageHeight int64 - messageRound int - messageBlockParts *cmn.BitArray - expectErr bool + malleateFn func(*NewValidBlockMessage) + expErr string }{ - {"Valid Message", 0, 0, testBitArray, false}, - {"Invalid Message", -1, 0, testBitArray, true}, - {"Invalid Message", 0, -1, testBitArray, true}, - {"Invalid Message", 0, 0, cmn.NewBitArray(0), true}, + {func(msg *NewValidBlockMessage) {}, ""}, + {func(msg *NewValidBlockMessage) { msg.Height = -1 }, "Negative Height"}, + {func(msg *NewValidBlockMessage) { msg.Round = -1 }, "Negative Round"}, + { + func(msg *NewValidBlockMessage) { msg.BlockPartsHeader.Total = 2 }, + "BlockParts bit array size 1 not equal to BlockPartsHeader.Total 2", + }, + { + func(msg *NewValidBlockMessage) { msg.BlockPartsHeader.Total = 0; msg.BlockParts = cmn.NewBitArray(0) }, + "Empty BlockParts", + }, + { + func(msg *NewValidBlockMessage) { msg.BlockParts = cmn.NewBitArray(types.MaxBlockPartsCount + 1) }, + "BlockParts bit array size 1601 not equal to BlockPartsHeader.Total 1", + }, } - for _, tc := range testCases { + for i, tc := range testCases { tc := tc - t.Run(tc.testName, func(t *testing.T) { - message := NewValidBlockMessage{ - Height: tc.messageHeight, - Round: tc.messageRound, - BlockParts: tc.messageBlockParts, + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + msg := &NewValidBlockMessage{ + Height: 1, + Round: 0, + BlockPartsHeader: types.PartSetHeader{ + Total: 1, + }, + BlockParts: cmn.NewBitArray(1), } - message.BlockPartsHeader.Total = 1 - - assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + tc.malleateFn(msg) + err := msg.ValidateBasic() + if tc.expErr != "" && assert.Error(t, err) { + assert.Contains(t, err.Error(), tc.expErr) + } }) } } func TestProposalPOLMessageValidateBasic(t *testing.T) { - testBitArray := cmn.NewBitArray(1) testCases := []struct { - testName string - messageHeight int64 - messageProposalPOLRound int - messageProposalPOL *cmn.BitArray - expectErr bool + malleateFn func(*ProposalPOLMessage) + expErr string }{ - {"Valid Message", 0, 0, testBitArray, false}, - {"Invalid Message", -1, 0, testBitArray, true}, - {"Invalid Message", 0, -1, testBitArray, true}, - {"Invalid Message", 0, 0, cmn.NewBitArray(0), true}, + {func(msg *ProposalPOLMessage) {}, ""}, + {func(msg *ProposalPOLMessage) { msg.Height = -1 }, "Negative Height"}, + {func(msg *ProposalPOLMessage) { msg.ProposalPOLRound = -1 }, "Negative ProposalPOLRound"}, + {func(msg *ProposalPOLMessage) { msg.ProposalPOL = cmn.NewBitArray(0) }, "Empty ProposalPOL bit array"}, + {func(msg *ProposalPOLMessage) { msg.ProposalPOL = cmn.NewBitArray(types.MaxVotesCount + 1) }, + "ProposalPOL bit array is too big: 10001, max: 10000"}, } - for _, tc := range testCases { + for i, tc := range testCases { tc := tc - t.Run(tc.testName, func(t *testing.T) { - message := ProposalPOLMessage{ - Height: tc.messageHeight, - ProposalPOLRound: tc.messageProposalPOLRound, - ProposalPOL: tc.messageProposalPOL, + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + msg := &ProposalPOLMessage{ + Height: 1, + ProposalPOLRound: 1, + ProposalPOL: cmn.NewBitArray(1), } - assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + tc.malleateFn(msg) + err := msg.ValidateBasic() + if tc.expErr != "" && assert.Error(t, err) { + assert.Contains(t, err.Error(), tc.expErr) + } }) } } @@ -781,49 +795,43 @@ func TestVoteSetMaj23MessageValidateBasic(t *testing.T) { } func TestVoteSetBitsMessageValidateBasic(t *testing.T) { - const ( - validSignedMsgType types.SignedMsgType = 0x01 - invalidSignedMsgType types.SignedMsgType = 0x03 - ) - - validBlockID := types.BlockID{} - invalidBlockID := types.BlockID{ - Hash: cmn.HexBytes{}, - PartsHeader: types.PartSetHeader{ - Total: -1, - Hash: cmn.HexBytes{}, - }, - } - testBitArray := cmn.NewBitArray(1) - testCases := []struct { // nolint: maligned - expectErr bool - messageRound int - messageHeight int64 - testName string - messageType types.SignedMsgType - messageBlockID types.BlockID - messageVotes *cmn.BitArray + malleateFn func(*VoteSetBitsMessage) + expErr string }{ - {false, 0, 0, "Valid Message", validSignedMsgType, validBlockID, testBitArray}, - {true, -1, 0, "Invalid Message", validSignedMsgType, validBlockID, testBitArray}, - {true, 0, -1, "Invalid Message", validSignedMsgType, validBlockID, testBitArray}, - {true, 0, 0, "Invalid Message", invalidSignedMsgType, validBlockID, testBitArray}, - {true, 0, 0, "Invalid Message", validSignedMsgType, invalidBlockID, testBitArray}, + {func(msg *VoteSetBitsMessage) {}, ""}, + {func(msg *VoteSetBitsMessage) { msg.Height = -1 }, "Negative Height"}, + {func(msg *VoteSetBitsMessage) { msg.Round = -1 }, "Negative Round"}, + {func(msg *VoteSetBitsMessage) { msg.Type = 0x03 }, "Invalid Type"}, + {func(msg *VoteSetBitsMessage) { + msg.BlockID = types.BlockID{ + Hash: cmn.HexBytes{}, + PartsHeader: types.PartSetHeader{ + Total: -1, + Hash: cmn.HexBytes{}, + }, + } + }, "Wrong BlockID: Wrong PartsHeader: Negative Total"}, + {func(msg *VoteSetBitsMessage) { msg.Votes = cmn.NewBitArray(types.MaxVotesCount + 1) }, + "Votes bit array is too big: 10001, max: 10000"}, } - for _, tc := range testCases { + for i, tc := range testCases { tc := tc - t.Run(tc.testName, func(t *testing.T) { - message := VoteSetBitsMessage{ - Height: tc.messageHeight, - Round: tc.messageRound, - Type: tc.messageType, - // Votes: tc.messageVotes, - BlockID: tc.messageBlockID, + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + msg := &VoteSetBitsMessage{ + Height: 1, + Round: 0, + Type: 0x01, + Votes: cmn.NewBitArray(1), + BlockID: types.BlockID{}, } - assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") + tc.malleateFn(msg) + err := msg.ValidateBasic() + if tc.expErr != "" && assert.Error(t, err) { + assert.Contains(t, err.Error(), tc.expErr) + } }) } } From 86d9c14def41213c76e88c654cfc12c946c8f6d3 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 17 Oct 2019 18:08:36 -0500 Subject: [PATCH 23/31] fix merge conflicts --- crypto/merkle/simple_proof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index 2a2d41d0a36..c43f2f922b0 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -2,9 +2,9 @@ package merkle import ( "bytes" - "errors" "fmt" + "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto/tmhash" cmn "github.com/tendermint/tendermint/libs/common" ) From 0639ea472df7c32a18544a596c53134e7f2f5c96 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 17 Oct 2019 18:08:36 -0500 Subject: [PATCH 24/31] fix merge conflicts --- crypto/merkle/simple_proof.go | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index c43f2f922b0..f0d7188276f 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/tmhash" cmn "github.com/tendermint/tendermint/libs/common" ) From 56d3b041241302a0fb06e245e4378e5566f681c9 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 23 Oct 2019 17:16:41 -0500 Subject: [PATCH 25/31] align max wal msg and max consensus msg sizes --- consensus/wal.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/consensus/wal.go b/consensus/wal.go index 3b2e7ee88c8..b817f86d926 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -19,8 +19,12 @@ import ( ) const ( - // must be greater than types.BlockPartSizeBytes + a few bytes - maxMsgSizeBytes = 1024 * 1024 // 1MB + + // amino overhead + time.Time + max consensus msg size + maxMsgSizeBytes = maxMsgSize + 24 + + // how often the WAL should be sync'd during period sync'ing + walDefaultFlushInterval = 2 * time.Second ) //-------------------------------------------------------- From aacbb369fe1fa4dd6aa8dcfec74766a6c64de603 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 23 Oct 2019 17:33:47 -0500 Subject: [PATCH 26/31] fix tests --- consensus/reactor_test.go | 2 +- crypto/merkle/simple_proof_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 25e45f3148f..17e9cf762c7 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -623,7 +623,7 @@ func TestNewValidBlockMessageValidateBasic(t *testing.T) { }, { func(msg *NewValidBlockMessage) { msg.BlockParts = cmn.NewBitArray(types.MaxBlockPartsCount + 1) }, - "BlockParts bit array size 1601 not equal to BlockPartsHeader.Total 1", + "BlockParts bit array size 1602 not equal to BlockPartsHeader.Total 1", }, } diff --git a/crypto/merkle/simple_proof_test.go b/crypto/merkle/simple_proof_test.go index 521bf4a3559..1175ce3cc7a 100644 --- a/crypto/merkle/simple_proof_test.go +++ b/crypto/merkle/simple_proof_test.go @@ -16,7 +16,7 @@ func TestSimpleProofValidateBasic(t *testing.T) { {"Negative Total", func(sp *SimpleProof) { sp.Total = -1 }, "negative Total"}, {"Negative Index", func(sp *SimpleProof) { sp.Index = -1 }, "negative Index"}, {"Invalid LeafHash", func(sp *SimpleProof) { sp.LeafHash = make([]byte, 10) }, "expected LeafHash size to be 32, got 10"}, - {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, maxAunts+1) }, "expected no more than 30000 aunts, got 30001"}, + {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, maxAunts+1) }, "expected no more than 101 aunts, got 101"}, {"Invalid Aunt", func(sp *SimpleProof) { sp.Aunts[0] = make([]byte, 10) }, "expected Aunts#0 size to be 32, got 10"}, } From a294db9167b604747ac5ce0acc5eeb717b913035 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 23 Oct 2019 18:03:09 -0500 Subject: [PATCH 27/31] fix test --- crypto/merkle/simple_proof_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/merkle/simple_proof_test.go b/crypto/merkle/simple_proof_test.go index 1175ce3cc7a..1a517905b5e 100644 --- a/crypto/merkle/simple_proof_test.go +++ b/crypto/merkle/simple_proof_test.go @@ -16,7 +16,7 @@ func TestSimpleProofValidateBasic(t *testing.T) { {"Negative Total", func(sp *SimpleProof) { sp.Total = -1 }, "negative Total"}, {"Negative Index", func(sp *SimpleProof) { sp.Index = -1 }, "negative Index"}, {"Invalid LeafHash", func(sp *SimpleProof) { sp.LeafHash = make([]byte, 10) }, "expected LeafHash size to be 32, got 10"}, - {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, maxAunts+1) }, "expected no more than 101 aunts, got 101"}, + {"Too many Aunts", func(sp *SimpleProof) { sp.Aunts = make([][]byte, maxAunts+1) }, "expected no more than 100 aunts, got 101"}, {"Invalid Aunt", func(sp *SimpleProof) { sp.Aunts[0] = make([]byte, 10) }, "expected Aunts#0 size to be 32, got 10"}, } From a99e0b0ff90ba7a92e60999b7281dfd0295950bb Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Wed, 30 Oct 2019 14:04:26 +0800 Subject: [PATCH 28/31] Update version --- go.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.mod b/go.mod index 80a4a1a01f9..43b312b6d1c 100644 --- a/go.mod +++ b/go.mod @@ -41,3 +41,5 @@ require ( golang.org/x/net v0.0.0-20190628185345-da137c7871d7 google.golang.org/grpc v1.22.0 ) + +go 1.13 From ce7010acaaf093075c0700b2e8adb5a63ca93864 Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Wed, 30 Oct 2019 15:02:38 +0800 Subject: [PATCH 29/31] fix tests --- consensus/wal_test.go | 123 +++++++++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 18 deletions(-) diff --git a/consensus/wal_test.go b/consensus/wal_test.go index 54219264f0f..daed449c21a 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -3,22 +3,27 @@ package consensus import ( "bytes" "crypto/rand" - "fmt" "io/ioutil" "os" "path/filepath" + // "sync" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/consensus/types" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/libs/autofile" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" +) - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" +const ( + walTestFlushInterval = time.Duration(100) * time.Millisecond ) func TestWALTruncate(t *testing.T) { @@ -28,8 +33,10 @@ func TestWALTruncate(t *testing.T) { walFile := filepath.Join(walDir, "wal") - //this magic number 4K can truncate the content when RotateFile. defaultHeadSizeLimit(10M) is hard to simulate. - //this magic number 1 * time.Millisecond make RotateFile check frequently. defaultGroupCheckDuration(5s) is hard to simulate. + // this magic number 4K can truncate the content when RotateFile. + // defaultHeadSizeLimit(10M) is hard to simulate. + // this magic number 1 * time.Millisecond make RotateFile check frequently. + // defaultGroupCheckDuration(5s) is hard to simulate. wal, err := NewWAL(walFile, autofile.GroupHeadSizeLimit(4096), autofile.GroupCheckDuration(1*time.Millisecond), @@ -45,20 +52,21 @@ func TestWALTruncate(t *testing.T) { wal.Wait() }() - //60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), when headBuf is full, truncate content will Flush to the file. - //at this time, RotateFile is called, truncate content exist in each file. + // 60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), + // when headBuf is full, truncate content will Flush to the file. at this + // time, RotateFile is called, truncate content exist in each file. err = WALGenerateNBlocks(wal.Group(), 60) require.NoError(t, err) time.Sleep(1 * time.Millisecond) //wait groupCheckDuration, make sure RotateFile run - wal.Group().Flush() + wal.group.Flush() h := int64(50) gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) - assert.NoError(t, err, fmt.Sprintf("expected not to err on height %d", h)) - assert.True(t, found, fmt.Sprintf("expected to find end height for %d", h)) - assert.NotNil(t, gr, "expected group not to be nil") + assert.NoError(t, err, "expected not to err on height %d", h) + assert.True(t, found, "expected to find end height for %d", h) + assert.NotNil(t, gr) defer gr.Close() dec := NewWALDecoder(gr) @@ -66,14 +74,14 @@ func TestWALTruncate(t *testing.T) { assert.NoError(t, err, "expected to decode a message") rs, ok := msg.Msg.(tmtypes.EventDataRoundState) assert.True(t, ok, "expected message of type EventDataRoundState") - assert.Equal(t, rs.Height, h+1, fmt.Sprintf("wrong height")) + assert.Equal(t, rs.Height, h+1, "wrong height") } func TestWALEncoderDecoder(t *testing.T) { now := tmtime.Now() msgs := []TimedWALMessage{ - TimedWALMessage{Time: now, Msg: EndHeightMessage{0}}, - TimedWALMessage{Time: now, Msg: timeoutInfo{Duration: time.Second, Height: 1, Round: 1, Step: types.RoundStepPropose}}, + {Time: now, Msg: EndHeightMessage{0}}, + {Time: now, Msg: timeoutInfo{Duration: time.Second, Height: 1, Round: 1, Step: types.RoundStepPropose}}, } b := new(bytes.Buffer) @@ -94,6 +102,43 @@ func TestWALEncoderDecoder(t *testing.T) { } } +func TestWALWrite(t *testing.T) { + walDir, err := ioutil.TempDir("", "wal") + require.NoError(t, err) + defer os.RemoveAll(walDir) + walFile := filepath.Join(walDir, "wal") + + wal, err := NewWAL(walFile) + require.NoError(t, err) + err = wal.Start() + require.NoError(t, err) + defer func() { + wal.Stop() + // wait for the wal to finish shutting down so we + // can safely remove the directory + wal.Wait() + }() + + // 1) Write returns an error if msg is too big + msg := &BlockPartMessage{ + Height: 1, + Round: 1, + Part: &tmtypes.Part{ + Index: 1, + Bytes: make([]byte, 1), + Proof: merkle.SimpleProof{ + Total: 1, + Index: 1, + LeafHash: make([]byte, maxMsgSizeBytes-30), + }, + }, + } + err = wal.Write(msg) + if assert.Error(t, err) { + assert.Equal(t, "msg is too big: 1048593 bytes, max: 1048576 bytes", err.Error()) + } +} + func TestWALSearchForEndHeight(t *testing.T) { walBody, err := WALWithNBlocks(6) if err != nil { @@ -107,9 +152,9 @@ func TestWALSearchForEndHeight(t *testing.T) { h := int64(3) gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) - assert.NoError(t, err, fmt.Sprintf("expected not to err on height %d", h)) - assert.True(t, found, fmt.Sprintf("expected to find end height for %d", h)) - assert.NotNil(t, gr, "expected group not to be nil") + assert.NoError(t, err, "expected not to err on height %d", h) + assert.True(t, found, "expected to find end height for %d", h) + assert.NotNil(t, gr) defer gr.Close() dec := NewWALDecoder(gr) @@ -117,9 +162,51 @@ func TestWALSearchForEndHeight(t *testing.T) { assert.NoError(t, err, "expected to decode a message") rs, ok := msg.Msg.(tmtypes.EventDataRoundState) assert.True(t, ok, "expected message of type EventDataRoundState") - assert.Equal(t, rs.Height, h+1, fmt.Sprintf("wrong height")) + assert.Equal(t, rs.Height, h+1, "wrong height") } +/* +// TODO: cherry pick FlushInterval +func TestWALPeriodicSync(t *testing.T) { + walDir, err := ioutil.TempDir("", "wal") + require.NoError(t, err) + defer os.RemoveAll(walDir) + + walFile := filepath.Join(walDir, "wal") + wal, err := NewWAL(walFile, autofile.GroupCheckDuration(1*time.Millisecond)) + require.NoError(t, err) + + wal.SetFlushInterval(walTestFlushInterval) + wal.SetLogger(log.TestingLogger()) + + // Generate some data + err = WALGenerateNBlocks(t, wal.Group(), 5) + require.NoError(t, err) + + // We should have data in the buffer now + assert.NotZero(t, wal.Group().Buffered()) + + require.NoError(t, wal.Start()) + defer func() { + wal.Stop() + wal.Wait() + }() + + time.Sleep(walTestFlushInterval + (10 * time.Millisecond)) + + // The data should have been flushed by the periodic sync + assert.Zero(t, wal.Group().Buffered()) + + h := int64(4) + gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) + assert.NoError(t, err, "expected not to err on height %d", h) + assert.True(t, found, "expected to find end height for %d", h) + assert.NotNil(t, gr) + if gr != nil { + gr.Close() + } +}*/ + /* var initOnce sync.Once From d17df9141bc14b8b890655125a2bdabc6ef8201f Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Wed, 30 Oct 2019 16:41:39 +0800 Subject: [PATCH 30/31] fix tests --- consensus/byzantine_test.go | 4 ++++ consensus/reactor_test.go | 2 +- consensus/state_test.go | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 6f46c04d39c..83cb3758b6b 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -242,6 +242,10 @@ type ByzantineReactor struct { reactor *ConsensusReactor } +func (br *ByzantineReactor) InitPeer(peer p2p.Peer) p2p.Peer { + return peer +} + func NewByzantineReactor(conR *ConsensusReactor) *ByzantineReactor { return &ByzantineReactor{ Service: conR, diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 17e9cf762c7..491f016480f 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -22,12 +22,12 @@ import ( cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/crypto/tmhash" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) func init() { diff --git a/consensus/state_test.go b/consensus/state_test.go index 40103e47259..542748a1074 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -14,7 +14,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" - p2pdummy "github.com/tendermint/tendermint/p2p/dummy" + p2pdummy "github.com/tendermint/tendermint/p2p/mock" "github.com/tendermint/tendermint/types" ) @@ -1432,7 +1432,7 @@ func TestStateHalt1(t *testing.T) { func TestStateOutputsBlockPartsStats(t *testing.T) { // create dummy peer cs, _ := randConsensusState(1) - peer := p2pdummy.NewPeer() + peer := p2pdummy.NewPeer(nil) // 1) new block part parts := types.NewPartSetFromData(cmn.RandBytes(100), 10) @@ -1475,7 +1475,7 @@ func TestStateOutputsBlockPartsStats(t *testing.T) { func TestStateOutputVoteStats(t *testing.T) { cs, vss := randConsensusState(2) // create dummy peer - peer := p2pdummy.NewPeer() + peer := p2pdummy.NewPeer(nil) vote := signVote(vss[1], types.PrecommitType, []byte("test"), types.PartSetHeader{}) From 02ac3938b2629086f8ba445bd8199b5f40e60bf2 Mon Sep 17 00:00:00 2001 From: zhangyelong Date: Mon, 4 Nov 2019 14:58:07 +0800 Subject: [PATCH 31/31] Update tendermint version --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index 845c093ffab..d8bd764f76e 100644 --- a/version/version.go +++ b/version/version.go @@ -18,7 +18,7 @@ const ( // TMCoreSemVer is the current version of Tendermint Core. // It's the Semantic Version of the software. // Must be a string because scripts like dist.sh read this file. - TMCoreSemVer = "0.32.0-dev" + TMCoreSemVer = "0.32.0" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0"