From 0ff715125bf6bfbee99928ad6561245b177d21f4 Mon Sep 17 00:00:00 2001 From: Zach Date: Sun, 16 Dec 2018 23:34:13 -0500 Subject: [PATCH 01/31] fix docs / proxy app (#2988) * fix docs / proxy app, closes #2986 * counter_serial * review comments * list all possible options * add changelog entries --- CHANGELOG_PENDING.md | 2 ++ abci/cmd/abci-cli/abci-cli.go | 22 +--------------------- cmd/tendermint/commands/run_node.go | 2 +- docs/tendermint-core/using-tendermint.md | 2 +- proxy/client.go | 11 ++++++----- 5 files changed, 11 insertions(+), 28 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2283ff37..022965bb 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -7,6 +7,8 @@ Special thanks to external contributors on this release: ### BREAKING CHANGES: * CLI/RPC/Config +- [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. +- [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. * Apps diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index 50972ec3..cc3f9c45 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -58,7 +58,7 @@ var RootCmd = &cobra.Command{ PersistentPreRunE: func(cmd *cobra.Command, args []string) error { switch cmd.Use { - case "counter", "kvstore", "dummy": // for the examples apps, don't pre-run + case "counter", "kvstore": // for the examples apps, don't pre-run return nil case "version": // skip running for version command return nil @@ -127,10 +127,6 @@ func addCounterFlags() { counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions") } -func addDummyFlags() { - dummyCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") -} - func addKVStoreFlags() { kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") } @@ -152,10 +148,6 @@ func addCommands() { // examples addCounterFlags() RootCmd.AddCommand(counterCmd) - // deprecated, left for backwards compatibility - addDummyFlags() - RootCmd.AddCommand(dummyCmd) - // replaces dummy, see issue #196 addKVStoreFlags() RootCmd.AddCommand(kvstoreCmd) } @@ -291,18 +283,6 @@ var counterCmd = &cobra.Command{ }, } -// deprecated, left for backwards compatibility -var dummyCmd = &cobra.Command{ - Use: "dummy", - Deprecated: "use: [abci-cli kvstore] instead", - Short: "ABCI demo example", - Long: "ABCI demo example", - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - return cmdKVStore(cmd, args) - }, -} - var kvstoreCmd = &cobra.Command{ Use: "kvstore", Short: "ABCI demo example", diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 6dabacb1..ef205aa6 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -24,7 +24,7 @@ func AddNodeFlags(cmd *cobra.Command) { cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") // abci flags - cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'kvstore' for local testing.") + cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or one of: 'kvstore', 'persistent_kvstore', 'counter', 'counter_serial' or 'noop' for local testing.") cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)") // rpc flags diff --git a/docs/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index 148c874c..2ca8c9e9 100644 --- a/docs/tendermint-core/using-tendermint.md +++ b/docs/tendermint-core/using-tendermint.md @@ -113,7 +113,7 @@ blocks are produced regularly, even if there are no transactions. See _No Empty Blocks_, below, to modify this setting. Tendermint supports in-process versions of the `counter`, `kvstore` and -`nil` apps that ship as examples with `abci-cli`. It's easy to compile +`noop` apps that ship as examples with `abci-cli`. It's easy to compile your own app in-process with Tendermint if it's written in Go. If your app is not written in Go, simply run it in another process, and use the `--proxy_app` flag to specify the address of the socket it is listening diff --git a/proxy/client.go b/proxy/client.go index 87f4e716..c5ee5fe1 100644 --- a/proxy/client.go +++ b/proxy/client.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" abcicli "github.com/tendermint/tendermint/abci/client" + "github.com/tendermint/tendermint/abci/example/counter" "github.com/tendermint/tendermint/abci/example/kvstore" "github.com/tendermint/tendermint/abci/types" ) @@ -64,15 +65,15 @@ func (r *remoteClientCreator) NewABCIClient() (abcicli.Client, error) { func DefaultClientCreator(addr, transport, dbDir string) ClientCreator { switch addr { + case "counter": + return NewLocalClientCreator(counter.NewCounterApplication(false)) + case "counter_serial": + return NewLocalClientCreator(counter.NewCounterApplication(true)) case "kvstore": - fallthrough - case "dummy": return NewLocalClientCreator(kvstore.NewKVStoreApplication()) case "persistent_kvstore": - fallthrough - case "persistent_dummy": return NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(dbDir)) - case "nilapp": + case "noop": return NewLocalClientCreator(types.NewBaseApplication()) default: mustConnect := false // loop retrying From a06912b5793787df769c2991270d86129d243349 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 17 Dec 2018 20:35:05 +0400 Subject: [PATCH 02/31] mempool: move tx to back, not front (#3036) because we pop txs from the front if the cache is full Refs #3035 --- mempool/mempool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mempool/mempool.go b/mempool/mempool.go index c5f966c4..3a1921bc 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -676,7 +676,7 @@ func (cache *mapTxCache) Push(tx types.Tx) bool { // Use the tx hash in the cache txHash := sha256.Sum256(tx) if moved, exists := cache.map_[txHash]; exists { - cache.list.MoveToFront(moved) + cache.list.MoveToBack(moved) return false } From 2182f6a7022366c52769c3fe8b073b8e4a9101b9 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Dec 2018 11:51:53 -0500 Subject: [PATCH 03/31] update go version & other cleanup (#3018) * update go version & other cleanup * fix lints * go one.eleven.four * keep circle on 1.11.3 for now --- .circleci/config.yml | 2 +- README.md | 2 +- blockchain/reactor.go | 6 +- docs/package-lock.json | 4670 ------------------ docs/package.json | 40 - docs/yarn.lock | 2611 ---------- scripts/install/install_tendermint_arm.sh | 10 +- scripts/install/install_tendermint_bsd.sh | 2 +- scripts/install/install_tendermint_ubuntu.sh | 2 +- types/tx_test.go | 4 +- 10 files changed, 8 insertions(+), 7341 deletions(-) delete mode 100644 docs/package-lock.json delete mode 100644 docs/package.json delete mode 100644 docs/yarn.lock diff --git a/.circleci/config.yml b/.circleci/config.yml index 0bbe76ff..dcc0e289 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &defaults working_directory: /go/src/github.com/tendermint/tendermint docker: - - image: circleci/golang:1.10.3 + - image: circleci/golang:1.11.3 environment: GOBIN: /tmp/workspace/bin diff --git a/README.md b/README.md index 7c386ec3..6e5c9e9a 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY. Requirement|Notes ---|--- -Go version | Go1.10 or higher +Go version | Go1.11.4 or higher ## Documentation diff --git a/blockchain/reactor.go b/blockchain/reactor.go index e62a9e4f..bed082cd 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -432,11 +432,7 @@ type bcBlockResponseMessage struct { // ValidateBasic performs basic validation. func (m *bcBlockResponseMessage) ValidateBasic() error { - if err := m.Block.ValidateBasic(); err != nil { - return err - } - - return nil + return m.Block.ValidateBasic() } func (m *bcBlockResponseMessage) String() string { diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index 3449eda1..00000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,4670 +0,0 @@ -{ - "name": "tendermint", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@azu/format-text": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.1.tgz", - "integrity": "sha1-aWc1CpRkD2sChVFpvYl85U1s6+I=" - }, - "@azu/style-format": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.0.tgz", - "integrity": "sha1-5wGH+Khi4ZGxvObAJo8TrNOlayA=", - "requires": { - "@azu/format-text": "^1.0.1" - } - }, - "@sindresorhus/is": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" - }, - "@textlint/ast-node-types": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.0.3.tgz", - "integrity": "sha512-mkkqbuxZkCESmMCrVN5QEgmFqBJAcoAGIaZaQfziqKAyCQBLLgKVJzeFuup9mDm9mvCTKekhLk9yIaEFc8EFxA==" - }, - "@textlint/ast-traverse": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-2.0.9.tgz", - "integrity": "sha512-E2neVj65wyadt3hr9R+DHW01dG4dNOMmFRab7Bph/rkDDeK85w/6RNJgIt9vBCPtt7a4bndTj1oZrK6wDZAEtQ==", - "requires": { - "@textlint/ast-node-types": "^4.0.3" - } - }, - "@textlint/feature-flag": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-3.0.5.tgz", - "integrity": "sha512-hXTDGvltgiUtJs7QhALSILNE+g0cdY4CyqHR2r5+EmiYbS3NuqWVLn3GZYUPWXl9rVDky/IpR+6DF0uLJF8m8Q==", - "requires": { - "map-like": "^2.0.0" - } - }, - "@textlint/fixer-formatter": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-3.0.8.tgz", - "integrity": "sha512-LTHcCLTyESdz90NGYzrYC0juSqLzGBc5VMMRO8Xvz3fapBya/Sn5ncgvsHqnKY0OIbV/IdOT54G2F46D8R6P9Q==", - "requires": { - "@textlint/kernel": "^3.0.0", - "chalk": "^1.1.3", - "debug": "^2.1.0", - "diff": "^2.2.2", - "interop-require": "^1.0.0", - "is-file": "^1.0.0", - "string-width": "^1.0.1", - "text-table": "^0.2.0", - "try-resolve": "^1.0.1" - }, - "dependencies": { - "@textlint/kernel": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-3.0.0.tgz", - "integrity": "sha512-SxHWr6VAD/SdqTCy1uB03bFLbGYbhZeQTeUuIJE6s1pD7wtQ1+Y1n8nx9I9m7nqGZi5eYuVA6WnpvCq10USz+w==", - "requires": { - "@textlint/ast-node-types": "^4.0.3", - "@textlint/ast-traverse": "^2.0.9", - "@textlint/feature-flag": "^3.0.5", - "@types/bluebird": "^3.5.18", - "bluebird": "^3.5.1", - "debug": "^2.6.6", - "deep-equal": "^1.0.1", - "map-like": "^2.0.0", - "object-assign": "^4.1.1", - "structured-source": "^3.0.2" - } - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "@textlint/kernel": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-2.0.9.tgz", - "integrity": "sha512-0237/9yDIlSVaH0pcVAxm0rV1xF96UpjXUXoBRdciWnf2+O0tWQEeBC9B2/B2jLw9Ha0zGlK+q+bLREpXB97Cw==", - "requires": { - "@textlint/ast-node-types": "^4.0.2", - "@textlint/ast-traverse": "^2.0.8", - "@textlint/feature-flag": "^3.0.4", - "@types/bluebird": "^3.5.18", - "bluebird": "^3.5.1", - "debug": "^2.6.6", - "deep-equal": "^1.0.1", - "object-assign": "^4.1.1", - "structured-source": "^3.0.2" - } - }, - "@textlint/linter-formatter": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-3.0.8.tgz", - "integrity": "sha512-hayZi4ybj01Km9Soi34cT8EkmEcqGgQKHu1tvPQVd8S2zaE3m/8nmf6qhwAo/HAwMzbIj0XxdV8nVuiUfz8ADQ==", - "requires": { - "@azu/format-text": "^1.0.1", - "@azu/style-format": "^1.0.0", - "@textlint/kernel": "^3.0.0", - "chalk": "^1.0.0", - "concat-stream": "^1.5.1", - "js-yaml": "^3.2.4", - "optionator": "^0.8.1", - "pluralize": "^2.0.0", - "string-width": "^1.0.1", - "string.prototype.padstart": "^3.0.0", - "strip-ansi": "^3.0.1", - "table": "^3.7.8", - "text-table": "^0.2.0", - "try-resolve": "^1.0.1", - "xml-escape": "^1.0.0" - }, - "dependencies": { - "@textlint/kernel": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-3.0.0.tgz", - "integrity": "sha512-SxHWr6VAD/SdqTCy1uB03bFLbGYbhZeQTeUuIJE6s1pD7wtQ1+Y1n8nx9I9m7nqGZi5eYuVA6WnpvCq10USz+w==", - "requires": { - "@textlint/ast-node-types": "^4.0.3", - "@textlint/ast-traverse": "^2.0.9", - "@textlint/feature-flag": "^3.0.5", - "@types/bluebird": "^3.5.18", - "bluebird": "^3.5.1", - "debug": "^2.6.6", - "deep-equal": "^1.0.1", - "map-like": "^2.0.0", - "object-assign": "^4.1.1", - "structured-source": "^3.0.2" - } - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "@textlint/markdown-to-ast": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-6.0.9.tgz", - "integrity": "sha512-hfAWBvTeUGh5t5kTn2U3uP3qOSM1BSrxzl1jF3nn0ywfZXpRBZr5yRjXnl4DzIYawCtZOshmRi/tI3/x4TE1jQ==", - "requires": { - "@textlint/ast-node-types": "^4.0.3", - "debug": "^2.1.3", - "remark-frontmatter": "^1.2.0", - "remark-parse": "^5.0.0", - "structured-source": "^3.0.2", - "traverse": "^0.6.6", - "unified": "^6.1.6" - } - }, - "@textlint/text-to-ast": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-3.0.9.tgz", - "integrity": "sha512-0Vycl2XtGv3pUtUNkBn9M/e3jBAtmlh7STUa3GuiyATXg49PsqqX7c8NxGPrNqMvDYCJ3ZubBx8GSEyra6ZWFw==", - "requires": { - "@textlint/ast-node-types": "^4.0.3" - } - }, - "@textlint/textlint-plugin-markdown": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-4.0.10.tgz", - "integrity": "sha512-HIV2UAhjnt9/tJQbuXkrD3CRiEFRtNpYoQEZCNCwd1nBMWUypAFthL9jT1KJ8tagOF7wEiGMB19QfDxiNQ+6mw==", - "requires": { - "@textlint/markdown-to-ast": "^6.0.8" - } - }, - "@textlint/textlint-plugin-text": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-3.0.10.tgz", - "integrity": "sha512-GSw9vsuKt7E85jDSFEXT0VYZo4C3e8XFFrSWYqXlwPKl/oQ/WHQfMg7GM288uGoEaMzbKEfBtpdwdZqTjGHOQA==", - "requires": { - "@textlint/text-to-ast": "^3.0.8" - } - }, - "@types/bluebird": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.24.tgz", - "integrity": "sha512-YeQoDpq4Lm8ppSBqAnAeF/xy1cYp/dMTif2JFcvmAbETMRlvKHT2iLcWu+WyYiJO3b3Ivokwo7EQca/xfLVJmg==" - }, - "adverb-where": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/adverb-where/-/adverb-where-0.0.9.tgz", - "integrity": "sha1-CcXN3Y1QO5/l924LjcXHCo8ZPjQ=" - }, - "aggregate-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-1.0.0.tgz", - "integrity": "sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w=", - "requires": { - "clean-stack": "^1.0.0", - "indent-string": "^3.0.0" - } - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "bail": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", - "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", - "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==" - }, - "bluebird": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", - "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==" - }, - "boundary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", - "integrity": "sha1-TWfcJgLAzBbdm85+v4fpSCkPWBI=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "cacheable-request": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", - "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", - "requires": { - "clone-response": "1.0.2", - "get-stream": "3.0.0", - "http-cache-semantics": "3.8.1", - "keyv": "3.0.0", - "lowercase-keys": "1.0.0", - "normalize-url": "2.0.1", - "responselike": "1.0.2" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" - } - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "ccount": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", - "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==" - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "character-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", - "integrity": "sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ==" - }, - "character-entities-html4": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.2.tgz", - "integrity": "sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw==" - }, - "character-entities-legacy": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz", - "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA==" - }, - "character-reference-invalid": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", - "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ==" - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "clean-stack": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz", - "integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE=" - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collapse-white-space": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", - "integrity": "sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw==" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "diff": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", - "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=" - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-socket": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-1.6.3.tgz", - "integrity": "sha512-/mUy3VGqIP69dAZjh2xxHXcpK9wk2Len1Dxz8mWAdrIgFC8tnR/aQAyU4a+UTXzOcTvEvGBdp1zFiwnpWKaXng==", - "requires": { - "dns-packet": "^1.1.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "e-prime": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/e-prime/-/e-prime-0.10.2.tgz", - "integrity": "sha1-6pN165hWNt6IATx6n7EprZ4V7/g=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "requires": { - "fill-range": "^2.1.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "fault": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.2.tgz", - "integrity": "sha512-o2eo/X2syzzERAtN5LcGbiVQ0WwZSlN3qLtadwAz3X8Bu+XWD16dja/KMsjZLiQr+BLGPDnHGkc4yUJf1Xpkpw==", - "requires": { - "format": "^0.2.2" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" - }, - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - } - }, - "fn-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz", - "integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - }, - "dependencies": { - "combined-stream": { - "version": "1.0.6", - "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "~1.0.0" - } - } - } - }, - "format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "optional": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "optional": true, - "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=" - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "requires": { - "is-glob": "^2.0.0" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "got": { - "version": "6.7.1", - "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "interop-require": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/interop-require/-/interop-require-1.0.0.tgz", - "integrity": "sha1-5TEDZ5lEyI1+YQW2Kp9EdceDlx4=" - }, - "into-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", - "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", - "requires": { - "from2": "^2.1.1", - "p-is-promise": "^1.1.0" - } - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-alphabetical": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz", - "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg==" - }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=" - }, - "is-alphanumerical": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz", - "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==", - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-decimal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", - "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg==" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" - }, - "is-empty": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", - "integrity": "sha1-3pu1snhzigWgsJpX4ftNSjQan2s=" - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" - }, - "is-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-file/-/is-file-1.0.0.tgz", - "integrity": "sha1-KKRM+9nT2xkwRfIrZfzo7fliBZY=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-hexadecimal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", - "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==" - }, - "is-hidden": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-hidden/-/is-hidden-1.1.1.tgz", - "integrity": "sha512-175UKecS8+U4hh2PSY0j4xnm2GKYzvSKnbh+naC93JjuBA7LgIo6YxlbcsSo6seFBdQO3RuIcH980yvqqD/2cA==" - }, - "is-ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", - "integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=", - "requires": { - "ip-regex": "^2.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" - }, - "is-online": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-online/-/is-online-7.0.0.tgz", - "integrity": "sha1-fiQIwK4efje6jVC9sjcmDTK/2W4=", - "requires": { - "got": "^6.7.1", - "p-any": "^1.0.0", - "p-timeout": "^1.0.0", - "public-ip": "^2.3.0" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-relative-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-2.0.0.tgz", - "integrity": "sha1-cpAtf+BLPUeS59sV+duEtyBMnO8=", - "requires": { - "is-absolute-url": "^2.0.0" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, - "is-whitespace-character": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", - "integrity": "sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ==" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "is-word-character": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.2.tgz", - "integrity": "sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isemail": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.1.3.tgz", - "integrity": "sha512-5xbsG5wYADIcB+mfLsd+nst1V/D+I7EU7LEZPo2GOIMu4JzfcRs5yQoypP4avA7QtUqgxYLKBYNv4IdzBmbhdw==", - "requires": { - "punycode": "2.x.x" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "keyv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "link-check": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/link-check/-/link-check-4.4.4.tgz", - "integrity": "sha512-yvowNBZEMOFH9nGLiJ5/YV68PBMVTo4opC2SzcACO8g4gSPTB9Rwa5GIziOX9Z5Er3Yf01DHoOyVV2LeApIw8w==", - "requires": { - "is-relative-url": "^2.0.0", - "isemail": "^3.1.2", - "ms": "^2.1.1", - "request": "^2.87.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "load-plugin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-2.2.2.tgz", - "integrity": "sha512-FYzamtURIJefQykZGtiClYuZkJBUKzmx8Tc74y8JGAulDzbzVm/C+w/MbAljHRr+REL0cRzy3WgnHE+T8gce5g==", - "requires": { - "npm-prefix": "^1.2.0", - "resolve-from": "^4.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "longest-streak": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", - "integrity": "sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-like": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-like/-/map-like-2.0.0.tgz", - "integrity": "sha1-lEltSa0zPA3DI0snrbvR6FNZU7Q=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "markdown-escapes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.2.tgz", - "integrity": "sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA==" - }, - "markdown-extensions": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", - "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==" - }, - "markdown-table": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.2.tgz", - "integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==" - }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=" - }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, - "mdast-util-compact": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz", - "integrity": "sha512-d2WS98JSDVbpSsBfVvD9TaDMlqPRz7ohM/11G0rp5jOBb5q96RJ6YLszQ/09AAixyzh23FeIpCGqfaamEADtWg==", - "requires": { - "unist-util-visit": "^1.1.0" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" - }, - "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", - "requires": { - "mime-db": "~1.36.0" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nan": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", - "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "nlcst-to-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-2.0.2.tgz", - "integrity": "sha512-DV7wVvMcAsmZ5qEwvX1JUNF4lKkAAKbChwNlIH7NLsPR7LWWoeIt53YlZ5CQH5KDXEXQ9Xa3mw0PbPewymrtew==" - }, - "no-cliches": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/no-cliches/-/no-cliches-0.1.0.tgz", - "integrity": "sha1-9OuBpVH+zegT+MYR415kpRGNw4w=" - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - } - } - }, - "npm-prefix": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/npm-prefix/-/npm-prefix-1.2.0.tgz", - "integrity": "sha1-5hlFX3B0ulTMZtbQ033Z8b5ry8A=", - "requires": { - "rc": "^1.1.0", - "shellsubstitute": "^1.1.0", - "untildify": "^2.1.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "p-any": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-any/-/p-any-1.1.0.tgz", - "integrity": "sha512-Ef0tVa4CZ5pTAmKn+Cg3w8ABBXh+hHO1aV8281dKOoUHfX+3tjG2EaFcC+aZyagg9b4EYGsHEjz21DnEE8Og2g==", - "requires": { - "p-some": "^2.0.0" - } - }, - "p-cancelable": { - "version": "0.4.1", - "resolved": "http://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-some": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-some/-/p-some-2.0.1.tgz", - "integrity": "sha1-Zdh8ixVO289SIdFnd4ttLhUPbwY=", - "requires": { - "aggregate-error": "^1.0.0" - } - }, - "p-timeout": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", - "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", - "requires": { - "p-finally": "^1.0.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" - }, - "parse-entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.2.tgz", - "integrity": "sha512-5N9lmQ7tmxfXf+hO3X6KRG6w7uYO/HL9fHalSySTdyn63C3WNvTM/1R8tn1u1larNcEbo3Slcy2bsVDQqvEpUg==", - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "passive-voice": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/passive-voice/-/passive-voice-0.1.0.tgz", - "integrity": "sha1-Fv+RrkC6DpLEPmcXY/3IQqcCcLE=" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-to-glob-pattern": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-to-glob-pattern/-/path-to-glob-pattern-1.0.2.tgz", - "integrity": "sha1-Rz5qOikqnRP7rj7czuctO6uoxhk=" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "pluralize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", - "integrity": "sha1-crcmqm+sHt7uQiVsfY3CVrM1Z38=" - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" - }, - "prettier": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.3.tgz", - "integrity": "sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==" - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" - }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" - }, - "public-ip": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-2.4.0.tgz", - "integrity": "sha512-74cIy+T2cDmt+Z71AfVipH2q6qqZITPyNGszKV86OGDYIRvti1m8zg4GOaiTPCLgEIWnToKYXbhEnMiZWHPEUA==", - "requires": { - "dns-socket": "^1.6.2", - "got": "^8.0.0", - "is-ip": "^2.0.0", - "pify": "^3.0.0" - }, - "dependencies": { - "got": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", - "requires": { - "@sindresorhus/is": "^0.7.0", - "cacheable-request": "^2.1.1", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "into-stream": "^3.1.0", - "is-retry-allowed": "^1.1.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "mimic-response": "^1.0.0", - "p-cancelable": "^0.4.0", - "p-timeout": "^2.0.1", - "pify": "^3.0.0", - "safe-buffer": "^5.1.1", - "timed-out": "^4.0.1", - "url-parse-lax": "^3.0.0", - "url-to-options": "^1.0.1" - } - }, - "p-timeout": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", - "requires": { - "p-finally": "^1.0.0" - } - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "randomatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", - "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "rc-config-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-2.0.2.tgz", - "integrity": "sha512-Nx9SNM47eNRqe0TdntOY600qWb8NDh+xU9sv5WnTscEtzfTB0ukihlqwuCLPteyJksvZ0sEVPoySNE01TKrmTQ==", - "requires": { - "debug": "^3.1.0", - "js-yaml": "^3.12.0", - "json5": "^1.0.1", - "object-assign": "^4.1.0", - "object-keys": "^1.0.12", - "path-exists": "^3.0.0", - "require-from-string": "^2.0.2" - }, - "dependencies": { - "debug": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", - "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "remark": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", - "integrity": "sha512-amw8rGdD5lHbMEakiEsllmkdBP+/KpjW/PRK6NSGPZKCQowh0BT4IWXDAkRMyG3SB9dKPXWMviFjNusXzXNn3A==", - "requires": { - "remark-parse": "^5.0.0", - "remark-stringify": "^5.0.0", - "unified": "^6.0.0" - } - }, - "remark-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-5.0.0.tgz", - "integrity": "sha512-+j0tza5XZ/XHfity3mg5GJFezRt5hS+ybC7/LDItmOAA8u8gRgB51B+/m5U3yT6RLlhefdqkMGKZnZMcamnvsQ==", - "requires": { - "markdown-extensions": "^1.1.0", - "remark": "^9.0.0", - "unified-args": "^5.0.0" - } - }, - "remark-frontmatter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-1.2.1.tgz", - "integrity": "sha512-PEXZFO3jrB+E0G6ZIsV8GOED1gPHQF5hgedJQJ8SbsLRQv4KKrFj3A+huaeu0qtzTScdxPeDTacQ9gkV4vIarA==", - "requires": { - "fault": "^1.0.1", - "xtend": "^4.0.1" - } - }, - "remark-lint-no-dead-urls": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/remark-lint-no-dead-urls/-/remark-lint-no-dead-urls-0.3.0.tgz", - "integrity": "sha512-eG+vVrNui7zeBmU6fsjIi8rwXriuyNhNcmJDQ7M5oaxCluWbH5bt6Yi/JNsabYE39dFdlVbw9JM3cLjaJv2hQw==", - "requires": { - "is-online": "^7.0.0", - "is-relative-url": "^2.0.0", - "link-check": "^4.1.0", - "unified-lint-rule": "^1.0.1", - "unist-util-visit": "^1.1.3" - } - }, - "remark-lint-write-good": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/remark-lint-write-good/-/remark-lint-write-good-1.0.3.tgz", - "integrity": "sha512-d4D4VrAklAx2ONhpXoQnt0YrJFpJBE5XEeCyDGjPhm4DkIoLOmHWZEjxl1HvdrpGXLb/KfYU4lJPeyxlKiDhVA==", - "requires": { - "nlcst-to-string": "^2.0.0", - "unified-lint-rule": "^1.0.1", - "unist-util-visit": "^1.1.1", - "write-good": "^0.11.1" - } - }, - "remark-parse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", - "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", - "requires": { - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", - "xtend": "^4.0.1" - } - }, - "remark-stringify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-5.0.0.tgz", - "integrity": "sha512-Ws5MdA69ftqQ/yhRF9XhVV29mhxbfGhbz0Rx5bQH+oJcNhhSM6nCu1EpLod+DjrFGrU0BMPs+czVmJZU7xiS7w==", - "requires": { - "ccount": "^1.0.0", - "is-alphanumeric": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "longest-streak": "^2.0.1", - "markdown-escapes": "^1.0.0", - "markdown-table": "^1.1.0", - "mdast-util-compact": "^1.0.0", - "parse-entities": "^1.0.2", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "stringify-entities": "^1.0.1", - "unherit": "^1.0.4", - "xtend": "^4.0.1" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shellsubstitute": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shellsubstitute/-/shellsubstitute-1.2.0.tgz", - "integrity": "sha1-5PcCpQxRiw9v6YRRiQ1wWvKba3A=" - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" - }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - } - }, - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", - "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==" - }, - "split-lines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/split-lines/-/split-lines-2.0.0.tgz", - "integrity": "sha512-gaIdhbqxkB5/VflPXsJwZvEzh/kdwiRPF9iqpkxX4us+lzB8INedFwjCyo6vwuz5x2Ddlnav2zh270CEjCG8mA==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "state-toggle": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz", - "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og==" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string.prototype.padstart": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.0.0.tgz", - "integrity": "sha1-W8+tOfRkm7LQMSkuGbzwtRDUskI=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.4.3", - "function-bind": "^1.0.2" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-entities": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", - "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==", - "requires": { - "character-entities-html4": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "structured-source": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-3.0.2.tgz", - "integrity": "sha1-3YAkJeD1PcSm56yjdSkBoczaevU=", - "requires": { - "boundary": "^1.0.1" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "3.8.3", - "resolved": "http://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", - "slice-ansi": "0.0.4", - "string-width": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "textlint": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/textlint/-/textlint-10.2.1.tgz", - "integrity": "sha512-tjSvxRZ7iewPmw0ShIA5IIZNJM9m157K1hGXE9wGxALcSb+xOZ0oLPv1HN7z0UzqOuMNqYyeN7mi4N0IplLkYA==", - "requires": { - "@textlint/ast-node-types": "^4.0.2", - "@textlint/ast-traverse": "^2.0.8", - "@textlint/feature-flag": "^3.0.4", - "@textlint/fixer-formatter": "^3.0.7", - "@textlint/kernel": "^2.0.9", - "@textlint/linter-formatter": "^3.0.7", - "@textlint/textlint-plugin-markdown": "^4.0.10", - "@textlint/textlint-plugin-text": "^3.0.10", - "@types/bluebird": "^3.5.18", - "bluebird": "^3.0.5", - "debug": "^2.1.0", - "deep-equal": "^1.0.1", - "file-entry-cache": "^2.0.0", - "get-stdin": "^5.0.1", - "glob": "^7.1.1", - "interop-require": "^1.0.0", - "is-file": "^1.0.0", - "log-symbols": "^1.0.2", - "map-like": "^2.0.0", - "md5": "^2.2.1", - "mkdirp": "^0.5.0", - "object-assign": "^4.0.1", - "optionator": "^0.8.0", - "path-to-glob-pattern": "^1.0.2", - "rc-config-loader": "^2.0.1", - "read-pkg": "^1.1.0", - "read-pkg-up": "^3.0.0", - "structured-source": "^3.0.2", - "try-resolve": "^1.0.1", - "unique-concat": "^0.2.2" - } - }, - "textlint-rule-helper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.0.0.tgz", - "integrity": "sha1-lctGlslcQljS4zienmS4SflyE4I=", - "requires": { - "unist-util-visit": "^1.1.0" - } - }, - "textlint-rule-stop-words": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/textlint-rule-stop-words/-/textlint-rule-stop-words-1.0.5.tgz", - "integrity": "sha512-sttfqpFX3ji4AD4eF3gpiCH+csqsaztO0V2koWVYhrHyPjUL4cPlB1I/H4Fa7G3Ik35dBA0q5Tf+88A0vO9erQ==", - "requires": { - "lodash": "^4.17.10", - "split-lines": "^2.0.0", - "textlint-rule-helper": "^2.0.0" - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "to-vfile": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-2.2.0.tgz", - "integrity": "sha512-saGC8/lWdGrEoBMLUtgzhRHWAkQMP8gdldA3MOAUhBwTGEb1RSMVcflHGSx4ZJsdEZ9o1qDBCPp47LCPrbZWow==", - "requires": { - "is-buffer": "^1.1.4", - "vfile": "^2.0.0", - "x-is-function": "^1.0.4" - } - }, - "too-wordy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/too-wordy/-/too-wordy-0.1.4.tgz", - "integrity": "sha1-jnsgp7ek2Pw3WfTgDEkpmT0bEvA=" - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" - }, - "trim-trailing-lines": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz", - "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg==" - }, - "trough": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.3.tgz", - "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==" - }, - "try-resolve": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz", - "integrity": "sha1-z95vq9ctY+V5fPqrhzq76OcA6RI=" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "unherit": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", - "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==", - "requires": { - "inherits": "^2.0.1", - "xtend": "^4.0.1" - } - }, - "unified": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", - "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^1.1.0", - "trough": "^1.0.0", - "vfile": "^2.0.0", - "x-is-string": "^0.1.0" - } - }, - "unified-args": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unified-args/-/unified-args-5.1.0.tgz", - "integrity": "sha512-IR8bS/qrfOMuIYrLlaXt+3L6cvDHv5YbBfYNVGBLbShUjE9vpbnUiPFMc/XKtH6oAGrD/m8lvVwCHDsFGBBzJA==", - "requires": { - "camelcase": "^4.0.0", - "chalk": "^2.0.0", - "chokidar": "^1.5.1", - "json5": "^0.5.1", - "minimist": "^1.2.0", - "text-table": "^0.2.0", - "unified-engine": "^5.1.0" - } - }, - "unified-engine": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unified-engine/-/unified-engine-5.1.0.tgz", - "integrity": "sha512-N7b7HG6doQUtkWr+kH35tfUhfc9QiYeiZGG6TcZlexSURf4xRUpYKBbc2f67qJF5oPmn6mMkImkdhr31Q6saoA==", - "requires": { - "concat-stream": "^1.5.1", - "debug": "^3.1.0", - "fault": "^1.0.0", - "fn-name": "^2.0.1", - "glob": "^7.0.3", - "ignore": "^3.2.0", - "is-empty": "^1.0.0", - "is-hidden": "^1.0.1", - "is-object": "^1.0.1", - "js-yaml": "^3.6.1", - "load-plugin": "^2.0.0", - "parse-json": "^4.0.0", - "to-vfile": "^2.0.0", - "trough": "^1.0.0", - "unist-util-inspect": "^4.1.2", - "vfile-reporter": "^4.0.0", - "vfile-statistics": "^1.1.0", - "x-is-function": "^1.0.4", - "x-is-string": "^0.1.0", - "xtend": "^4.0.1" - }, - "dependencies": { - "debug": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", - "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "unified-lint-rule": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/unified-lint-rule/-/unified-lint-rule-1.0.3.tgz", - "integrity": "sha512-6z+HH3mtlFdj/w3MaQpObrZAd9KRiro370GxBFh13qkV8LYR21lLozA4iQiZPhe7KuX/lHewoGOEgQ4AWrAR3Q==", - "requires": { - "wrapped": "^1.0.1" - } - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "unique-concat": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/unique-concat/-/unique-concat-0.2.2.tgz", - "integrity": "sha1-khD5vcqsxeHjkpSQ18AZ35bxhxI=" - }, - "unist-util-inspect": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/unist-util-inspect/-/unist-util-inspect-4.1.3.tgz", - "integrity": "sha512-Fv9R88ZBbDp7mHN+wsbxS1r8VW3unyhZh/F18dcJRQsg0+g3DxNQnMS+AEG/uotB8Md+HMK/TfzSU5lUDWxkZg==", - "requires": { - "is-empty": "^1.0.0" - } - }, - "unist-util-is": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz", - "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw==" - }, - "unist-util-remove-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz", - "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", - "requires": { - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-stringify-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", - "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" - }, - "unist-util-visit": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.0.tgz", - "integrity": "sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==", - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - }, - "unist-util-visit-parents": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz", - "integrity": "sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA==", - "requires": { - "unist-util-is": "^2.1.2" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "untildify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz", - "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=", - "requires": { - "os-homedir": "^1.0.0" - } - }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vfile": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", - "requires": { - "is-buffer": "^1.1.4", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^1.0.0", - "vfile-message": "^1.0.0" - } - }, - "vfile-location": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.3.tgz", - "integrity": "sha512-zM5/l4lfw1CBoPx3Jimxoc5RNDAHHpk6AM6LM0pTIkm5SUSsx8ZekZ0PVdf0WEZ7kjlhSt7ZlqbRL6Cd6dBs6A==" - }, - "vfile-message": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.1.tgz", - "integrity": "sha512-vSGCkhNvJzO6VcWC6AlJW4NtYOVtS+RgCaqFIYUjoGIlHnFL+i0LbtYvonDWOMcB97uTPT4PRsyYY7REWC9vug==", - "requires": { - "unist-util-stringify-position": "^1.1.1" - } - }, - "vfile-reporter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-4.0.0.tgz", - "integrity": "sha1-6m8K4TQvSEFXOYXgX5QXNvJ96do=", - "requires": { - "repeat-string": "^1.5.0", - "string-width": "^1.0.0", - "supports-color": "^4.1.0", - "unist-util-stringify-position": "^1.0.0", - "vfile-statistics": "^1.1.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, - "vfile-statistics": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.1.tgz", - "integrity": "sha512-dxUM6IYvGChHuwMT3dseyU5BHprNRXzAV0OHx1A769lVGsTiT50kU7BbpRFV+IE6oWmU+PwHdsTKfXhnDIRIgQ==" - }, - "weasel-words": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/weasel-words/-/weasel-words-0.1.1.tgz", - "integrity": "sha1-cTeUZYXHP+RIggE4U70ADF1oek4=" - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "wrapped": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wrapped/-/wrapped-1.0.1.tgz", - "integrity": "sha1-x4PZ2Aeyc+mwHoUWgKk4yHyQckI=", - "requires": { - "co": "3.1.0", - "sliced": "^1.0.1" - }, - "dependencies": { - "co": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", - "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=" - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-good": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/write-good/-/write-good-0.11.3.tgz", - "integrity": "sha512-fDKIHO5wCzTLCOGNJl1rzzJrZlTIzfZl8msOoJQZzRhYo0X/tFTm4+2B1zTibFYK01Nnd1kLZBjj4xjcFLePNQ==", - "requires": { - "adverb-where": "0.0.9", - "e-prime": "^0.10.2", - "no-cliches": "^0.1.0", - "object.assign": "^4.0.4", - "passive-voice": "^0.1.0", - "too-wordy": "^0.1.4", - "weasel-words": "^0.1.1" - } - }, - "x-is-function": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", - "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4=" - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" - }, - "xml-escape": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", - "integrity": "sha1-OQTBQ/qOs6ADDsZG0pAqLxtwbEQ=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index d45ba539..00000000 --- a/docs/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "dependencies": { - "prettier": "^1.13.7", - "remark-cli": "^5.0.0", - "remark-lint-no-dead-urls": "^0.3.0", - "remark-lint-write-good": "^1.0.3", - "textlint": "^10.2.1", - "textlint-rule-stop-words": "^1.0.3" - }, - "name": "tendermint", - "description": "Tendermint Core Documentation", - "version": "0.0.1", - "main": "README.md", - "devDependencies": {}, - "scripts": { - "lint:json": "prettier \"**/*.json\" --write", - "lint:md": "prettier \"**/*.md\" --write && remark . && textlint \"md/**\"", - "lint": "yarn lint:json && yarn lint:md" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/tendermint/tendermint.git" - }, - "keywords": [ - "tendermint", - "blockchain" - ], - "author": "Tendermint", - "license": "ISC", - "bugs": { - "url": "https://github.com/tendermint/tendermint/issues" - }, - "homepage": "https://tendermint.com/docs/", - "remarkConfig": { - "plugins": [ - "remark-lint-no-dead-urls", - "remark-lint-write-good" - ] - } -} diff --git a/docs/yarn.lock b/docs/yarn.lock deleted file mode 100644 index 4f453ed4..00000000 --- a/docs/yarn.lock +++ /dev/null @@ -1,2611 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@azu/format-text@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@azu/format-text/-/format-text-1.0.1.tgz#6967350a94640f6b02855169bd897ce54d6cebe2" - -"@azu/style-format@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azu/style-format/-/style-format-1.0.0.tgz#e70187f8a862e191b1bce6c0268f13acd3a56b20" - dependencies: - "@azu/format-text" "^1.0.1" - -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - -"@textlint/ast-node-types@^4.0.2", "@textlint/ast-node-types@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@textlint/ast-node-types/-/ast-node-types-4.0.3.tgz#b51c87bb86022323f764fbdc976b173f19261cc5" - -"@textlint/ast-traverse@^2.0.8", "@textlint/ast-traverse@^2.0.9": - version "2.0.9" - resolved "https://registry.yarnpkg.com/@textlint/ast-traverse/-/ast-traverse-2.0.9.tgz#4bf427cf01b7195013e75d27540a77ad68c363d9" - dependencies: - "@textlint/ast-node-types" "^4.0.3" - -"@textlint/feature-flag@^3.0.4", "@textlint/feature-flag@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@textlint/feature-flag/-/feature-flag-3.0.5.tgz#3783e0f2661053d2a74fdad775993395a2d530b4" - dependencies: - map-like "^2.0.0" - -"@textlint/fixer-formatter@^3.0.7": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@textlint/fixer-formatter/-/fixer-formatter-3.0.8.tgz#90ef804c60b9e694c8c048a06febbf1f331abd49" - dependencies: - "@textlint/kernel" "^3.0.0" - chalk "^1.1.3" - debug "^2.1.0" - diff "^2.2.2" - interop-require "^1.0.0" - is-file "^1.0.0" - string-width "^1.0.1" - text-table "^0.2.0" - try-resolve "^1.0.1" - -"@textlint/kernel@^2.0.9": - version "2.0.9" - resolved "https://registry.yarnpkg.com/@textlint/kernel/-/kernel-2.0.9.tgz#a4471b7969e192551230c35ea9fae32d80128ee0" - dependencies: - "@textlint/ast-node-types" "^4.0.2" - "@textlint/ast-traverse" "^2.0.8" - "@textlint/feature-flag" "^3.0.4" - "@types/bluebird" "^3.5.18" - bluebird "^3.5.1" - debug "^2.6.6" - deep-equal "^1.0.1" - object-assign "^4.1.1" - structured-source "^3.0.2" - -"@textlint/kernel@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@textlint/kernel/-/kernel-3.0.0.tgz#ba10962acff64f17b9e5fce8089a40f1f8880dcd" - dependencies: - "@textlint/ast-node-types" "^4.0.3" - "@textlint/ast-traverse" "^2.0.9" - "@textlint/feature-flag" "^3.0.5" - "@types/bluebird" "^3.5.18" - bluebird "^3.5.1" - debug "^2.6.6" - deep-equal "^1.0.1" - map-like "^2.0.0" - object-assign "^4.1.1" - structured-source "^3.0.2" - -"@textlint/linter-formatter@^3.0.7": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@textlint/linter-formatter/-/linter-formatter-3.0.8.tgz#030aa03ff3d85dda94ca9fa9e6bf824f9c1cb7ef" - dependencies: - "@azu/format-text" "^1.0.1" - "@azu/style-format" "^1.0.0" - "@textlint/kernel" "^3.0.0" - chalk "^1.0.0" - concat-stream "^1.5.1" - js-yaml "^3.2.4" - optionator "^0.8.1" - pluralize "^2.0.0" - string-width "^1.0.1" - string.prototype.padstart "^3.0.0" - strip-ansi "^3.0.1" - table "^3.7.8" - text-table "^0.2.0" - try-resolve "^1.0.1" - xml-escape "^1.0.0" - -"@textlint/markdown-to-ast@^6.0.8": - version "6.0.9" - resolved "https://registry.yarnpkg.com/@textlint/markdown-to-ast/-/markdown-to-ast-6.0.9.tgz#e7c89e5ad15d17dcd8e5a62758358936827658fa" - dependencies: - "@textlint/ast-node-types" "^4.0.3" - debug "^2.1.3" - remark-frontmatter "^1.2.0" - remark-parse "^5.0.0" - structured-source "^3.0.2" - traverse "^0.6.6" - unified "^6.1.6" - -"@textlint/text-to-ast@^3.0.8": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@textlint/text-to-ast/-/text-to-ast-3.0.9.tgz#dcb63f09cc79ea2096fc823c3b6cd07c79a060b5" - dependencies: - "@textlint/ast-node-types" "^4.0.3" - -"@textlint/textlint-plugin-markdown@^4.0.10": - version "4.0.10" - resolved "https://registry.yarnpkg.com/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-4.0.10.tgz#a99b4a308067597e89439a9e87bc1c4a7f4d076b" - dependencies: - "@textlint/markdown-to-ast" "^6.0.8" - -"@textlint/textlint-plugin-text@^3.0.10": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@textlint/textlint-plugin-text/-/textlint-plugin-text-3.0.10.tgz#619600bdc352d33a68e7a73d77d58b0c52b2a44f" - dependencies: - "@textlint/text-to-ast" "^3.0.8" - -"@types/bluebird@^3.5.18": - version "3.5.23" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.23.tgz#e805da976b76892b2b2e50eec29e84914c730670" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -adverb-where@0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/adverb-where/-/adverb-where-0.0.9.tgz#09c5cddd8d503b9fe5f76e0b8dc5c70a8f193e34" - -aggregate-error@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-1.0.0.tgz#888344dad0220a72e3af50906117f48771925fac" - dependencies: - clean-stack "^1.0.0" - indent-string "^3.0.0" - -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - dependencies: - color-convert "^1.9.0" - -anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -array-iterate@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.2.tgz#f66a57e84426f8097f4197fbb6c051b8e5cdf7d8" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - -aws4@^1.6.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - -bail@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - dependencies: - tweetnacl "^0.14.3" - -binary-extensions@^1.0.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" - -bluebird@^3.0.5, bluebird@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - -boundary@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/boundary/-/boundary-1.0.1.tgz#4d67dc2602c0cc16dd9bce7ebf87e948290f5812" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - -capture-stack-trace@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -ccount@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.3.tgz#f1cec43f332e2ea5a569fd46f9f5bde4e6102aff" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -character-entities-html4@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610" - -character-entities-legacy@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" - -character-entities@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" - -character-reference-invalid@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" - -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - -chokidar@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -chownr@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - -clean-stack@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" - -clone-response@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - dependencies: - mimic-response "^1.0.0" - -co@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/co/-/co-3.1.0.tgz#4ea54ea5a08938153185e15210c68d9092bc1b78" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -collapse-white-space@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" - -color-convert@^1.9.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" - dependencies: - color-name "1.1.1" - -color-name@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" - -combined-stream@1.0.6, combined-stream@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-stream@^1.5.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - dependencies: - capture-stack-trace "^1.0.0" - -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -debug@^2.1.0, debug@^2.1.2, debug@^2.1.3, debug@^2.6.6: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - dependencies: - ms "2.0.0" - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - dependencies: - mimic-response "^1.0.0" - -deep-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - -define-properties@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" - dependencies: - foreach "^2.0.5" - object-keys "^1.0.8" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - -diff@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" - -dns-packet@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-socket@^1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/dns-socket/-/dns-socket-1.6.3.tgz#5268724fad4aa46ad9c5ca4ffcd16e1de5342aab" - dependencies: - dns-packet "^1.1.0" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - -e-prime@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/e-prime/-/e-prime-0.10.2.tgz#ea9375eb985636de88013c7a9fb129ad9e15eff8" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -error-ex@^1.2.0, error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.4.3: - version "1.12.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" - dependencies: - es-to-primitive "^1.1.1" - function-bind "^1.1.1" - has "^1.0.1" - is-callable "^1.1.3" - is-regex "^1.0.4" - -es-to-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" - dependencies: - is-callable "^1.1.1" - is-date-object "^1.0.1" - is-symbol "^1.0.1" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -extend@^3.0.0, extend@~3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - -fault@^1.0.0, fault@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa" - dependencies: - format "^0.2.2" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - dependencies: - locate-path "^2.0.0" - -flat-cache@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - -fn-name@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" - -for-in@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -form-data@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" - dependencies: - asynckit "^0.4.0" - combined-stream "1.0.6" - mime-types "^2.1.12" - -format@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" - -from2@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - dependencies: - minipass "^2.2.1" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" - dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" - -function-bind@^1.0.2, function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -get-stdin@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" - -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - dependencies: - assert-plus "^1.0.0" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - -got@^8.0.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - -graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - dependencies: - has-symbol-support-x "^1.4.1" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -has@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - dependencies: - function-bind "^1.1.1" - -hosted-git-info@^2.1.4: - version "2.7.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" - -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -iconv-lite@^0.4.4: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - dependencies: - minimatch "^3.0.4" - -ignore@^3.2.0: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - -interop-require@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/interop-require/-/interop-require-1.0.0.tgz#e53103679944c88d7e6105b62a9f4475c783971e" - -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - -ip-regex@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - -ip@^1.1.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - -is-alphabetical@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41" - -is-alphanumeric@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" - -is-alphanumerical@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40" - dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.4, is-buffer@^1.1.5, is-buffer@~1.1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - -is-callable@^1.1.1, is-callable@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - -is-decimal@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-empty@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-empty/-/is-empty-1.2.0.tgz#de9bb5b278738a05a0b09a57e1fb4d4a341a9f6b" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-file/-/is-file-1.0.0.tgz#28a44cfbd9d3db193045f22b65fce8edf9620596" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-hexadecimal@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" - -is-hidden@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-hidden/-/is-hidden-1.1.1.tgz#82ee6a93aeef3fb007ad5b9457c0584d45329f38" - -is-ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab" - dependencies: - ip-regex "^2.0.0" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - -is-online@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-online/-/is-online-7.0.0.tgz#7e2408c0ae1e7e37ba8d50bdb237260d32bfd96e" - dependencies: - got "^6.7.1" - p-any "^1.0.0" - p-timeout "^1.0.0" - public-ip "^2.3.0" - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - dependencies: - path-is-inside "^1.0.1" - -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - dependencies: - has "^1.0.1" - -is-relative-url@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-relative-url/-/is-relative-url-2.0.0.tgz#72902d7fe04b3d4792e7db15f9db84b7204c9cef" - dependencies: - is-absolute-url "^2.0.0" - -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - -is-stream@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-symbol@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - -is-whitespace-character@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed" - -is-word-character@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.2.tgz#46a5dac3f2a1840898b91e576cd40d493f3ae553" - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isemail@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.1.3.tgz#64f37fc113579ea12523165c3ebe3a71a56ce571" - dependencies: - punycode "2.x.x" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - -js-yaml@^3.12.0, js-yaml@^3.2.4, js-yaml@^3.6.1: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - dependencies: - minimist "^1.2.0" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - dependencies: - json-buffer "3.0.0" - -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -kind-of@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -link-check@^4.1.0: - version "4.4.4" - resolved "https://registry.yarnpkg.com/link-check/-/link-check-4.4.4.tgz#08dbb881b70c23f1c173889c3a34d682c2e68c1a" - dependencies: - is-relative-url "^2.0.0" - isemail "^3.1.2" - ms "^2.1.1" - request "^2.87.0" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -load-plugin@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/load-plugin/-/load-plugin-2.2.2.tgz#ebc7599491ff33e5077719fbe051d5725a9f7a89" - dependencies: - npm-prefix "^1.2.0" - resolve-from "^4.0.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -lodash@^4.0.0, lodash@^4.17.4: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - dependencies: - chalk "^1.0.0" - -longest-streak@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e" - -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - -map-like@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/map-like/-/map-like-2.0.0.tgz#94496d49ad333c0dc3234b27adbbd1e8535953b4" - -markdown-escapes@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122" - -markdown-extensions@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3" - -markdown-table@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786" - -math-random@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" - -md5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - -mdast-util-compact@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz#cdb5f84e2b6a2d3114df33bd05d9cb32e3c4083a" - dependencies: - unist-util-modify-children "^1.0.0" - unist-util-visit "^1.1.0" - -micromatch@^2.1.5: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -mime-db@~1.35.0: - version "1.35.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" - -mime-types@^2.1.12, mime-types@~2.1.17: - version "2.1.19" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0" - dependencies: - mime-db "~1.35.0" - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - -minimatch@^3.0.2, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minipass@^2.2.1, minipass@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" - dependencies: - minipass "^2.2.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -ms@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - -nan@^2.9.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" - -needle@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" - dependencies: - debug "^2.1.2" - iconv-lite "^0.4.4" - sax "^1.2.4" - -nlcst-to-string@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-2.0.2.tgz#7125af4d4d369850c697192a658f01f36af9937b" - -no-cliches@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/no-cliches/-/no-cliches-0.1.0.tgz#f4eb81a551fecde813f8c611e35e64a5118dc38c" - -node-pre-gyp@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.0, normalize-path@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - -npm-bundled@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" - -npm-packlist@^1.1.6: - version "1.1.11" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -npm-prefix@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/npm-prefix/-/npm-prefix-1.2.0.tgz#e619455f7074ba54cc66d6d0d37dd9f1be6bcbc0" - dependencies: - rc "^1.1.0" - shellsubstitute "^1.1.0" - untildify "^2.1.0" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.8: - version "1.0.12" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" - -object.assign@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -optionator@^0.8.0, optionator@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-any@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-any/-/p-any-1.1.0.tgz#1d03835c7eed1e34b8e539c47b7b60d0d015d4e1" - dependencies: - p-some "^2.0.0" - -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - dependencies: - p-try "^1.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - dependencies: - p-limit "^1.1.0" - -p-some@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-some/-/p-some-2.0.1.tgz#65d87c8b154edbcf5221d167778b6d2e150f6f06" - dependencies: - aggregate-error "^1.0.0" - -p-timeout@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - dependencies: - p-finally "^1.0.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - dependencies: - p-finally "^1.0.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - -parse-entities@^1.0.2, parse-entities@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.2.tgz#9eaf719b29dc3bd62246b4332009072e01527777" - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -passive-voice@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/passive-voice/-/passive-voice-0.1.0.tgz#16ff91ae40ba0e92c43e671763fdc842a70270b1" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - -path-to-glob-pattern@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-to-glob-pattern/-/path-to-glob-pattern-1.0.2.tgz#473e6a3a292a9d13fbae3edccee72d3baba8c619" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -pluralize@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-2.0.0.tgz#72b726aa6fac1edeee42256c7d8dc256b335677f" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -prettier@^1.13.7: - version "1.14.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9" - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -public-ip@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/public-ip/-/public-ip-2.4.0.tgz#f00c028a15366d8c798e47efab6acd09a17666da" - dependencies: - dns-socket "^1.6.2" - got "^8.0.0" - is-ip "^2.0.0" - pify "^3.0.0" - -punycode@2.x.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -qs@~6.5.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -randomatic@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.0.tgz#36f2ca708e9e567f5ed2ec01949026d50aa10116" - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - -rc-config-loader@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/rc-config-loader/-/rc-config-loader-2.0.2.tgz#46eb2f98fb5b2aa7b1119d66c0554de5133f1bc1" - dependencies: - debug "^3.1.0" - js-yaml "^3.12.0" - json5 "^1.0.1" - object-assign "^4.1.0" - object-keys "^1.0.12" - path-exists "^3.0.0" - require-from-string "^2.0.2" - -rc@^1.1.0, rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - dependencies: - is-equal-shallow "^0.1.3" - -remark-cli@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/remark-cli/-/remark-cli-5.0.0.tgz#9feefd06474f3d0ff132df21b5334c546df12ab6" - dependencies: - markdown-extensions "^1.1.0" - remark "^9.0.0" - unified-args "^5.0.0" - -remark-frontmatter@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remark-frontmatter/-/remark-frontmatter-1.2.0.tgz#67905d178c0fe531ed12c57b98759f101fc2c1b5" - dependencies: - fault "^1.0.1" - xtend "^4.0.1" - -remark-lint-no-dead-urls@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/remark-lint-no-dead-urls/-/remark-lint-no-dead-urls-0.3.0.tgz#b640ecbb4ccaf780afe28c8d13e79f5dc6769449" - dependencies: - is-online "^7.0.0" - is-relative-url "^2.0.0" - link-check "^4.1.0" - unified-lint-rule "^1.0.1" - unist-util-visit "^1.1.3" - -remark-lint-write-good@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/remark-lint-write-good/-/remark-lint-write-good-1.0.3.tgz#daa4cf122212cfa06e437702ef7b43a12875bd5d" - dependencies: - nlcst-to-string "^2.0.0" - unified-lint-rule "^1.0.1" - unist-util-visit "^1.1.1" - write-good "^0.11.1" - -remark-parse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" - dependencies: - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^1.1.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^1.0.0" - vfile-location "^2.0.0" - xtend "^4.0.1" - -remark-stringify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-5.0.0.tgz#336d3a4d4a6a3390d933eeba62e8de4bd280afba" - dependencies: - ccount "^1.0.0" - is-alphanumeric "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - longest-streak "^2.0.1" - markdown-escapes "^1.0.0" - markdown-table "^1.1.0" - mdast-util-compact "^1.0.0" - parse-entities "^1.0.2" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - stringify-entities "^1.0.1" - unherit "^1.0.4" - xtend "^4.0.1" - -remark@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/remark/-/remark-9.0.0.tgz#c5cfa8ec535c73a67c4b0f12bfdbd3a67d8b2f60" - dependencies: - remark-parse "^5.0.0" - remark-stringify "^5.0.0" - unified "^6.0.0" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^1.5.0, repeat-string@^1.5.2, repeat-string@^1.5.4: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -replace-ext@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - -request@^2.87.0: - version "2.87.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - -responselike@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - dependencies: - lowercase-keys "^1.0.0" - -rimraf@^2.2.8, rimraf@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - -shellsubstitute@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shellsubstitute/-/shellsubstitute-1.2.0.tgz#e4f702a50c518b0f6fe98451890d705af29b6b70" - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - -sliced@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" - -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - dependencies: - is-plain-obj "^1.0.0" - -spdx-correct@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" - -split-lines@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-lines/-/split-lines-1.1.0.tgz#3abba8f598614142f9db8d27ab6ab875662a1e09" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -sshpk@^1.7.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - safer-buffer "^2.0.2" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -state-toggle@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - -string-width@^1.0.0, string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2", string-width@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string.prototype.padstart@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.padstart/-/string.prototype.padstart-3.0.0.tgz#5bcfad39f4649bb2d031292e19bcf0b510d4b242" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.4.3" - function-bind "^1.0.2" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - dependencies: - safe-buffer "~5.1.0" - -stringify-entities@^1.0.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7" - dependencies: - character-entities-html4 "^1.0.0" - character-entities-legacy "^1.0.0" - is-alphanumerical "^1.0.0" - is-hexadecimal "^1.0.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -structured-source@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/structured-source/-/structured-source-3.0.2.tgz#dd802425e0f53dc4a6e7aca3752901a1ccda7af5" - dependencies: - boundary "^1.0.1" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -supports-color@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - dependencies: - has-flag "^2.0.0" - -supports-color@^5.3.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - dependencies: - has-flag "^3.0.0" - -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - -tar@^4: - version "4.4.6" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" - dependencies: - chownr "^1.0.1" - fs-minipass "^1.2.5" - minipass "^2.3.3" - minizlib "^1.1.0" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.2" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - -textlint-rule-helper@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/textlint-rule-helper/-/textlint-rule-helper-2.0.0.tgz#95cb4696c95c4258d2e3389e9e64b849f9721382" - dependencies: - unist-util-visit "^1.1.0" - -textlint-rule-stop-words@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/textlint-rule-stop-words/-/textlint-rule-stop-words-1.0.3.tgz#fe2f40cbe5837331b2a09fdec57cc71758093bf0" - dependencies: - lodash "^4.17.4" - split-lines "^1.1.0" - textlint-rule-helper "^2.0.0" - -textlint@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/textlint/-/textlint-10.2.1.tgz#ee22b7967d59cef7c74a04a5f4e8883134e5c79d" - dependencies: - "@textlint/ast-node-types" "^4.0.2" - "@textlint/ast-traverse" "^2.0.8" - "@textlint/feature-flag" "^3.0.4" - "@textlint/fixer-formatter" "^3.0.7" - "@textlint/kernel" "^2.0.9" - "@textlint/linter-formatter" "^3.0.7" - "@textlint/textlint-plugin-markdown" "^4.0.10" - "@textlint/textlint-plugin-text" "^3.0.10" - "@types/bluebird" "^3.5.18" - bluebird "^3.0.5" - debug "^2.1.0" - deep-equal "^1.0.1" - file-entry-cache "^2.0.0" - get-stdin "^5.0.1" - glob "^7.1.1" - interop-require "^1.0.0" - is-file "^1.0.0" - log-symbols "^1.0.2" - map-like "^2.0.0" - md5 "^2.2.1" - mkdirp "^0.5.0" - object-assign "^4.0.1" - optionator "^0.8.0" - path-to-glob-pattern "^1.0.2" - rc-config-loader "^2.0.1" - read-pkg "^1.1.0" - read-pkg-up "^3.0.0" - structured-source "^3.0.2" - try-resolve "^1.0.1" - unique-concat "^0.2.2" - -timed-out@^4.0.0, timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - -to-vfile@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/to-vfile/-/to-vfile-2.2.0.tgz#342d1705e6df526d569b1fc8bfa29f1f36d6c416" - dependencies: - is-buffer "^1.1.4" - vfile "^2.0.0" - x-is-function "^1.0.4" - -too-wordy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/too-wordy/-/too-wordy-0.1.4.tgz#8e7b20a7b7a4d8fc3759f4e00c4929993d1b12f0" - -tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - dependencies: - punycode "^1.4.1" - -traverse@^0.6.6: - version "0.6.6" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" - -trim-trailing-lines@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz#e0ec0810fd3c3f1730516b45f49083caaf2774d9" - -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - -trough@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" - -try-resolve@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - dependencies: - prelude-ls "~1.1.2" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -unherit@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c" - dependencies: - inherits "^2.0.1" - xtend "^4.0.1" - -unified-args@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unified-args/-/unified-args-5.1.0.tgz#1889200e072998a662e6e84d817d6f4b5f448dd1" - dependencies: - camelcase "^4.0.0" - chalk "^2.0.0" - chokidar "^1.5.1" - json5 "^0.5.1" - minimist "^1.2.0" - text-table "^0.2.0" - unified-engine "^5.1.0" - -unified-engine@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/unified-engine/-/unified-engine-5.1.0.tgz#30db83bcc76c821f773bb5a8a491aa0e2471e3d1" - dependencies: - concat-stream "^1.5.1" - debug "^3.1.0" - fault "^1.0.0" - fn-name "^2.0.1" - glob "^7.0.3" - ignore "^3.2.0" - is-empty "^1.0.0" - is-hidden "^1.0.1" - is-object "^1.0.1" - js-yaml "^3.6.1" - load-plugin "^2.0.0" - parse-json "^4.0.0" - to-vfile "^2.0.0" - trough "^1.0.0" - unist-util-inspect "^4.1.2" - vfile-reporter "^4.0.0" - vfile-statistics "^1.1.0" - x-is-function "^1.0.4" - x-is-string "^0.1.0" - xtend "^4.0.1" - -unified-lint-rule@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/unified-lint-rule/-/unified-lint-rule-1.0.3.tgz#e302b0c4a7ac428c0980e049a500e59528001299" - dependencies: - wrapped "^1.0.1" - -unified@^6.0.0, unified@^6.1.6: - version "6.2.0" - resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^1.1.0" - trough "^1.0.0" - vfile "^2.0.0" - x-is-string "^0.1.0" - -unique-concat@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/unique-concat/-/unique-concat-0.2.2.tgz#9210f9bdcaacc5e1e3929490d7c019df96f18712" - -unist-util-inspect@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/unist-util-inspect/-/unist-util-inspect-4.1.3.tgz#39470e6d77485db285966df78431219aa1287822" - dependencies: - is-empty "^1.0.0" - -unist-util-is@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.2.tgz#1193fa8f2bfbbb82150633f3a8d2eb9a1c1d55db" - -unist-util-modify-children@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.2.tgz#c7f1b91712554ee59c47a05b551ed3e052a4e2d1" - dependencies: - array-iterate "^1.0.0" - -unist-util-remove-position@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz#86b5dad104d0bbfbeb1db5f5c92f3570575c12cb" - dependencies: - unist-util-visit "^1.1.0" - -unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" - -unist-util-visit-parents@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz#63fffc8929027bee04bfef7d2cce474f71cb6217" - dependencies: - unist-util-is "^2.1.2" - -unist-util-visit@^1.1.0, unist-util-visit@^1.1.1, unist-util-visit@^1.1.3: - version "1.4.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.0.tgz#1cb763647186dc26f5e1df5db6bd1e48b3cc2fb1" - dependencies: - unist-util-visit-parents "^2.0.0" - -untildify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" - dependencies: - os-homedir "^1.0.0" - -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - dependencies: - prepend-http "^1.0.1" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - dependencies: - prepend-http "^2.0.0" - -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -uuid@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vfile-location@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.3.tgz#083ba80e50968e8d420be49dd1ea9a992131df77" - -vfile-message@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.0.1.tgz#51a2ccd8a6b97a7980bb34efb9ebde9632e93677" - dependencies: - unist-util-stringify-position "^1.1.1" - -vfile-reporter@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-4.0.0.tgz#ea6f0ae1342f4841573985e05f941736f27de9da" - dependencies: - repeat-string "^1.5.0" - string-width "^1.0.0" - supports-color "^4.1.0" - unist-util-stringify-position "^1.0.0" - vfile-statistics "^1.1.0" - -vfile-statistics@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-1.1.1.tgz#a22fd4eb844c9eaddd781ad3b3246db88375e2e3" - -vfile@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a" - dependencies: - is-buffer "^1.1.4" - replace-ext "1.0.0" - unist-util-stringify-position "^1.0.0" - vfile-message "^1.0.0" - -weasel-words@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/weasel-words/-/weasel-words-0.1.1.tgz#7137946585c73fe44882013853bd000c5d687a4e" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - dependencies: - string-width "^1.0.2 || 2" - -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wrapped@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wrapped/-/wrapped-1.0.1.tgz#c783d9d807b273e9b01e851680a938c87c907242" - dependencies: - co "3.1.0" - sliced "^1.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -write-good@^0.11.1: - version "0.11.3" - resolved "https://registry.yarnpkg.com/write-good/-/write-good-0.11.3.tgz#8eeb5da9a8e155dafb1325d27eba33cb67d24d8c" - dependencies: - adverb-where "0.0.9" - e-prime "^0.10.2" - no-cliches "^0.1.0" - object.assign "^4.0.4" - passive-voice "^0.1.0" - too-wordy "^0.1.4" - weasel-words "^0.1.1" - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - -x-is-function@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/x-is-function/-/x-is-function-1.0.4.tgz#5d294dc3d268cbdd062580e0c5df77a391d1fa1e" - -x-is-string@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" - -xml-escape@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/xml-escape/-/xml-escape-1.1.0.tgz#3904c143fa8eb3a0030ec646d2902a2f1b706c44" - -xtend@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -yallist@^3.0.0, yallist@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" diff --git a/scripts/install/install_tendermint_arm.sh b/scripts/install/install_tendermint_arm.sh index 2e8d50ae..06a20f14 100644 --- a/scripts/install/install_tendermint_arm.sh +++ b/scripts/install/install_tendermint_arm.sh @@ -1,19 +1,11 @@ #!/usr/bin/env bash -# XXX: this script is intended to be run from -# a fresh Digital Ocean droplet with Ubuntu - -# upon its completion, you must either reset -# your terminal or run `source ~/.profile` - -# as written, this script will install -# tendermint core from master branch REPO=github.com/tendermint/tendermint # change this to a specific release or branch BRANCH=master -GO_VERSION=1.11.2 +GO_VERSION=1.11.4 sudo apt-get update -y diff --git a/scripts/install/install_tendermint_bsd.sh b/scripts/install/install_tendermint_bsd.sh index 0f7ef9b5..c3834058 100644 --- a/scripts/install/install_tendermint_bsd.sh +++ b/scripts/install/install_tendermint_bsd.sh @@ -16,7 +16,7 @@ set BRANCH=master set REPO=github.com/tendermint/tendermint -set GO_VERSION=1.11.2 +set GO_VERSION=1.11.4 sudo pkg update diff --git a/scripts/install/install_tendermint_ubuntu.sh b/scripts/install/install_tendermint_ubuntu.sh index 91ca1598..59ab7a0a 100644 --- a/scripts/install/install_tendermint_ubuntu.sh +++ b/scripts/install/install_tendermint_ubuntu.sh @@ -13,7 +13,7 @@ REPO=github.com/tendermint/tendermint # change this to a specific release or branch BRANCH=master -GO_VERSION=1.11.2 +GO_VERSION=1.11.4 sudo apt-get update -y sudo apt-get install -y make diff --git a/types/tx_test.go b/types/tx_test.go index 3afaaccc..5cdadce5 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -103,9 +103,9 @@ func TestComputeTxsOverhead(t *testing.T) { }{ {Txs{[]byte{6, 6, 6, 6, 6, 6}}, 2}, // one 21 Mb transaction: - {Txs{make([]byte, 22020096, 22020096)}, 5}, + {Txs{make([]byte, 22020096)}, 5}, // two 21Mb/2 sized transactions: - {Txs{make([]byte, 11010048, 11010048), make([]byte, 11010048, 11010048)}, 10}, + {Txs{make([]byte, 11010048), make([]byte, 11010048)}, 10}, {Txs{[]byte{1, 2, 3}, []byte{1, 2, 3}, []byte{4, 5, 6}}, 6}, {Txs{[]byte{100, 5, 64}, []byte{42, 116, 118}, []byte{6, 6, 6}, []byte{6, 6, 6}}, 8}, } From 4d8f29f79c194bd2ba5648a81b1c14074f4cc1f8 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 17 Dec 2018 20:52:33 +0400 Subject: [PATCH 04/31] set allow_duplicate_ip to false (#2992) * config: cors options are arrays of strings, not strings Fixes #2980 * docs: update tendermint-core/configuration.html page * set allow_duplicate_ip to false * in `tendermint testnet`, set allow_duplicate_ip to true Refs #2712 * fixes after Ismail's review --- CHANGELOG_PENDING.md | 2 ++ cmd/tendermint/commands/testnet.go | 1 + config/config.go | 2 +- docs/tendermint-core/configuration.md | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 022965bb..e2b87f74 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,6 +9,7 @@ Special thanks to external contributors on this release: * CLI/RPC/Config - [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. - [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. +- [config] \#2992 `allow_duplicate_ip` is now set to false * Apps @@ -17,6 +18,7 @@ Special thanks to external contributors on this release: * Blockchain Protocol * P2P Protocol +- multiple connections from the same IP are now disabled by default (see `allow_duplicate_ip` config option) ### FEATURES: diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 7e5635ca..10c7d937 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -145,6 +145,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error { nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) config.SetRoot(nodeDir) config.P2P.AddrBookStrict = false + config.P2P.AllowDuplicateIP = true if populatePersistentPeers { config.P2P.PersistentPeers = persistentPeers } diff --git a/config/config.go b/config/config.go index 2b9c8758..fd262f1e 100644 --- a/config/config.go +++ b/config/config.go @@ -434,7 +434,7 @@ func DefaultP2PConfig() *P2PConfig { RecvRate: 5120000, // 5 mB/s PexReactor: true, SeedMode: false, - AllowDuplicateIP: true, // so non-breaking yet + AllowDuplicateIP: false, HandshakeTimeout: 20 * time.Second, DialTimeout: 3 * time.Second, TestDialFail: false, diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index e66dcf51..0d9a58c4 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -170,7 +170,7 @@ seed_mode = false private_peer_ids = "" # Toggle to disable guard against peers connecting from the same ip. -allow_duplicate_ip = true +allow_duplicate_ip = false # Peer connection configuration. handshake_timeout = "20s" From 30f346fe44b416d0597374ec63b3423689cc6f6f Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Dec 2018 14:02:26 -0500 Subject: [PATCH 05/31] docs: add rpc link to docs navbar and re-org sidebar (#3041) * add rpc to docs navbar and close #3000 * Update config.js --- docs/.vuepress/config.js | 45 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 7c8aeee5..5ecc97cf 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -19,7 +19,10 @@ module.exports = { indexName: 'tendermint', debug: false }, - nav: [{ text: "Back to Tendermint", link: "https://tendermint.com" }], + nav: [ + { text: "Back to Tendermint", link: "https://tendermint.com" }, + { text: "RPC", link: "../rpc/" } + ], sidebar: [ { title: "Introduction", @@ -31,6 +34,20 @@ module.exports = { "/introduction/what-is-tendermint" ] }, + { + title: "Apps", + collapsable: false, + children: [ + "/app-dev/getting-started", + "/app-dev/abci-cli", + "/app-dev/app-architecture", + "/app-dev/app-development", + "/app-dev/subscribing-to-events-via-websocket", + "/app-dev/indexing-transactions", + "/app-dev/abci-spec", + "/app-dev/ecosystem" + ] + }, { title: "Tendermint Core", collapsable: false, @@ -49,15 +66,6 @@ module.exports = { "/tendermint-core/validators" ] }, - { - title: "Tools", - collapsable: false, - children: [ - "/tools/", - "/tools/benchmarking", - "/tools/monitoring" - ] - }, { title: "Networks", collapsable: false, @@ -68,18 +76,13 @@ module.exports = { ] }, { - title: "Apps", + title: "Tools", collapsable: false, - children: [ - "/app-dev/getting-started", - "/app-dev/abci-cli", - "/app-dev/app-architecture", - "/app-dev/app-development", - "/app-dev/subscribing-to-events-via-websocket", - "/app-dev/indexing-transactions", - "/app-dev/abci-spec", - "/app-dev/ecosystem" - ] + children: [ + "/tools/", + "/tools/benchmarking", + "/tools/monitoring" + ] }, { title: "Tendermint Spec", From daddebac29d9f1258f7e93282e84709bd16a0acd Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 19 Dec 2018 12:45:12 -0500 Subject: [PATCH 06/31] circleci: update go version (#3051) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dcc0e289..5669384c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &defaults working_directory: /go/src/github.com/tendermint/tendermint docker: - - image: circleci/golang:1.11.3 + - image: circleci/golang:1.11.4 environment: GOBIN: /tmp/workspace/bin From 41e2eeee9c7bb5be3f5cbe077f649edaa070b00c Mon Sep 17 00:00:00 2001 From: yutianwu Date: Sat, 22 Dec 2018 05:58:27 +0800 Subject: [PATCH 07/31] R4R: Split immutable and mutable parts of priv_validator.json (#2870) * split immutable and mutable parts of priv_validator.json * fix bugs * minor changes * retrig test * delete scripts/wire2amino.go * fix test * fixes from review * privval: remove mtx * rearrange priv_validator.go * upgrade path * write tests for the upgrade * fix for unsafe_reset_all * add test * add reset test --- CHANGELOG_PENDING.md | 1 + cmd/priv_val_server/main.go | 12 +- cmd/tendermint/commands/gen_validator.go | 2 +- cmd/tendermint/commands/init.go | 16 +- .../commands/reset_priv_validator.go | 28 +- cmd/tendermint/commands/show_validator.go | 2 +- cmd/tendermint/commands/testnet.go | 11 +- config/config.go | 70 ++-- config/toml.go | 24 +- config/toml_test.go | 2 +- consensus/common_test.go | 31 +- consensus/replay_test.go | 4 +- consensus/wal_generator.go | 5 +- node/node.go | 21 +- privval/old_priv_validator.go | 80 ++++ privval/old_priv_validator_test.go | 77 ++++ privval/priv_validator.go | 387 +++++++++++------- privval/priv_validator_test.go | 128 ++++-- rpc/test/helpers.go | 5 +- scripts/privValUpgrade.go | 41 ++ scripts/privValUpgrade_test.go | 121 ++++++ scripts/wire2amino.go | 182 -------- 22 files changed, 805 insertions(+), 445 deletions(-) create mode 100644 privval/old_priv_validator.go create mode 100644 privval/old_priv_validator_test.go create mode 100644 scripts/privValUpgrade.go create mode 100644 scripts/privValUpgrade_test.go delete mode 100644 scripts/wire2amino.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index e2b87f74..2fd0d795 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,6 +21,7 @@ Special thanks to external contributors on this release: - multiple connections from the same IP are now disabled by default (see `allow_duplicate_ip` config option) ### FEATURES: +- [privval] \#1181 Split immutable and mutable parts of priv_validator.json ### IMPROVEMENTS: diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 03aa57f4..54602558 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -13,9 +13,10 @@ import ( func main() { var ( - addr = flag.String("addr", ":26659", "Address of client to connect to") - chainID = flag.String("chain-id", "mychain", "chain id") - privValPath = flag.String("priv", "", "priv val file path") + addr = flag.String("addr", ":26659", "Address of client to connect to") + chainID = flag.String("chain-id", "mychain", "chain id") + privValKeyPath = flag.String("priv-key", "", "priv val key file path") + privValStatePath = flag.String("priv-state", "", "priv val state file path") logger = log.NewTMLogger( log.NewSyncWriter(os.Stdout), @@ -27,10 +28,11 @@ func main() { "Starting private validator", "addr", *addr, "chainID", *chainID, - "privPath", *privValPath, + "privKeyPath", *privValKeyPath, + "privStatePath", *privValStatePath, ) - pv := privval.LoadFilePV(*privValPath) + pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath) rs := privval.NewRemoteSigner( logger, diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index 20d43d4d..572bc974 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{ } func genValidator(cmd *cobra.Command, args []string) { - pv := privval.GenFilePV("") + pv := privval.GenFilePV("", "") jsbz, err := cdc.MarshalJSON(pv) if err != nil { panic(err) diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index 85ee4491..896bee2e 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/spf13/cobra" - cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" @@ -26,15 +25,18 @@ func initFiles(cmd *cobra.Command, args []string) error { func initFilesWithConfig(config *cfg.Config) error { // private validator - privValFile := config.PrivValidatorFile() + privValKeyFile := config.PrivValidatorKeyFile() + privValStateFile := config.PrivValidatorStateFile() var pv *privval.FilePV - if cmn.FileExists(privValFile) { - pv = privval.LoadFilePV(privValFile) - logger.Info("Found private validator", "path", privValFile) + if cmn.FileExists(privValKeyFile) { + pv = privval.LoadFilePV(privValKeyFile, privValStateFile) + logger.Info("Found private validator", "keyFile", privValKeyFile, + "stateFile", privValStateFile) } else { - pv = privval.GenFilePV(privValFile) + pv = privval.GenFilePV(privValKeyFile, privValStateFile) pv.Save() - logger.Info("Generated private validator", "path", privValFile) + logger.Info("Generated private validator", "keyFile", privValKeyFile, + "stateFile", privValStateFile) } nodeKeyFile := config.NodeKeyFile() diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 53d34712..122c2a72 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" ) @@ -27,36 +28,41 @@ var ResetPrivValidatorCmd = &cobra.Command{ // XXX: this is totally unsafe. // it's only suitable for testnets. func resetAll(cmd *cobra.Command, args []string) { - ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorFile(), logger) + ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorKeyFile(), + config.PrivValidatorStateFile(), logger) } // XXX: this is totally unsafe. // it's only suitable for testnets. func resetPrivValidator(cmd *cobra.Command, args []string) { - resetFilePV(config.PrivValidatorFile(), logger) + resetFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), logger) } -// ResetAll removes the privValidator and address book files plus all data. +// ResetAll removes address book files plus all data, and resets the privValdiator data. // Exported so other CLI tools can use it. -func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) { - resetFilePV(privValFile, logger) +func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) { removeAddrBook(addrBookFile, logger) if err := os.RemoveAll(dbDir); err == nil { logger.Info("Removed all blockchain history", "dir", dbDir) } else { logger.Error("Error removing all blockchain history", "dir", dbDir, "err", err) } + // recreate the dbDir since the privVal state needs to live there + cmn.EnsureDir(dbDir, 0700) + resetFilePV(privValKeyFile, privValStateFile, logger) } -func resetFilePV(privValFile string, logger log.Logger) { - if _, err := os.Stat(privValFile); err == nil { - pv := privval.LoadFilePV(privValFile) +func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) { + if _, err := os.Stat(privValKeyFile); err == nil { + pv := privval.LoadFilePVEmptyState(privValKeyFile, privValStateFile) pv.Reset() - logger.Info("Reset private validator file to genesis state", "file", privValFile) + logger.Info("Reset private validator file to genesis state", "keyFile", privValKeyFile, + "stateFile", privValStateFile) } else { - pv := privval.GenFilePV(privValFile) + pv := privval.GenFilePV(privValKeyFile, privValStateFile) pv.Save() - logger.Info("Generated private validator file", "file", privValFile) + logger.Info("Generated private validator file", "file", "keyFile", privValKeyFile, + "stateFile", privValStateFile) } } diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index 54765164..78bc0603 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -16,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{ } func showValidator(cmd *cobra.Command, args []string) { - privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile()) + privValidator := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey()) fmt.Println(string(pubKeyJSONBytes)) } diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 10c7d937..c3ef8619 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -85,11 +85,18 @@ func testnetFiles(cmd *cobra.Command, args []string) error { _ = os.RemoveAll(outputDir) return err } + err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } initFilesWithConfig(config) - pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator) - pv := privval.LoadFilePV(pvFile) + pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey) + pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState) + + pv := privval.LoadFilePV(pvKeyFile, pvStateFile) genVals[i] = types.GenesisValidator{ Address: pv.GetPubKey().Address(), PubKey: pv.GetPubKey(), diff --git a/config/config.go b/config/config.go index fd262f1e..fc50d7c6 100644 --- a/config/config.go +++ b/config/config.go @@ -35,15 +35,24 @@ var ( defaultConfigFileName = "config.toml" defaultGenesisJSONName = "genesis.json" - defaultPrivValName = "priv_validator.json" + defaultPrivValKeyName = "priv_validator_key.json" + defaultPrivValStateName = "priv_validator_state.json" + defaultNodeKeyName = "node_key.json" defaultAddrBookName = "addrbook.json" - defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName) - defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName) - defaultPrivValPath = filepath.Join(defaultConfigDir, defaultPrivValName) - defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName) - defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName) + defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName) + defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName) + defaultPrivValKeyPath = filepath.Join(defaultConfigDir, defaultPrivValKeyName) + defaultPrivValStatePath = filepath.Join(defaultDataDir, defaultPrivValStateName) + + defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName) + defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName) +) + +var ( + oldPrivVal = "priv_validator.json" + oldPrivValPath = filepath.Join(defaultConfigDir, oldPrivVal) ) // Config defines the top level configuration for a Tendermint node @@ -160,7 +169,10 @@ type BaseConfig struct { Genesis string `mapstructure:"genesis_file"` // Path to the JSON file containing the private key to use as a validator in the consensus protocol - PrivValidator string `mapstructure:"priv_validator_file"` + PrivValidatorKey string `mapstructure:"priv_validator_key_file"` + + // Path to the JSON file containing the last sign state of a validator + PrivValidatorState string `mapstructure:"priv_validator_state_file"` // TCP or UNIX socket address for Tendermint to listen on for // connections from an external PrivValidator process @@ -183,19 +195,20 @@ type BaseConfig struct { // DefaultBaseConfig returns a default base configuration for a Tendermint node func DefaultBaseConfig() BaseConfig { return BaseConfig{ - Genesis: defaultGenesisJSONPath, - PrivValidator: defaultPrivValPath, - NodeKey: defaultNodeKeyPath, - Moniker: defaultMoniker, - ProxyApp: "tcp://127.0.0.1:26658", - ABCI: "socket", - LogLevel: DefaultPackageLogLevels(), - LogFormat: LogFormatPlain, - ProfListenAddress: "", - FastSync: true, - FilterPeers: false, - DBBackend: "leveldb", - DBPath: "data", + Genesis: defaultGenesisJSONPath, + PrivValidatorKey: defaultPrivValKeyPath, + PrivValidatorState: defaultPrivValStatePath, + NodeKey: defaultNodeKeyPath, + Moniker: defaultMoniker, + ProxyApp: "tcp://127.0.0.1:26658", + ABCI: "socket", + LogLevel: DefaultPackageLogLevels(), + LogFormat: LogFormatPlain, + ProfListenAddress: "", + FastSync: true, + FilterPeers: false, + DBBackend: "leveldb", + DBPath: "data", } } @@ -218,9 +231,20 @@ func (cfg BaseConfig) GenesisFile() string { return rootify(cfg.Genesis, cfg.RootDir) } -// PrivValidatorFile returns the full path to the priv_validator.json file -func (cfg BaseConfig) PrivValidatorFile() string { - return rootify(cfg.PrivValidator, cfg.RootDir) +// PrivValidatorKeyFile returns the full path to the priv_validator_key.json file +func (cfg BaseConfig) PrivValidatorKeyFile() string { + return rootify(cfg.PrivValidatorKey, cfg.RootDir) +} + +// PrivValidatorFile returns the full path to the priv_validator_state.json file +func (cfg BaseConfig) PrivValidatorStateFile() string { + return rootify(cfg.PrivValidatorState, cfg.RootDir) +} + +// OldPrivValidatorFile returns the full path of the priv_validator.json from pre v0.28.0. +// TODO: eventually remove. +func (cfg BaseConfig) OldPrivValidatorFile() string { + return rootify(oldPrivValPath, cfg.RootDir) } // NodeKeyFile returns the full path to the node_key.json file diff --git a/config/toml.go b/config/toml.go index 9aa30451..79ae99be 100644 --- a/config/toml.go +++ b/config/toml.go @@ -95,7 +95,10 @@ log_format = "{{ .BaseConfig.LogFormat }}" genesis_file = "{{ js .BaseConfig.Genesis }}" # Path to the JSON file containing the private key to use as a validator in the consensus protocol -priv_validator_file = "{{ js .BaseConfig.PrivValidator }}" +priv_validator_key_file = "{{ js .BaseConfig.PrivValidatorKey }}" + +# Path to the JSON file containing the last sign state of a validator +priv_validator_state_file = "{{ js .BaseConfig.PrivValidatorState }}" # TCP or UNIX socket address for Tendermint to listen on for # connections from an external PrivValidator process @@ -342,7 +345,8 @@ func ResetTestRoot(testName string) *Config { baseConfig := DefaultBaseConfig() configFilePath := filepath.Join(rootDir, defaultConfigFilePath) genesisFilePath := filepath.Join(rootDir, baseConfig.Genesis) - privFilePath := filepath.Join(rootDir, baseConfig.PrivValidator) + privKeyFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorKey) + privStateFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorState) // Write default config file if missing. if !cmn.FileExists(configFilePath) { @@ -352,7 +356,8 @@ func ResetTestRoot(testName string) *Config { cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644) } // we always overwrite the priv val - cmn.MustWriteFile(privFilePath, []byte(testPrivValidator), 0644) + cmn.MustWriteFile(privKeyFilePath, []byte(testPrivValidatorKey), 0644) + cmn.MustWriteFile(privStateFilePath, []byte(testPrivValidatorState), 0644) config := TestConfig().SetRoot(rootDir) return config @@ -374,7 +379,7 @@ var testGenesis = `{ "app_hash": "" }` -var testPrivValidator = `{ +var testPrivValidatorKey = `{ "address": "A3258DCBF45DCA0DF052981870F2D1441A36D145", "pub_key": { "type": "tendermint/PubKeyEd25519", @@ -383,8 +388,11 @@ var testPrivValidator = `{ "priv_key": { "type": "tendermint/PrivKeyEd25519", "value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ==" - }, - "last_height": "0", - "last_round": "0", - "last_step": 0 + } +}` + +var testPrivValidatorState = `{ + "height": "0", + "round": "0", + "step": 0 }` diff --git a/config/toml_test.go b/config/toml_test.go index a1637f67..59528db1 100644 --- a/config/toml_test.go +++ b/config/toml_test.go @@ -60,7 +60,7 @@ func TestEnsureTestRoot(t *testing.T) { // TODO: make sure the cfg returned and testconfig are the same! baseConfig := DefaultBaseConfig() - ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidator) + ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidatorKey, baseConfig.PrivValidatorState) } func checkConfig(configFile string) bool { diff --git a/consensus/common_test.go b/consensus/common_test.go index 46be5cbd..1f6be437 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -6,14 +6,18 @@ import ( "fmt" "io/ioutil" "os" - "path" + "path/filepath" "reflect" "sort" "sync" "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" @@ -27,11 +31,6 @@ 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" ) const ( @@ -281,9 +280,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S } func loadPrivValidator(config *cfg.Config) *privval.FilePV { - privValidatorFile := config.PrivValidatorFile() - ensureDir(path.Dir(privValidatorFile), 0700) - privValidator := privval.LoadOrGenFilePV(privValidatorFile) + privValidatorKeyFile := config.PrivValidatorKeyFile() + ensureDir(filepath.Dir(privValidatorKeyFile), 0700) + privValidatorStateFile := config.PrivValidatorStateFile() + privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) privValidator.Reset() return privValidator } @@ -591,7 +591,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou for _, opt := range configOpts { opt(thisConfig) } - ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal app := appFunc() vals := types.TM2PB.ValidatorUpdates(state.Validators) app.InitChain(abci.RequestInitChain{Validators: vals}) @@ -612,16 +612,21 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF stateDB := dbm.NewMemDB() // each state needs its own db state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) - ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal var privVal types.PrivValidator if i < nValidators { privVal = privVals[i] } else { - tempFile, err := ioutil.TempFile("", "priv_validator_") + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") if err != nil { panic(err) } - privVal = privval.GenFilePV(tempFile.Name()) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + if err != nil { + panic(err) + } + + privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) } app := appFunc() diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 7cd32c7a..71b93775 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -319,7 +319,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { walFile := tempWALWithData(walBody) config.Consensus.SetWalFile(walFile) - privVal := privval.LoadFilePV(config.PrivValidatorFile()) + privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) wal, err := NewWAL(walFile) require.NoError(t, err) @@ -633,7 +633,7 @@ func TestInitChainUpdateValidators(t *testing.T) { clientCreator := proxy.NewLocalClientCreator(app) config := ResetConfig("proxy_test_") - privVal := privval.LoadFilePV(config.PrivValidatorFile()) + privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0) oldValAddr := state.Validators.Validators[0].Address diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 5ff597a5..83861d3e 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -40,8 +40,9 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) { // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS // NOTE: we can't import node package because of circular dependency. // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly. - privValidatorFile := config.PrivValidatorFile() - privValidator := privval.LoadOrGenFilePV(privValidatorFile) + privValidatorKeyFile := config.PrivValidatorKeyFile() + privValidatorStateFile := config.PrivValidatorStateFile() + privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile) genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) if err != nil { return errors.Wrap(err, "failed to read genesis file") diff --git a/node/node.go b/node/node.go index 993f1cd1..00d9e8a7 100644 --- a/node/node.go +++ b/node/node.go @@ -7,6 +7,7 @@ import ( "net" "net/http" _ "net/http/pprof" + "os" "strings" "time" @@ -86,8 +87,26 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { if err != nil { return nil, err } + + // Convert old PrivValidator if it exists. + oldPrivVal := config.OldPrivValidatorFile() + newPrivValKey := config.PrivValidatorKeyFile() + newPrivValState := config.PrivValidatorStateFile() + if _, err := os.Stat(oldPrivVal); !os.IsNotExist(err) { + oldPV, err := privval.LoadOldFilePV(oldPrivVal) + if err != nil { + return nil, fmt.Errorf("Error reading OldPrivValidator from %v: %v\n", oldPrivVal, err) + } + logger.Info("Upgrading PrivValidator file", + "old", oldPrivVal, + "newKey", newPrivValKey, + "newState", newPrivValState, + ) + oldPV.Upgrade(newPrivValKey, newPrivValState) + } + return NewNode(config, - privval.LoadOrGenFilePV(config.PrivValidatorFile()), + privval.LoadOrGenFilePV(newPrivValKey, newPrivValState), nodeKey, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), diff --git a/privval/old_priv_validator.go b/privval/old_priv_validator.go new file mode 100644 index 00000000..ec72c183 --- /dev/null +++ b/privval/old_priv_validator.go @@ -0,0 +1,80 @@ +package privval + +import ( + "io/ioutil" + "os" + + "github.com/tendermint/tendermint/crypto" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/types" +) + +// OldFilePV is the old version of the FilePV, pre v0.28.0. +type OldFilePV struct { + Address types.Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + LastHeight int64 `json:"last_height"` + LastRound int `json:"last_round"` + LastStep int8 `json:"last_step"` + LastSignature []byte `json:"last_signature,omitempty"` + LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"` + PrivKey crypto.PrivKey `json:"priv_key"` + + filePath string +} + +// LoadOldFilePV loads an OldFilePV from the filePath. +func LoadOldFilePV(filePath string) (*OldFilePV, error) { + pvJSONBytes, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, err + } + pv := &OldFilePV{} + err = cdc.UnmarshalJSON(pvJSONBytes, &pv) + if err != nil { + return nil, err + } + + // overwrite pubkey and address for convenience + pv.PubKey = pv.PrivKey.PubKey() + pv.Address = pv.PubKey.Address() + + pv.filePath = filePath + return pv, nil +} + +// Upgrade convets the OldFilePV to the new FilePV, separating the immutable and mutable components, +// and persisting them to the keyFilePath and stateFilePath, respectively. +// It renames the original file by adding ".bak". +func (oldFilePV *OldFilePV) Upgrade(keyFilePath, stateFilePath string) *FilePV { + privKey := oldFilePV.PrivKey + pvKey := FilePVKey{ + PrivKey: privKey, + PubKey: privKey.PubKey(), + Address: privKey.PubKey().Address(), + filePath: keyFilePath, + } + + pvState := FilePVLastSignState{ + Height: oldFilePV.LastHeight, + Round: oldFilePV.LastRound, + Step: oldFilePV.LastStep, + Signature: oldFilePV.LastSignature, + SignBytes: oldFilePV.LastSignBytes, + filePath: stateFilePath, + } + + // Save the new PV files + pv := &FilePV{ + Key: pvKey, + LastSignState: pvState, + } + pv.Save() + + // Rename the old PV file + err := os.Rename(oldFilePV.filePath, oldFilePV.filePath+".bak") + if err != nil { + panic(err) + } + return pv +} diff --git a/privval/old_priv_validator_test.go b/privval/old_priv_validator_test.go new file mode 100644 index 00000000..46391a3f --- /dev/null +++ b/privval/old_priv_validator_test.go @@ -0,0 +1,77 @@ +package privval_test + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/privval" +) + +const oldPrivvalContent = `{ + "address": "1D8089FAFDFAE4A637F3D616E17B92905FA2D91D", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "r3Yg2AhDZ745CNTpavsGU+mRZ8WpRXqoJuyqjN8mJq0=" + }, + "last_height": "5", + "last_round": "0", + "last_step": 3, + "last_signature": "CTr7b9ZQlrJJf+12rPl5t/YSCUc/KqV7jQogCfFJA24e7hof69X6OMT7eFLVQHyodPjD/QTA298XHV5ejxInDQ==", + "last_signbytes": "750802110500000000000000220B08B398F3E00510F48DA6402A480A20FC258973076512999C3E6839A22E9FBDB1B77CF993E8A9955412A41A59D4CAD312240A20C971B286ACB8AAA6FCA0365EB0A660B189EDC08B46B5AF2995DEFA51A28D215B10013211746573742D636861696E2D533245415533", + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "7MwvTGEWWjsYwjn2IpRb+GYsWi9nnFsw8jPLLY1UtP6vdiDYCENnvjkI1Olq+wZT6ZFnxalFeqgm7KqM3yYmrQ==" + } +}` + +func TestLoadAndUpgrade(t *testing.T) { + + oldFilePath := initTmpOldFile(t) + defer os.Remove(oldFilePath) + newStateFile, err := ioutil.TempFile("", "priv_validator_state*.json") + defer os.Remove(newStateFile.Name()) + require.NoError(t, err) + newKeyFile, err := ioutil.TempFile("", "priv_validator_key*.json") + defer os.Remove(newKeyFile.Name()) + require.NoError(t, err) + + oldPV, err := privval.LoadOldFilePV(oldFilePath) + assert.NoError(t, err) + newPV := oldPV.Upgrade(newKeyFile.Name(), newStateFile.Name()) + + assertEqualPV(t, oldPV, newPV) + assert.NoError(t, err) + upgradedPV := privval.LoadFilePV(newKeyFile.Name(), newStateFile.Name()) + assertEqualPV(t, oldPV, upgradedPV) + oldPV, err = privval.LoadOldFilePV(oldFilePath + ".bak") + require.NoError(t, err) + assertEqualPV(t, oldPV, upgradedPV) +} + +func assertEqualPV(t *testing.T, oldPV *privval.OldFilePV, newPV *privval.FilePV) { + assert.Equal(t, oldPV.Address, newPV.Key.Address) + assert.Equal(t, oldPV.Address, newPV.GetAddress()) + assert.Equal(t, oldPV.PubKey, newPV.Key.PubKey) + assert.Equal(t, oldPV.PubKey, newPV.GetPubKey()) + assert.Equal(t, oldPV.PrivKey, newPV.Key.PrivKey) + + assert.Equal(t, oldPV.LastHeight, newPV.LastSignState.Height) + assert.Equal(t, oldPV.LastRound, newPV.LastSignState.Round) + assert.Equal(t, oldPV.LastSignature, newPV.LastSignState.Signature) + assert.Equal(t, oldPV.LastSignBytes, newPV.LastSignState.SignBytes) + assert.Equal(t, oldPV.LastStep, newPV.LastSignState.Step) +} + +func initTmpOldFile(t *testing.T) string { + tmpfile, err := ioutil.TempFile("", "priv_validator_*.json") + require.NoError(t, err) + t.Logf("created test file %s", tmpfile.Name()) + _, err = tmpfile.WriteString(oldPrivvalContent) + require.NoError(t, err) + + return tmpfile.Name() +} diff --git a/privval/priv_validator.go b/privval/priv_validator.go index ba777e1f..1ee5b4d8 100644 --- a/privval/priv_validator.go +++ b/privval/priv_validator.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io/ioutil" - "sync" "time" "github.com/tendermint/tendermint/crypto" @@ -35,100 +34,90 @@ func voteToStep(vote *types.Vote) int8 { } } -// FilePV implements PrivValidator using data persisted to disk -// to prevent double signing. -// NOTE: the directory containing the pv.filePath must already exist. -// It includes the LastSignature and LastSignBytes so we don't lose the signature -// if the process crashes after signing but before the resulting consensus message is processed. -type FilePV struct { - Address types.Address `json:"address"` - PubKey crypto.PubKey `json:"pub_key"` - LastHeight int64 `json:"last_height"` - LastRound int `json:"last_round"` - LastStep int8 `json:"last_step"` - LastSignature []byte `json:"last_signature,omitempty"` - LastSignBytes cmn.HexBytes `json:"last_signbytes,omitempty"` - PrivKey crypto.PrivKey `json:"priv_key"` +//------------------------------------------------------------------------------- + +// FilePVKey stores the immutable part of PrivValidator. +type FilePVKey struct { + Address types.Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + PrivKey crypto.PrivKey `json:"priv_key"` - // For persistence. - // Overloaded for testing. filePath string - mtx sync.Mutex } -// GetAddress returns the address of the validator. -// Implements PrivValidator. -func (pv *FilePV) GetAddress() types.Address { - return pv.Address -} - -// GetPubKey returns the public key of the validator. -// Implements PrivValidator. -func (pv *FilePV) GetPubKey() crypto.PubKey { - return pv.PubKey -} - -// GenFilePV generates a new validator with randomly generated private key -// and sets the filePath, but does not call Save(). -func GenFilePV(filePath string) *FilePV { - privKey := ed25519.GenPrivKey() - return &FilePV{ - Address: privKey.PubKey().Address(), - PubKey: privKey.PubKey(), - PrivKey: privKey, - LastStep: stepNone, - filePath: filePath, - } -} - -// LoadFilePV loads a FilePV from the filePath. The FilePV handles double -// signing prevention by persisting data to the filePath. If the filePath does -// not exist, the FilePV must be created manually and saved. -func LoadFilePV(filePath string) *FilePV { - pvJSONBytes, err := ioutil.ReadFile(filePath) - if err != nil { - cmn.Exit(err.Error()) - } - pv := &FilePV{} - err = cdc.UnmarshalJSON(pvJSONBytes, &pv) - if err != nil { - cmn.Exit(fmt.Sprintf("Error reading PrivValidator from %v: %v\n", filePath, err)) - } - - // overwrite pubkey and address for convenience - pv.PubKey = pv.PrivKey.PubKey() - pv.Address = pv.PubKey.Address() - - pv.filePath = filePath - return pv -} - -// LoadOrGenFilePV loads a FilePV from the given filePath -// or else generates a new one and saves it to the filePath. -func LoadOrGenFilePV(filePath string) *FilePV { - var pv *FilePV - if cmn.FileExists(filePath) { - pv = LoadFilePV(filePath) - } else { - pv = GenFilePV(filePath) - pv.Save() - } - return pv -} - -// Save persists the FilePV to disk. -func (pv *FilePV) Save() { - pv.mtx.Lock() - defer pv.mtx.Unlock() - pv.save() -} - -func (pv *FilePV) save() { - outFile := pv.filePath +// Save persists the FilePVKey to its filePath. +func (pvKey FilePVKey) Save() { + outFile := pvKey.filePath if outFile == "" { - panic("Cannot save PrivValidator: filePath not set") + panic("Cannot save PrivValidator key: filePath not set") } - jsonBytes, err := cdc.MarshalJSONIndent(pv, "", " ") + + jsonBytes, err := cdc.MarshalJSONIndent(pvKey, "", " ") + if err != nil { + panic(err) + } + err = cmn.WriteFileAtomic(outFile, jsonBytes, 0600) + if err != nil { + panic(err) + } + +} + +//------------------------------------------------------------------------------- + +// FilePVLastSignState stores the mutable part of PrivValidator. +type FilePVLastSignState struct { + Height int64 `json:"height"` + Round int `json:"round"` + Step int8 `json:"step"` + Signature []byte `json:"signature,omitempty"` + SignBytes cmn.HexBytes `json:"signbytes,omitempty"` + + filePath string +} + +// CheckHRS checks the given height, round, step (HRS) against that of the +// FilePVLastSignState. It returns an error if the arguments constitute a regression, +// or if they match but the SignBytes are empty. +// The returned boolean indicates whether the last Signature should be reused - +// it returns true if the HRS matches the arguments and the SignBytes are not empty (indicating +// we have already signed for this HRS, and can reuse the existing signature). +// It panics if the HRS matches the arguments, there's a SignBytes, but no Signature. +func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bool, error) { + + if lss.Height > height { + return false, errors.New("Height regression") + } + + if lss.Height == height { + if lss.Round > round { + return false, errors.New("Round regression") + } + + if lss.Round == round { + if lss.Step > step { + return false, errors.New("Step regression") + } else if lss.Step == step { + if lss.SignBytes != nil { + if lss.Signature == nil { + panic("pv: Signature is nil but SignBytes is not!") + } + return true, nil + } + return false, errors.New("No SignBytes found") + } + } + } + return false, nil +} + +// Save persists the FilePvLastSignState to its filePath. +func (lss *FilePVLastSignState) Save() { + outFile := lss.filePath + if outFile == "" { + panic("Cannot save FilePVLastSignState: filePath not set") + } + jsonBytes, err := cdc.MarshalJSONIndent(lss, "", " ") if err != nil { panic(err) } @@ -138,23 +127,115 @@ func (pv *FilePV) save() { } } -// Reset resets all fields in the FilePV. -// NOTE: Unsafe! -func (pv *FilePV) Reset() { - var sig []byte - pv.LastHeight = 0 - pv.LastRound = 0 - pv.LastStep = 0 - pv.LastSignature = sig - pv.LastSignBytes = nil - pv.Save() +//------------------------------------------------------------------------------- + +// FilePV implements PrivValidator using data persisted to disk +// to prevent double signing. +// NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist. +// It includes the LastSignature and LastSignBytes so we don't lose the signature +// if the process crashes after signing but before the resulting consensus message is processed. +type FilePV struct { + Key FilePVKey + LastSignState FilePVLastSignState +} + +// GenFilePV generates a new validator with randomly generated private key +// and sets the filePaths, but does not call Save(). +func GenFilePV(keyFilePath, stateFilePath string) *FilePV { + privKey := ed25519.GenPrivKey() + + return &FilePV{ + Key: FilePVKey{ + Address: privKey.PubKey().Address(), + PubKey: privKey.PubKey(), + PrivKey: privKey, + filePath: keyFilePath, + }, + LastSignState: FilePVLastSignState{ + Step: stepNone, + filePath: stateFilePath, + }, + } +} + +// LoadFilePV loads a FilePV from the filePaths. The FilePV handles double +// signing prevention by persisting data to the stateFilePath. If either file path +// does not exist, the program will exit. +func LoadFilePV(keyFilePath, stateFilePath string) *FilePV { + return loadFilePV(keyFilePath, stateFilePath, true) +} + +// LoadFilePVEmptyState loads a FilePV from the given keyFilePath, with an empty LastSignState. +// If the keyFilePath does not exist, the program will exit. +func LoadFilePVEmptyState(keyFilePath, stateFilePath string) *FilePV { + return loadFilePV(keyFilePath, stateFilePath, false) +} + +// If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. +func loadFilePV(keyFilePath, stateFilePath string, loadState bool) *FilePV { + keyJSONBytes, err := ioutil.ReadFile(keyFilePath) + if err != nil { + cmn.Exit(err.Error()) + } + pvKey := FilePVKey{} + err = cdc.UnmarshalJSON(keyJSONBytes, &pvKey) + if err != nil { + cmn.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err)) + } + + // overwrite pubkey and address for convenience + pvKey.PubKey = pvKey.PrivKey.PubKey() + pvKey.Address = pvKey.PubKey.Address() + pvKey.filePath = keyFilePath + + pvState := FilePVLastSignState{} + if loadState { + stateJSONBytes, err := ioutil.ReadFile(stateFilePath) + if err != nil { + cmn.Exit(err.Error()) + } + err = cdc.UnmarshalJSON(stateJSONBytes, &pvState) + if err != nil { + cmn.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err)) + } + } + + pvState.filePath = stateFilePath + + return &FilePV{ + Key: pvKey, + LastSignState: pvState, + } +} + +// LoadOrGenFilePV loads a FilePV from the given filePaths +// or else generates a new one and saves it to the filePaths. +func LoadOrGenFilePV(keyFilePath, stateFilePath string) *FilePV { + var pv *FilePV + if cmn.FileExists(keyFilePath) { + pv = LoadFilePV(keyFilePath, stateFilePath) + } else { + pv = GenFilePV(keyFilePath, stateFilePath) + pv.Save() + } + return pv +} + +// GetAddress returns the address of the validator. +// Implements PrivValidator. +func (pv *FilePV) GetAddress() types.Address { + return pv.Key.Address +} + +// GetPubKey returns the public key of the validator. +// Implements PrivValidator. +func (pv *FilePV) GetPubKey() crypto.PubKey { + return pv.Key.PubKey } // SignVote signs a canonical representation of the vote, along with the // chainID. Implements PrivValidator. func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error { - pv.mtx.Lock() - defer pv.mtx.Unlock() if err := pv.signVote(chainID, vote); err != nil { return fmt.Errorf("Error signing vote: %v", err) } @@ -164,65 +245,63 @@ func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error { // SignProposal signs a canonical representation of the proposal, along with // the chainID. Implements PrivValidator. func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error { - pv.mtx.Lock() - defer pv.mtx.Unlock() if err := pv.signProposal(chainID, proposal); err != nil { return fmt.Errorf("Error signing proposal: %v", err) } return nil } -// returns error if HRS regression or no LastSignBytes. returns true if HRS is unchanged -func (pv *FilePV) checkHRS(height int64, round int, step int8) (bool, error) { - if pv.LastHeight > height { - return false, errors.New("Height regression") - } - - if pv.LastHeight == height { - if pv.LastRound > round { - return false, errors.New("Round regression") - } - - if pv.LastRound == round { - if pv.LastStep > step { - return false, errors.New("Step regression") - } else if pv.LastStep == step { - if pv.LastSignBytes != nil { - if pv.LastSignature == nil { - panic("pv: LastSignature is nil but LastSignBytes is not!") - } - return true, nil - } - return false, errors.New("No LastSignature found") - } - } - } - return false, nil +// Save persists the FilePV to disk. +func (pv *FilePV) Save() { + pv.Key.Save() + pv.LastSignState.Save() } +// Reset resets all fields in the FilePV. +// NOTE: Unsafe! +func (pv *FilePV) Reset() { + var sig []byte + pv.LastSignState.Height = 0 + pv.LastSignState.Round = 0 + pv.LastSignState.Step = 0 + pv.LastSignState.Signature = sig + pv.LastSignState.SignBytes = nil + pv.Save() +} + +// String returns a string representation of the FilePV. +func (pv *FilePV) String() string { + return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastSignState.Height, pv.LastSignState.Round, pv.LastSignState.Step) +} + +//------------------------------------------------------------------------------------ + // signVote checks if the vote is good to sign and sets the vote signature. // It may need to set the timestamp as well if the vote is otherwise the same as // a previously signed vote (ie. we crashed after signing but before the vote hit the WAL). func (pv *FilePV) signVote(chainID string, vote *types.Vote) error { height, round, step := vote.Height, vote.Round, voteToStep(vote) - signBytes := vote.SignBytes(chainID) - sameHRS, err := pv.checkHRS(height, round, step) + lss := pv.LastSignState + + sameHRS, err := lss.CheckHRS(height, round, step) if err != nil { return err } + signBytes := vote.SignBytes(chainID) + // We might crash before writing to the wal, // causing us to try to re-sign for the same HRS. // If signbytes are the same, use the last signature. // If they only differ by timestamp, use last timestamp and signature // Otherwise, return error if sameHRS { - if bytes.Equal(signBytes, pv.LastSignBytes) { - vote.Signature = pv.LastSignature - } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok { + if bytes.Equal(signBytes, lss.SignBytes) { + vote.Signature = lss.Signature + } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { vote.Timestamp = timestamp - vote.Signature = pv.LastSignature + vote.Signature = lss.Signature } else { err = fmt.Errorf("Conflicting data") } @@ -230,7 +309,7 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error { } // It passed the checks. Sign the vote - sig, err := pv.PrivKey.Sign(signBytes) + sig, err := pv.Key.PrivKey.Sign(signBytes) if err != nil { return err } @@ -244,24 +323,27 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error { // a previously signed proposal ie. we crashed after signing but before the proposal hit the WAL). func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error { height, round, step := proposal.Height, proposal.Round, stepPropose - signBytes := proposal.SignBytes(chainID) - sameHRS, err := pv.checkHRS(height, round, step) + lss := pv.LastSignState + + sameHRS, err := lss.CheckHRS(height, round, step) if err != nil { return err } + signBytes := proposal.SignBytes(chainID) + // We might crash before writing to the wal, // causing us to try to re-sign for the same HRS. // If signbytes are the same, use the last signature. // If they only differ by timestamp, use last timestamp and signature // Otherwise, return error if sameHRS { - if bytes.Equal(signBytes, pv.LastSignBytes) { - proposal.Signature = pv.LastSignature - } else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(pv.LastSignBytes, signBytes); ok { + if bytes.Equal(signBytes, lss.SignBytes) { + proposal.Signature = lss.Signature + } else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { proposal.Timestamp = timestamp - proposal.Signature = pv.LastSignature + proposal.Signature = lss.Signature } else { err = fmt.Errorf("Conflicting data") } @@ -269,7 +351,7 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error { } // It passed the checks. Sign the proposal - sig, err := pv.PrivKey.Sign(signBytes) + sig, err := pv.Key.PrivKey.Sign(signBytes) if err != nil { return err } @@ -282,20 +364,15 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error { func (pv *FilePV) saveSigned(height int64, round int, step int8, signBytes []byte, sig []byte) { - pv.LastHeight = height - pv.LastRound = round - pv.LastStep = step - pv.LastSignature = sig - pv.LastSignBytes = signBytes - pv.save() + pv.LastSignState.Height = height + pv.LastSignState.Round = round + pv.LastSignState.Step = step + pv.LastSignState.Signature = sig + pv.LastSignState.SignBytes = signBytes + pv.LastSignState.Save() } -// String returns a string representation of the FilePV. -func (pv *FilePV) String() string { - return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep) -} - -//------------------------------------- +//----------------------------------------------------------------------------------------- // returns the timestamp from the lastSignBytes. // returns true if the only difference in the votes is their timestamp. diff --git a/privval/priv_validator_test.go b/privval/priv_validator_test.go index 4f4eed97..06d75a80 100644 --- a/privval/priv_validator_test.go +++ b/privval/priv_validator_test.go @@ -18,36 +18,100 @@ import ( func TestGenLoadValidator(t *testing.T) { assert := assert.New(t) - tempFile, err := ioutil.TempFile("", "priv_validator_") + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) - privVal := GenFilePV(tempFile.Name()) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) height := int64(100) - privVal.LastHeight = height + privVal.LastSignState.Height = height privVal.Save() addr := privVal.GetAddress() - privVal = LoadFilePV(tempFile.Name()) + privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name()) assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") - assert.Equal(height, privVal.LastHeight, "expected privval.LastHeight to have been saved") + assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved") +} + +func TestResetValidator(t *testing.T) { + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + require.Nil(t, err) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) + emptyState := FilePVLastSignState{filePath: tempStateFile.Name()} + + // new priv val has empty state + assert.Equal(t, privVal.LastSignState, emptyState) + + // test vote + height, round := int64(10), 1 + voteType := byte(types.PrevoteType) + blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) + err = privVal.SignVote("mychainid", vote) + assert.NoError(t, err, "expected no error signing vote") + + // priv val after signing is not same as empty + assert.NotEqual(t, privVal.LastSignState, emptyState) + + // priv val after reset is same as empty + privVal.Reset() + assert.Equal(t, privVal.LastSignState, emptyState) } func TestLoadOrGenValidator(t *testing.T) { assert := assert.New(t) - tempFile, err := ioutil.TempFile("", "priv_validator_") + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) - tempFilePath := tempFile.Name() - if err := os.Remove(tempFilePath); err != nil { + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + tempKeyFilePath := tempKeyFile.Name() + if err := os.Remove(tempKeyFilePath); err != nil { t.Error(err) } - privVal := LoadOrGenFilePV(tempFilePath) + tempStateFilePath := tempStateFile.Name() + if err := os.Remove(tempStateFilePath); err != nil { + t.Error(err) + } + + privVal := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath) addr := privVal.GetAddress() - privVal = LoadOrGenFilePV(tempFilePath) + privVal = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath) assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") } -func TestUnmarshalValidator(t *testing.T) { +func TestUnmarshalValidatorState(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // create some fixed values + serialized := `{ + "height": "1", + "round": "1", + "step": 1 + }` + + val := FilePVLastSignState{} + err := cdc.UnmarshalJSON([]byte(serialized), &val) + require.Nil(err, "%+v", err) + + // make sure the values match + assert.EqualValues(val.Height, 1) + assert.EqualValues(val.Round, 1) + assert.EqualValues(val.Step, 1) + + // export it and make sure it is the same + out, err := cdc.MarshalJSON(val) + require.Nil(err, "%+v", err) + assert.JSONEq(serialized, string(out)) +} + +func TestUnmarshalValidatorKey(t *testing.T) { assert, require := assert.New(t), require.New(t) // create some fixed values @@ -67,22 +131,19 @@ func TestUnmarshalValidator(t *testing.T) { "type": "tendermint/PubKeyEd25519", "value": "%s" }, - "last_height": "0", - "last_round": "0", - "last_step": 0, "priv_key": { "type": "tendermint/PrivKeyEd25519", "value": "%s" } }`, addr, pubB64, privB64) - val := FilePV{} + val := FilePVKey{} err := cdc.UnmarshalJSON([]byte(serialized), &val) require.Nil(err, "%+v", err) // make sure the values match - assert.EqualValues(addr, val.GetAddress()) - assert.EqualValues(pubKey, val.GetPubKey()) + assert.EqualValues(addr, val.Address) + assert.EqualValues(pubKey, val.PubKey) assert.EqualValues(privKey, val.PrivKey) // export it and make sure it is the same @@ -94,9 +155,12 @@ func TestUnmarshalValidator(t *testing.T) { func TestSignVote(t *testing.T) { assert := assert.New(t) - tempFile, err := ioutil.TempFile("", "priv_validator_") + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) - privVal := GenFilePV(tempFile.Name()) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}} @@ -104,7 +168,7 @@ func TestSignVote(t *testing.T) { voteType := byte(types.PrevoteType) // sign a vote for first time - vote := newVote(privVal.Address, 0, height, round, voteType, block1) + vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1) err = privVal.SignVote("mychainid", vote) assert.NoError(err, "expected no error signing vote") @@ -114,10 +178,10 @@ func TestSignVote(t *testing.T) { // now try some bad votes cases := []*types.Vote{ - newVote(privVal.Address, 0, height, round-1, voteType, block1), // round regression - newVote(privVal.Address, 0, height-1, round, voteType, block1), // height regression - newVote(privVal.Address, 0, height-2, round+4, voteType, block1), // height regression and different round - newVote(privVal.Address, 0, height, round, voteType, block2), // different block + newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression + newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression + newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round + newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block } for _, c := range cases { @@ -136,9 +200,12 @@ func TestSignVote(t *testing.T) { func TestSignProposal(t *testing.T) { assert := assert.New(t) - tempFile, err := ioutil.TempFile("", "priv_validator_") + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) - privVal := GenFilePV(tempFile.Name()) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}} block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{10, []byte{3, 2, 1}}} @@ -175,9 +242,12 @@ func TestSignProposal(t *testing.T) { } func TestDifferByTimestamp(t *testing.T) { - tempFile, err := ioutil.TempFile("", "priv_validator_") + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) - privVal := GenFilePV(tempFile.Name()) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}} height, round := int64(10), 1 @@ -208,7 +278,7 @@ func TestDifferByTimestamp(t *testing.T) { { voteType := byte(types.PrevoteType) blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} - vote := newVote(privVal.Address, 0, height, round, voteType, blockID) + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) err := privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error signing vote") diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index e68ec149..b89c0a17 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -119,8 +119,9 @@ func NewTendermint(app abci.Application) *nm.Node { config := GetConfig() logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger = log.NewFilter(logger, log.AllowError()) - pvFile := config.PrivValidatorFile() - pv := privval.LoadOrGenFilePV(pvFile) + pvKeyFile := config.PrivValidatorKeyFile() + pvKeyStateFile := config.PrivValidatorStateFile() + pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) papp := proxy.NewLocalClientCreator(app) nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) if err != nil { diff --git a/scripts/privValUpgrade.go b/scripts/privValUpgrade.go new file mode 100644 index 00000000..72ce505e --- /dev/null +++ b/scripts/privValUpgrade.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "os" + + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/privval" +) + +var ( + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) +) + +func main() { + args := os.Args[1:] + if len(args) != 3 { + fmt.Println("Expected three args: ") + fmt.Println("Eg. ~/.tendermint/config/priv_validator.json ~/.tendermint/config/priv_validator_key.json ~/.tendermint/data/priv_validator_state.json") + os.Exit(1) + } + err := loadAndUpgrade(args[0], args[1], args[2]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func loadAndUpgrade(oldPVPath, newPVKeyPath, newPVStatePath string) error { + oldPV, err := privval.LoadOldFilePV(oldPVPath) + if err != nil { + return fmt.Errorf("Error reading OldPrivValidator from %v: %v\n", oldPVPath, err) + } + logger.Info("Upgrading PrivValidator file", + "old", oldPVPath, + "newKey", newPVKeyPath, + "newState", newPVStatePath, + ) + oldPV.Upgrade(newPVKeyPath, newPVStatePath) + return nil +} diff --git a/scripts/privValUpgrade_test.go b/scripts/privValUpgrade_test.go new file mode 100644 index 00000000..bac4d315 --- /dev/null +++ b/scripts/privValUpgrade_test.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/privval" +) + +const oldPrivvalContent = `{ + "address": "1D8089FAFDFAE4A637F3D616E17B92905FA2D91D", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "r3Yg2AhDZ745CNTpavsGU+mRZ8WpRXqoJuyqjN8mJq0=" + }, + "last_height": "5", + "last_round": "0", + "last_step": 3, + "last_signature": "CTr7b9ZQlrJJf+12rPl5t/YSCUc/KqV7jQogCfFJA24e7hof69X6OMT7eFLVQHyodPjD/QTA298XHV5ejxInDQ==", + "last_signbytes": "750802110500000000000000220B08B398F3E00510F48DA6402A480A20FC258973076512999C3E6839A22E9FBDB1B77CF993E8A9955412A41A59D4CAD312240A20C971B286ACB8AAA6FCA0365EB0A660B189EDC08B46B5AF2995DEFA51A28D215B10013211746573742D636861696E2D533245415533", + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "7MwvTGEWWjsYwjn2IpRb+GYsWi9nnFsw8jPLLY1UtP6vdiDYCENnvjkI1Olq+wZT6ZFnxalFeqgm7KqM3yYmrQ==" + } +}` + +func TestLoadAndUpgrade(t *testing.T) { + + oldFilePath := initTmpOldFile(t) + defer os.Remove(oldFilePath) + newStateFile, err := ioutil.TempFile("", "priv_validator_state*.json") + defer os.Remove(newStateFile.Name()) + require.NoError(t, err) + newKeyFile, err := ioutil.TempFile("", "priv_validator_key*.json") + defer os.Remove(newKeyFile.Name()) + require.NoError(t, err) + emptyOldFile, err := ioutil.TempFile("", "priv_validator_empty*.json") + require.NoError(t, err) + defer os.Remove(emptyOldFile.Name()) + + type args struct { + oldPVPath string + newPVKeyPath string + newPVStatePath string + } + tests := []struct { + name string + args args + wantErr bool + wantPanic bool + }{ + {"successful upgrade", + args{oldPVPath: oldFilePath, newPVKeyPath: newKeyFile.Name(), newPVStatePath: newStateFile.Name()}, + false, false, + }, + {"unsuccessful upgrade: empty old privval file", + args{oldPVPath: emptyOldFile.Name(), newPVKeyPath: newKeyFile.Name(), newPVStatePath: newStateFile.Name()}, + true, false, + }, + {"unsuccessful upgrade: invalid new paths (1/3)", + args{oldPVPath: oldFilePath, newPVKeyPath: "", newPVStatePath: newStateFile.Name()}, + false, true, + }, + {"unsuccessful upgrade: invalid new paths (2/3)", + args{oldPVPath: oldFilePath, newPVKeyPath: newKeyFile.Name(), newPVStatePath: ""}, + false, true, + }, + {"unsuccessful upgrade: invalid new paths (3/3)", + args{oldPVPath: oldFilePath, newPVKeyPath: "", newPVStatePath: ""}, + false, true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // need to re-write the file everytime because upgrading renames it + err := ioutil.WriteFile(oldFilePath, []byte(oldPrivvalContent), 0600) + require.NoError(t, err) + if tt.wantPanic { + require.Panics(t, func() { loadAndUpgrade(tt.args.oldPVPath, tt.args.newPVKeyPath, tt.args.newPVStatePath) }) + } else { + err = loadAndUpgrade(tt.args.oldPVPath, tt.args.newPVKeyPath, tt.args.newPVStatePath) + if tt.wantErr { + assert.Error(t, err) + fmt.Println("ERR", err) + } else { + assert.NoError(t, err) + upgradedPV := privval.LoadFilePV(tt.args.newPVKeyPath, tt.args.newPVStatePath) + oldPV, err := privval.LoadOldFilePV(tt.args.oldPVPath + ".bak") + require.NoError(t, err) + + assert.Equal(t, oldPV.Address, upgradedPV.Key.Address) + assert.Equal(t, oldPV.Address, upgradedPV.GetAddress()) + assert.Equal(t, oldPV.PubKey, upgradedPV.Key.PubKey) + assert.Equal(t, oldPV.PubKey, upgradedPV.GetPubKey()) + assert.Equal(t, oldPV.PrivKey, upgradedPV.Key.PrivKey) + + assert.Equal(t, oldPV.LastHeight, upgradedPV.LastSignState.Height) + assert.Equal(t, oldPV.LastRound, upgradedPV.LastSignState.Round) + assert.Equal(t, oldPV.LastSignature, upgradedPV.LastSignState.Signature) + assert.Equal(t, oldPV.LastSignBytes, upgradedPV.LastSignState.SignBytes) + assert.Equal(t, oldPV.LastStep, upgradedPV.LastSignState.Step) + + } + } + }) + } +} + +func initTmpOldFile(t *testing.T) string { + tmpfile, err := ioutil.TempFile("", "priv_validator_*.json") + require.NoError(t, err) + t.Logf("created test file %s", tmpfile.Name()) + _, err = tmpfile.WriteString(oldPrivvalContent) + require.NoError(t, err) + + return tmpfile.Name() +} diff --git a/scripts/wire2amino.go b/scripts/wire2amino.go deleted file mode 100644 index 26069b50..00000000 --- a/scripts/wire2amino.go +++ /dev/null @@ -1,182 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "time" - - "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/crypto/ed25519" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - - cmn "github.com/tendermint/tendermint/libs/common" - - "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/privval" - "github.com/tendermint/tendermint/types" -) - -type GenesisValidator struct { - PubKey Data `json:"pub_key"` - Power int64 `json:"power"` - Name string `json:"name"` -} - -type Genesis struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - ConsensusParams *types.ConsensusParams `json:"consensus_params,omitempty"` - Validators []GenesisValidator `json:"validators"` - AppHash cmn.HexBytes `json:"app_hash"` - AppState json.RawMessage `json:"app_state,omitempty"` - AppOptions json.RawMessage `json:"app_options,omitempty"` // DEPRECATED -} - -type NodeKey struct { - PrivKey Data `json:"priv_key"` -} - -type PrivVal struct { - Address cmn.HexBytes `json:"address"` - LastHeight int64 `json:"last_height"` - LastRound int `json:"last_round"` - LastStep int8 `json:"last_step"` - PubKey Data `json:"pub_key"` - PrivKey Data `json:"priv_key"` -} - -type Data struct { - Type string `json:"type"` - Data cmn.HexBytes `json:"data"` -} - -func convertNodeKey(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { - var nodeKey NodeKey - err := json.Unmarshal(jsonBytes, &nodeKey) - if err != nil { - return nil, err - } - - var privKey ed25519.PrivKeyEd25519 - copy(privKey[:], nodeKey.PrivKey.Data) - - nodeKeyNew := p2p.NodeKey{privKey} - - bz, err := cdc.MarshalJSON(nodeKeyNew) - if err != nil { - return nil, err - } - return bz, nil -} - -func convertPrivVal(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { - var privVal PrivVal - err := json.Unmarshal(jsonBytes, &privVal) - if err != nil { - return nil, err - } - - var privKey ed25519.PrivKeyEd25519 - copy(privKey[:], privVal.PrivKey.Data) - - var pubKey ed25519.PubKeyEd25519 - copy(pubKey[:], privVal.PubKey.Data) - - privValNew := privval.FilePV{ - Address: pubKey.Address(), - PubKey: pubKey, - LastHeight: privVal.LastHeight, - LastRound: privVal.LastRound, - LastStep: privVal.LastStep, - PrivKey: privKey, - } - - bz, err := cdc.MarshalJSON(privValNew) - if err != nil { - return nil, err - } - return bz, nil -} - -func convertGenesis(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { - var genesis Genesis - err := json.Unmarshal(jsonBytes, &genesis) - if err != nil { - return nil, err - } - - genesisNew := types.GenesisDoc{ - GenesisTime: genesis.GenesisTime, - ChainID: genesis.ChainID, - ConsensusParams: genesis.ConsensusParams, - // Validators - AppHash: genesis.AppHash, - AppState: genesis.AppState, - } - - if genesis.AppOptions != nil { - genesisNew.AppState = genesis.AppOptions - } - - for _, v := range genesis.Validators { - var pubKey ed25519.PubKeyEd25519 - copy(pubKey[:], v.PubKey.Data) - genesisNew.Validators = append( - genesisNew.Validators, - types.GenesisValidator{ - PubKey: pubKey, - Power: v.Power, - Name: v.Name, - }, - ) - - } - - bz, err := cdc.MarshalJSON(genesisNew) - if err != nil { - return nil, err - } - return bz, nil -} - -func main() { - cdc := amino.NewCodec() - cryptoAmino.RegisterAmino(cdc) - - args := os.Args[1:] - if len(args) != 1 { - fmt.Println("Please specify a file to convert") - os.Exit(1) - } - - filePath := args[0] - fileName := filepath.Base(filePath) - - fileBytes, err := ioutil.ReadFile(filePath) - if err != nil { - panic(err) - } - - var bz []byte - - switch fileName { - case "node_key.json": - bz, err = convertNodeKey(cdc, fileBytes) - case "priv_validator.json": - bz, err = convertPrivVal(cdc, fileBytes) - case "genesis.json": - bz, err = convertGenesis(cdc, fileBytes) - default: - fmt.Println("Expected file name to be in (node_key.json, priv_validator.json, genesis.json)") - os.Exit(1) - } - - if err != nil { - panic(err) - } - fmt.Println(string(bz)) - -} From 2348f38927a8766592e02f63f7cfafffbff7f4b2 Mon Sep 17 00:00:00 2001 From: needkane <604476380@qq.com> Date: Sat, 22 Dec 2018 06:37:28 +0800 Subject: [PATCH 08/31] Update node_info.go (#3059) --- p2p/node_info.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/node_info.go b/p2p/node_info.go index 3e02e9c1..699fd7f1 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 ) From 6a80412a01c131a83cfe0eb3dfee2b9c6c740053 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Sat, 22 Dec 2018 06:36:45 +0100 Subject: [PATCH 09/31] Remove privval.GetAddress(), memoize pubkey (#2948) privval: remove GetAddress(), memoize pubkey --- CHANGELOG_PENDING.md | 9 ++- blockchain/reactor_test.go | 2 +- cmd/tendermint/commands/init.go | 5 +- consensus/common_test.go | 12 ++-- consensus/reactor_test.go | 9 ++- consensus/replay_test.go | 6 -- consensus/state.go | 23 ++++---- consensus/state_test.go | 42 +++++++++----- consensus/types/height_vote_set_test.go | 3 +- node/node.go | 14 +++-- privval/ipc.go | 5 +- privval/remote_signer.go | 71 ++++++++++++----------- privval/tcp.go | 6 +- privval/tcp_test.go | 28 +++------ types/evidence_test.go | 3 +- types/priv_validator.go | 11 +--- types/protobuf_test.go | 5 +- types/test_util.go | 4 +- types/validator.go | 3 +- types/vote_set_test.go | 76 ++++++++++++++++--------- 20 files changed, 194 insertions(+), 143 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2fd0d795..aaf80a4f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -11,9 +11,14 @@ Special thanks to external contributors on this release: - [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. - [config] \#2992 `allow_duplicate_ip` is now set to false +- [privval] \#2926 split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types + * Apps -* Go API +* Go API +- [types] \#2926 memoize consensus public key on initialization of remote signer and return the memoized key on +`PrivValidator.GetPubKey()` instead of requesting it again +- [types] \#2981 Remove `PrivValidator.GetAddress()` * Blockchain Protocol @@ -26,4 +31,4 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: - +- [types] \#2926 do not panic if retrieving the private validator's public key fails diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index ac499efa..f6c29d65 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -42,7 +42,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G } func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote { - addr := privVal.GetAddress() + addr := privVal.GetPubKey().Address() idx, _ := valset.GetByAddress(addr) vote := &types.Vote{ ValidatorAddress: addr, diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index 896bee2e..1d6e24d7 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -59,9 +59,10 @@ func initFilesWithConfig(config *cfg.Config) error { GenesisTime: tmtime.Now(), ConsensusParams: types.DefaultConsensusParams(), } + key := pv.GetPubKey() genDoc.Validators = []types.GenesisValidator{{ - Address: pv.GetPubKey().Address(), - PubKey: pv.GetPubKey(), + Address: key.Address(), + PubKey: key, Power: 10, }} diff --git a/consensus/common_test.go b/consensus/common_test.go index 1f6be437..a975b2b6 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -71,9 +71,10 @@ func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validato } func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) { + addr := vs.PrivValidator.GetPubKey().Address() vote := &types.Vote{ ValidatorIndex: vs.Index, - ValidatorAddress: vs.PrivValidator.GetAddress(), + ValidatorAddress: addr, Height: vs.Height, Round: vs.Round, Timestamp: tmtime.Now(), @@ -150,8 +151,9 @@ func signAddVotes(to *ConsensusState, voteType types.SignedMsgType, hash []byte, func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) { prevotes := cs.Votes.Prevotes(round) + address := privVal.GetPubKey().Address() var vote *types.Vote - if vote = prevotes.GetByAddress(privVal.GetAddress()); vote == nil { + if vote = prevotes.GetByAddress(address); vote == nil { panic("Failed to find prevote from validator") } if blockHash == nil { @@ -167,8 +169,9 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *valid func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorStub, blockHash []byte) { votes := cs.LastCommit + address := privVal.GetPubKey().Address() var vote *types.Vote - if vote = votes.GetByAddress(privVal.GetAddress()); vote == nil { + if vote = votes.GetByAddress(address); vote == nil { panic("Failed to find precommit from validator") } if !bytes.Equal(vote.BlockID.Hash, blockHash) { @@ -178,8 +181,9 @@ func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorS func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *validatorStub, votedBlockHash, lockedBlockHash []byte) { precommits := cs.Votes.Precommits(thisRound) + address := privVal.GetPubKey().Address() var vote *types.Vote - if vote = precommits.GetByAddress(privVal.GetAddress()); vote == nil { + if vote = precommits.GetByAddress(address); vote == nil { panic("Failed to find precommit from validator") } diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 1636785c..5334895f 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -143,7 +143,8 @@ func TestReactorWithEvidence(t *testing.T) { // mock the evidence pool // everyone includes evidence of another double signing vIdx := (i + 1) % nValidators - evpool := newMockEvidencePool(privVals[vIdx].GetAddress()) + addr := privVals[vIdx].GetPubKey().Address() + evpool := newMockEvidencePool(addr) // Make ConsensusState blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool) @@ -268,7 +269,8 @@ func TestReactorVotingPowerChange(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - activeVals[string(css[i].privValidator.GetAddress())] = struct{}{} + addr := css[i].privValidator.GetPubKey().Address() + activeVals[string(addr)] = struct{}{} } // wait till everyone makes block 1 @@ -331,7 +333,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - activeVals[string(css[i].privValidator.GetAddress())] = struct{}{} + addr := css[i].privValidator.GetPubKey().Address() + activeVals[string(addr)] = struct{}{} } // wait till everyone makes block 1 diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 71b93775..7c00251e 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -659,12 +659,6 @@ func TestInitChainUpdateValidators(t *testing.T) { assert.Equal(t, newValAddr, expectValAddr) } -func newInitChainApp(vals []abci.ValidatorUpdate) *initChainApp { - return &initChainApp{ - vals: vals, - } -} - // returns the vals on InitChain type initChainApp struct { abci.BaseApplication diff --git a/consensus/state.go b/consensus/state.go index 81bdce7d..1693e36b 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -2,13 +2,14 @@ package consensus import ( "bytes" - "errors" "fmt" "reflect" "runtime/debug" "sync" "time" + "github.com/pkg/errors" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" @@ -829,13 +830,14 @@ func (cs *ConsensusState) enterPropose(height int64, round int) { } // if not a validator, we're done - if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) { - logger.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators) + address := cs.privValidator.GetPubKey().Address() + if !cs.Validators.HasAddress(address) { + logger.Debug("This node is not a validator", "addr", address, "vals", cs.Validators) return } logger.Debug("This node is a validator") - if cs.isProposer() { + if cs.isProposer(address) { logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator) cs.decideProposal(height, round) } else { @@ -843,8 +845,8 @@ func (cs *ConsensusState) enterPropose(height int64, round int) { } } -func (cs *ConsensusState) isProposer() bool { - return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.GetAddress()) +func (cs *ConsensusState) isProposer(address []byte) bool { + return bytes.Equal(cs.Validators.GetProposer().Address, address) } func (cs *ConsensusState) defaultDecideProposal(height int64, round int) { @@ -929,7 +931,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts cs.state.Validators.Size(), len(evidence), ), maxGas) - proposerAddr := cs.privValidator.GetAddress() + proposerAddr := cs.privValidator.GetPubKey().Address() block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence, proposerAddr) return block, parts @@ -1474,7 +1476,8 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, err if err == ErrVoteHeightMismatch { return added, err } else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok { - if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) { + addr := cs.privValidator.GetPubKey().Address() + if bytes.Equal(vote.ValidatorAddress, addr) { cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type) return added, err } @@ -1639,7 +1642,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, } func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) { - addr := cs.privValidator.GetAddress() + addr := cs.privValidator.GetPubKey().Address() valIndex, _ := cs.Validators.GetByAddress(addr) vote := &types.Vote{ @@ -1675,7 +1678,7 @@ func (cs *ConsensusState) voteTime() time.Time { // sign the vote and publish on internalMsgQueue func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { // if we don't have a key or we're not in the validator set, do nothing - if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) { + if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetPubKey().Address()) { return nil } vote, err := cs.signVote(type_, hash, header) diff --git a/consensus/state_test.go b/consensus/state_test.go index ddab6404..40103e47 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -73,7 +73,8 @@ func TestStateProposerSelection0(t *testing.T) { // Commit a block and ensure proposer for the next height is correct. prop := cs1.GetRoundState().Validators.GetProposer() - if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) { + address := cs1.privValidator.GetPubKey().Address() + if !bytes.Equal(prop.Address, address) { t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) } @@ -87,7 +88,8 @@ func TestStateProposerSelection0(t *testing.T) { ensureNewRound(newRoundCh, height+1, 0) prop = cs1.GetRoundState().Validators.GetProposer() - if !bytes.Equal(prop.Address, vss[1].GetAddress()) { + addr := vss[1].GetPubKey().Address() + if !bytes.Equal(prop.Address, addr) { panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address)) } } @@ -110,7 +112,8 @@ func TestStateProposerSelection2(t *testing.T) { // everyone just votes nil. we get a new proposer each round for i := 0; i < len(vss); i++ { prop := cs1.GetRoundState().Validators.GetProposer() - correctProposer := vss[(i+round)%len(vss)].GetAddress() + addr := vss[(i+round)%len(vss)].GetPubKey().Address() + correctProposer := addr if !bytes.Equal(prop.Address, correctProposer) { panic(fmt.Sprintf("expected RoundState.Validators.GetProposer() to be validator %d. Got %X", (i+2)%len(vss), prop.Address)) } @@ -505,7 +508,8 @@ func TestStateLockPOLRelock(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) @@ -596,7 +600,8 @@ func TestStateLockPOLUnlock(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // everything done from perspective of cs1 @@ -689,7 +694,8 @@ func TestStateLockPOLSafety1(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, round) @@ -805,7 +811,8 @@ func TestStateLockPOLSafety2(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // the block for R0: gets polkad but we miss it // (even though we signed it, shhh) @@ -896,7 +903,8 @@ func TestProposeValidBlock(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, round) @@ -982,7 +990,8 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote startTestRound(cs1, cs1.Height, round) @@ -1041,7 +1050,8 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) round = round + 1 // move to round in which P0 is not proposer @@ -1111,7 +1121,8 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round startTestRound(cs1, height, round) @@ -1144,7 +1155,8 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round startTestRound(cs1, height, round) @@ -1177,7 +1189,8 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round in which PO is not proposer startTestRound(cs1, height, round) @@ -1361,7 +1374,8 @@ func TestStateHalt1(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock) - voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress()) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote startTestRound(cs1, height, round) diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index e2298cef..4460cd3e 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -50,8 +50,9 @@ func TestPeerCatchupRounds(t *testing.T) { func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote { privVal := privVals[valIndex] + addr := privVal.GetPubKey().Address() vote := &types.Vote{ - ValidatorAddress: privVal.GetAddress(), + ValidatorAddress: addr, ValidatorIndex: valIndex, Height: height, Round: round, diff --git a/node/node.go b/node/node.go index 00d9e8a7..be4c7cc7 100644 --- a/node/node.go +++ b/node/node.go @@ -259,16 +259,19 @@ func NewNode(config *cfg.Config, fastSync := config.FastSync if state.Validators.Size() == 1 { addr, _ := state.Validators.GetByIndex(0) - if bytes.Equal(privValidator.GetAddress(), addr) { + privValAddr := privValidator.GetPubKey().Address() + if bytes.Equal(privValAddr, addr) { fastSync = false } } + pubKey := privValidator.GetPubKey() + addr := pubKey.Address() // Log whether this node is a validator or an observer - if state.Validators.HasAddress(privValidator.GetAddress()) { - consensusLogger.Info("This node is a validator", "addr", privValidator.GetAddress(), "pubKey", privValidator.GetPubKey()) + if state.Validators.HasAddress(addr) { + consensusLogger.Info("This node is a validator", "addr", addr, "pubKey", pubKey) } else { - consensusLogger.Info("This node is not a validator", "addr", privValidator.GetAddress(), "pubKey", privValidator.GetPubKey()) + consensusLogger.Info("This node is not a validator", "addr", addr, "pubKey", pubKey) } csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider() @@ -636,7 +639,8 @@ func (n *Node) ConfigureRPC() { rpccore.SetEvidencePool(n.evidencePool) rpccore.SetP2PPeers(n.sw) rpccore.SetP2PTransport(n) - rpccore.SetPubKey(n.privValidator.GetPubKey()) + pubKey := n.privValidator.GetPubKey() + rpccore.SetPubKey(pubKey) rpccore.SetGenesisDoc(n.genesisDoc) rpccore.SetAddrBook(n.addrBook) rpccore.SetProxyAppQuery(n.proxyApp.Query()) diff --git a/privval/ipc.go b/privval/ipc.go index eda23fe6..1c82db33 100644 --- a/privval/ipc.go +++ b/privval/ipc.go @@ -67,7 +67,10 @@ func (sc *IPCVal) OnStart() error { return err } - sc.RemoteSignerClient = NewRemoteSignerClient(sc.conn) + sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn) + if err != nil { + return err + } // Start a routine to keep the connection alive sc.cancelPing = make(chan struct{}, 1) diff --git a/privval/remote_signer.go b/privval/remote_signer.go index 5d6339c3..b80884de 100644 --- a/privval/remote_signer.go +++ b/privval/remote_signer.go @@ -6,6 +6,8 @@ import ( "net" "sync" + "github.com/pkg/errors" + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" @@ -15,8 +17,9 @@ import ( // RemoteSignerClient implements PrivValidator, it uses a socket to request signatures // from an external process. type RemoteSignerClient struct { - conn net.Conn - lock sync.Mutex + conn net.Conn + consensusPubKey crypto.PubKey + mtx sync.Mutex } // Check that RemoteSignerClient implements PrivValidator. @@ -25,38 +28,29 @@ var _ types.PrivValidator = (*RemoteSignerClient)(nil) // NewRemoteSignerClient returns an instance of RemoteSignerClient. func NewRemoteSignerClient( conn net.Conn, -) *RemoteSignerClient { +) (*RemoteSignerClient, error) { sc := &RemoteSignerClient{ conn: conn, } - return sc -} - -// GetAddress implements PrivValidator. -func (sc *RemoteSignerClient) GetAddress() types.Address { pubKey, err := sc.getPubKey() if err != nil { - panic(err) + return nil, cmn.ErrorWrap(err, "error while retrieving public key for remote signer") } - - return pubKey.Address() + // retrieve and memoize the consensus public key once: + sc.consensusPubKey = pubKey + return sc, nil } // GetPubKey implements PrivValidator. func (sc *RemoteSignerClient) GetPubKey() crypto.PubKey { - pubKey, err := sc.getPubKey() - if err != nil { - panic(err) - } - - return pubKey + return sc.consensusPubKey } func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) { - sc.lock.Lock() - defer sc.lock.Unlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() - err := writeMsg(sc.conn, &PubKeyMsg{}) + err := writeMsg(sc.conn, &PubKeyRequest{}) if err != nil { return nil, err } @@ -65,14 +59,22 @@ func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) { if err != nil { return nil, err } + pubKeyResp, ok := res.(*PubKeyResponse) + if !ok { + return nil, errors.Wrap(ErrUnexpectedResponse, "response is not PubKeyResponse") + } - return res.(*PubKeyMsg).PubKey, nil + if pubKeyResp.Error != nil { + return nil, errors.Wrap(pubKeyResp.Error, "failed to get private validator's public key") + } + + return pubKeyResp.PubKey, nil } // SignVote implements PrivValidator. func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error { - sc.lock.Lock() - defer sc.lock.Unlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote}) if err != nil { @@ -101,8 +103,8 @@ func (sc *RemoteSignerClient) SignProposal( chainID string, proposal *types.Proposal, ) error { - sc.lock.Lock() - defer sc.lock.Unlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal}) if err != nil { @@ -127,8 +129,8 @@ func (sc *RemoteSignerClient) SignProposal( // Ping is used to check connection health. func (sc *RemoteSignerClient) Ping() error { - sc.lock.Lock() - defer sc.lock.Unlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() err := writeMsg(sc.conn, &PingRequest{}) if err != nil { @@ -152,7 +154,8 @@ type RemoteSignerMsg interface{} func RegisterRemoteSignerMsg(cdc *amino.Codec) { cdc.RegisterInterface((*RemoteSignerMsg)(nil), nil) - cdc.RegisterConcrete(&PubKeyMsg{}, "tendermint/remotesigner/PubKeyMsg", nil) + cdc.RegisterConcrete(&PubKeyRequest{}, "tendermint/remotesigner/PubKeyRequest", nil) + cdc.RegisterConcrete(&PubKeyResponse{}, "tendermint/remotesigner/PubKeyResponse", nil) cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/remotesigner/SignVoteRequest", nil) cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/remotesigner/SignedVoteResponse", nil) cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/remotesigner/SignProposalRequest", nil) @@ -161,9 +164,13 @@ func RegisterRemoteSignerMsg(cdc *amino.Codec) { cdc.RegisterConcrete(&PingResponse{}, "tendermint/remotesigner/PingResponse", nil) } -// PubKeyMsg is a PrivValidatorSocket message containing the public key. -type PubKeyMsg struct { +// PubKeyRequest requests the consensus public key from the remote signer. +type PubKeyRequest struct{} + +// PubKeyResponse is a PrivValidatorSocket message containing the public key. +type PubKeyResponse struct { PubKey crypto.PubKey + Error *RemoteSignerError } // SignVoteRequest is a PrivValidatorSocket message containing a vote. @@ -227,10 +234,10 @@ func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValida var err error switch r := req.(type) { - case *PubKeyMsg: + case *PubKeyRequest: var p crypto.PubKey p = privVal.GetPubKey() - res = &PubKeyMsg{p} + res = &PubKeyResponse{p, nil} case *SignVoteRequest: err = privVal.SignVote(chainID, r.Vote) if err != nil { diff --git a/privval/tcp.go b/privval/tcp.go index 11bd833c..1fb736e6 100644 --- a/privval/tcp.go +++ b/privval/tcp.go @@ -107,8 +107,10 @@ func (sc *TCPVal) OnStart() error { } sc.conn = conn - - sc.RemoteSignerClient = NewRemoteSignerClient(sc.conn) + sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn) + if err != nil { + return err + } // Start a routine to keep the connection alive sc.cancelPing = make(chan struct{}, 1) diff --git a/privval/tcp_test.go b/privval/tcp_test.go index d2489ad1..e893ef40 100644 --- a/privval/tcp_test.go +++ b/privval/tcp_test.go @@ -25,15 +25,10 @@ func TestSocketPVAddress(t *testing.T) { defer sc.Stop() defer rs.Stop() - serverAddr := rs.privVal.GetAddress() - - clientAddr := sc.GetAddress() + serverAddr := rs.privVal.GetPubKey().Address() + clientAddr := sc.GetPubKey().Address() assert.Equal(t, serverAddr, clientAddr) - - // TODO(xla): Remove when PrivValidator2 replaced PrivValidator. - assert.Equal(t, serverAddr, sc.GetAddress()) - } func TestSocketPVPubKey(t *testing.T) { @@ -47,12 +42,9 @@ func TestSocketPVPubKey(t *testing.T) { clientKey, err := sc.getPubKey() require.NoError(t, err) - privKey := rs.privVal.GetPubKey() + privvalPubKey := rs.privVal.GetPubKey() - assert.Equal(t, privKey, clientKey) - - // TODO(xla): Remove when PrivValidator2 replaced PrivValidator. - assert.Equal(t, privKey, sc.GetPubKey()) + assert.Equal(t, privvalPubKey, clientKey) } func TestSocketPVProposal(t *testing.T) { @@ -153,9 +145,9 @@ func TestSocketPVDeadline(t *testing.T) { go func(sc *TCPVal) { defer close(listenc) - require.NoError(t, sc.Start()) + assert.Equal(t, sc.Start().(cmn.Error).Data(), ErrConnTimeout) - assert.True(t, sc.IsRunning()) + assert.False(t, sc.IsRunning()) }(sc) for { @@ -174,9 +166,6 @@ func TestSocketPVDeadline(t *testing.T) { } <-listenc - - _, err := sc.getPubKey() - assert.Equal(t, err.(cmn.Error).Data(), ErrConnTimeout) } func TestRemoteSignerRetry(t *testing.T) { @@ -310,14 +299,15 @@ func TestErrUnexpectedResponse(t *testing.T) { testStartSocketPV(t, readyc, sc) defer sc.Stop() RemoteSignerConnDeadline(time.Millisecond)(rs) - RemoteSignerConnRetries(1e6)(rs) - + RemoteSignerConnRetries(100)(rs) // we do not want to Start() the remote signer here and instead use the connection to // reply with intentionally wrong replies below: rsConn, err := rs.connect() defer rsConn.Close() require.NoError(t, err) require.NotNil(t, rsConn) + // send over public key to get the remote signer running: + go testReadWriteResponse(t, &PubKeyResponse{}, rsConn) <-readyc // Proposal: diff --git a/types/evidence_test.go b/types/evidence_test.go index a96b63a9..19427150 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -17,8 +17,9 @@ type voteData struct { } func makeVote(val PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID BlockID) *Vote { + addr := val.GetPubKey().Address() v := &Vote{ - ValidatorAddress: val.GetAddress(), + ValidatorAddress: addr, ValidatorIndex: valIndex, Height: height, Round: round, diff --git a/types/priv_validator.go b/types/priv_validator.go index ebd64446..f0a19f40 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -12,7 +12,6 @@ import ( // PrivValidator defines the functionality of a local Tendermint validator // that signs votes and proposals, and never double signs. type PrivValidator interface { - GetAddress() Address // redundant since .PubKey().Address() GetPubKey() crypto.PubKey SignVote(chainID string, vote *Vote) error @@ -29,7 +28,7 @@ func (pvs PrivValidatorsByAddress) Len() int { } func (pvs PrivValidatorsByAddress) Less(i, j int) bool { - return bytes.Compare(pvs[i].GetAddress(), pvs[j].GetAddress()) == -1 + return bytes.Compare(pvs[i].GetPubKey().Address(), pvs[j].GetPubKey().Address()) == -1 } func (pvs PrivValidatorsByAddress) Swap(i, j int) { @@ -51,11 +50,6 @@ func NewMockPV() *MockPV { return &MockPV{ed25519.GenPrivKey()} } -// Implements PrivValidator. -func (pv *MockPV) GetAddress() Address { - return pv.privKey.PubKey().Address() -} - // Implements PrivValidator. func (pv *MockPV) GetPubKey() crypto.PubKey { return pv.privKey.PubKey() @@ -85,7 +79,8 @@ func (pv *MockPV) SignProposal(chainID string, proposal *Proposal) error { // String returns a string representation of the MockPV. func (pv *MockPV) String() string { - return fmt.Sprintf("MockPV{%v}", pv.GetAddress()) + addr := pv.GetPubKey().Address() + return fmt.Sprintf("MockPV{%v}", addr) } // XXX: Implement. diff --git a/types/protobuf_test.go b/types/protobuf_test.go index f5a2ce5d..18acf57a 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -142,14 +142,15 @@ func TestABCIEvidence(t *testing.T) { blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) const chainID = "mychain" + pubKey := val.GetPubKey() ev := &DuplicateVoteEvidence{ - PubKey: val.GetPubKey(), + PubKey: pubKey, VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), } abciEv := TM2PB.Evidence( ev, - NewValidatorSet([]*Validator{NewValidator(val.GetPubKey(), 10)}), + NewValidatorSet([]*Validator{NewValidator(pubKey, 10)}), time.Now(), ) diff --git a/types/test_util.go b/types/test_util.go index 80f0c787..18e47214 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -10,9 +10,9 @@ func MakeCommit(blockID BlockID, height int64, round int, // all sign for i := 0; i < len(validators); i++ { - + addr := validators[i].GetPubKey().Address() vote := &Vote{ - ValidatorAddress: validators[i].GetAddress(), + ValidatorAddress: addr, ValidatorIndex: i, Height: height, Round: round, diff --git a/types/validator.go b/types/validator.go index b7c6c679..1de326b0 100644 --- a/types/validator.go +++ b/types/validator.go @@ -101,6 +101,7 @@ func RandValidator(randPower bool, minPower int64) (*Validator, PrivValidator) { if randPower { votePower += int64(cmn.RandUint32()) } - val := NewValidator(privVal.GetPubKey(), votePower) + pubKey := privVal.GetPubKey() + val := NewValidator(pubKey, votePower) return val, privVal } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 64187292..59205efc 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -66,7 +66,8 @@ func TestAddVote(t *testing.T) { // t.Logf(">> %v", voteSet) - if voteSet.GetByAddress(val0.GetAddress()) != nil { + val0Addr := val0.GetPubKey().Address() + if voteSet.GetByAddress(val0Addr) != nil { t.Errorf("Expected GetByAddress(val0.Address) to be nil") } if voteSet.BitArray().GetIndex(0) { @@ -78,7 +79,7 @@ func TestAddVote(t *testing.T) { } vote := &Vote{ - ValidatorAddress: val0.GetAddress(), + ValidatorAddress: val0Addr, ValidatorIndex: 0, // since privValidators are in order Height: height, Round: round, @@ -91,7 +92,7 @@ func TestAddVote(t *testing.T) { t.Error(err) } - if voteSet.GetByAddress(val0.GetAddress()) == nil { + if voteSet.GetByAddress(val0Addr) == nil { t.Errorf("Expected GetByAddress(val0.Address) to be present") } if !voteSet.BitArray().GetIndex(0) { @@ -118,7 +119,8 @@ func Test2_3Majority(t *testing.T) { } // 6 out of 10 voted for nil. for i := 0; i < 6; i++ { - vote := withValidator(voteProto, privValidators[i].GetAddress(), i) + addr := privValidators[i].GetPubKey().Address() + vote := withValidator(voteProto, addr, i) _, err := signAddVote(privValidators[i], vote, voteSet) if err != nil { t.Error(err) @@ -131,7 +133,8 @@ func Test2_3Majority(t *testing.T) { // 7th validator voted for some blockhash { - vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) + addr := privValidators[6].GetPubKey().Address() + vote := withValidator(voteProto, addr, 6) _, err := signAddVote(privValidators[6], withBlockHash(vote, cmn.RandBytes(32)), voteSet) if err != nil { t.Error(err) @@ -144,7 +147,8 @@ func Test2_3Majority(t *testing.T) { // 8th validator voted for nil. { - vote := withValidator(voteProto, privValidators[7].GetAddress(), 7) + addr := privValidators[7].GetPubKey().Address() + vote := withValidator(voteProto, addr, 7) _, err := signAddVote(privValidators[7], vote, voteSet) if err != nil { t.Error(err) @@ -176,7 +180,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 66 out of 100 voted for nil. for i := 0; i < 66; i++ { - vote := withValidator(voteProto, privValidators[i].GetAddress(), i) + addr := privValidators[i].GetPubKey().Address() + vote := withValidator(voteProto, addr, i) _, err := signAddVote(privValidators[i], vote, voteSet) if err != nil { t.Error(err) @@ -189,7 +194,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 67th validator voted for nil { - vote := withValidator(voteProto, privValidators[66].GetAddress(), 66) + adrr := privValidators[66].GetPubKey().Address() + vote := withValidator(voteProto, adrr, 66) _, err := signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet) if err != nil { t.Error(err) @@ -202,7 +208,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 68th validator voted for a different BlockParts PartSetHeader { - vote := withValidator(voteProto, privValidators[67].GetAddress(), 67) + addr := privValidators[67].GetPubKey().Address() + vote := withValidator(voteProto, addr, 67) blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)} _, err := signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet) if err != nil { @@ -216,7 +223,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 69th validator voted for different BlockParts Total { - vote := withValidator(voteProto, privValidators[68].GetAddress(), 68) + addr := privValidators[68].GetPubKey().Address() + vote := withValidator(voteProto, addr, 68) blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash} _, err := signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet) if err != nil { @@ -230,7 +238,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 70th validator voted for different BlockHash { - vote := withValidator(voteProto, privValidators[69].GetAddress(), 69) + addr := privValidators[69].GetPubKey().Address() + vote := withValidator(voteProto, addr, 69) _, err := signAddVote(privValidators[69], withBlockHash(vote, cmn.RandBytes(32)), voteSet) if err != nil { t.Error(err) @@ -243,7 +252,8 @@ func Test2_3MajorityRedux(t *testing.T) { // 71st validator voted for the right BlockHash & BlockPartsHeader { - vote := withValidator(voteProto, privValidators[70].GetAddress(), 70) + addr := privValidators[70].GetPubKey().Address() + vote := withValidator(voteProto, addr, 70) _, err := signAddVote(privValidators[70], vote, voteSet) if err != nil { t.Error(err) @@ -271,7 +281,8 @@ func TestBadVotes(t *testing.T) { // val0 votes for nil. { - vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) + addr := privValidators[0].GetPubKey().Address() + vote := withValidator(voteProto, addr, 0) added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -280,7 +291,8 @@ func TestBadVotes(t *testing.T) { // val0 votes again for some block. { - vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) + addr := privValidators[0].GetPubKey().Address() + vote := withValidator(voteProto, addr, 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, cmn.RandBytes(32)), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, conflicting vote.") @@ -289,7 +301,8 @@ func TestBadVotes(t *testing.T) { // val1 votes on another height { - vote := withValidator(voteProto, privValidators[1].GetAddress(), 1) + addr := privValidators[1].GetPubKey().Address() + vote := withValidator(voteProto, addr, 1) added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, wrong height") @@ -298,7 +311,8 @@ func TestBadVotes(t *testing.T) { // val2 votes on another round { - vote := withValidator(voteProto, privValidators[2].GetAddress(), 2) + addr := privValidators[2].GetPubKey().Address() + vote := withValidator(voteProto, addr, 2) added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, wrong round") @@ -307,7 +321,8 @@ func TestBadVotes(t *testing.T) { // val3 votes of another type. { - vote := withValidator(voteProto, privValidators[3].GetAddress(), 3) + addr := privValidators[3].GetPubKey().Address() + vote := withValidator(voteProto, addr, 3) added, err := signAddVote(privValidators[3], withType(vote, byte(PrecommitType)), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, wrong type") @@ -331,9 +346,10 @@ func TestConflicts(t *testing.T) { BlockID: BlockID{nil, PartSetHeader{}}, } + val0Addr := privValidators[0].GetPubKey().Address() // val0 votes for nil. { - vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) + vote := withValidator(voteProto, val0Addr, 0) added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -342,7 +358,7 @@ func TestConflicts(t *testing.T) { // val0 votes again for blockHash1. { - vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) + vote := withValidator(voteProto, val0Addr, 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet) if added { t.Errorf("Expected VoteSet.Add to fail, conflicting vote.") @@ -357,7 +373,7 @@ func TestConflicts(t *testing.T) { // val0 votes again for blockHash1. { - vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) + vote := withValidator(voteProto, val0Addr, 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet) if !added { t.Errorf("Expected VoteSet.Add to succeed, called SetPeerMaj23().") @@ -372,7 +388,7 @@ func TestConflicts(t *testing.T) { // val0 votes again for blockHash1. { - vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) + vote := withValidator(voteProto, val0Addr, 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash2), voteSet) if added { t.Errorf("Expected VoteSet.Add to fail, duplicate SetPeerMaj23() from peerA") @@ -384,7 +400,8 @@ func TestConflicts(t *testing.T) { // val1 votes for blockHash1. { - vote := withValidator(voteProto, privValidators[1].GetAddress(), 1) + addr := privValidators[1].GetPubKey().Address() + vote := withValidator(voteProto, addr, 1) added, err := signAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -401,7 +418,8 @@ func TestConflicts(t *testing.T) { // val2 votes for blockHash2. { - vote := withValidator(voteProto, privValidators[2].GetAddress(), 2) + addr := privValidators[2].GetPubKey().Address() + vote := withValidator(voteProto, addr, 2) added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet) if !added || err != nil { t.Errorf("Expected VoteSet.Add to succeed") @@ -421,7 +439,8 @@ func TestConflicts(t *testing.T) { // val2 votes for blockHash1. { - vote := withValidator(voteProto, privValidators[2].GetAddress(), 2) + addr := privValidators[2].GetPubKey().Address() + vote := withValidator(voteProto, addr, 2) added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet) if !added { t.Errorf("Expected VoteSet.Add to succeed") @@ -462,7 +481,8 @@ func TestMakeCommit(t *testing.T) { // 6 out of 10 voted for some block. for i := 0; i < 6; i++ { - vote := withValidator(voteProto, privValidators[i].GetAddress(), i) + addr := privValidators[i].GetPubKey().Address() + vote := withValidator(voteProto, addr, i) _, err := signAddVote(privValidators[i], vote, voteSet) if err != nil { t.Error(err) @@ -474,7 +494,8 @@ func TestMakeCommit(t *testing.T) { // 7th voted for some other block. { - vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) + addr := privValidators[6].GetPubKey().Address() + vote := withValidator(voteProto, addr, 6) vote = withBlockHash(vote, cmn.RandBytes(32)) vote = withBlockPartsHeader(vote, PartSetHeader{123, cmn.RandBytes(32)}) @@ -486,7 +507,8 @@ func TestMakeCommit(t *testing.T) { // The 8th voted like everyone else. { - vote := withValidator(voteProto, privValidators[7].GetAddress(), 7) + addr := privValidators[7].GetPubKey().Address() + vote := withValidator(voteProto, addr, 7) _, err := signAddVote(privValidators[7], vote, voteSet) if err != nil { t.Error(err) From 49017a57874649567e967e17aa9c31f1b7af00fd Mon Sep 17 00:00:00 2001 From: srmo Date: Tue, 1 Jan 2019 08:42:39 +0100 Subject: [PATCH 10/31] 3070 [docs] unindent text as it is supposed to behave the same as the parts before (#3075) --- docs/spec/abci/apps.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/spec/abci/apps.md b/docs/spec/abci/apps.md index acf2c4e6..a378a2a8 100644 --- a/docs/spec/abci/apps.md +++ b/docs/spec/abci/apps.md @@ -407,21 +407,22 @@ If `storeBlockHeight == stateBlockHeight && appBlockHeight < storeBlockHeight`, replay all blocks in full from `appBlockHeight` to `storeBlockHeight`. This happens if we completed processing the block, but the app forgot its height. -If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done +If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done. This happens if we crashed at an opportune spot. If `storeBlockHeight == stateBlockHeight+1` This happens if we started processing the block but didn't finish. - If `appBlockHeight < stateBlockHeight` - replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`, - and replay the block at `storeBlockHeight` using the WAL. - This happens if the app forgot the last block it committed. +If `appBlockHeight < stateBlockHeight` + replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`, + and replay the block at `storeBlockHeight` using the WAL. +This happens if the app forgot the last block it committed. - If `appBlockHeight == stateBlockHeight`, - replay the last block (storeBlockHeight) in full. - This happens if we crashed before the app finished Commit +If `appBlockHeight == stateBlockHeight`, + replay the last block (storeBlockHeight) in full. +This happens if we crashed before the app finished Commit + +If `appBlockHeight == storeBlockHeight` + update the state using the saved ABCI responses but dont run the block against the real app. +This happens if we crashed after the app finished Commit but before Tendermint saved the state. - If appBlockHeight == storeBlockHeight { - update the state using the saved ABCI responses but dont run the block against the real app. - This happens if we crashed after the app finished Commit but before Tendermint saved the state. From 56a4fb4d72b986992f72e53a9f15cfbfcf1b0629 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 2 Jan 2019 17:18:45 -0800 Subject: [PATCH 11/31] add signing spec (#3061) * add signing spec * fixes from review * more fixes * fixes from review --- docs/spec/blockchain/blockchain.md | 2 +- docs/spec/consensus/signing.md | 205 +++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 docs/spec/consensus/signing.md diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index d96a3c7b..cda32326 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -230,7 +230,7 @@ The block version must match the state version. len(block.ChainID) < 50 ``` -ChainID must be maximum 50 UTF-8 symbols. +ChainID must be less than 50 bytes. ### Height diff --git a/docs/spec/consensus/signing.md b/docs/spec/consensus/signing.md new file mode 100644 index 00000000..d1ee71a6 --- /dev/null +++ b/docs/spec/consensus/signing.md @@ -0,0 +1,205 @@ +# Validator Signing + +Here we specify the rules for validating a proposal and vote before signing. +First we include some general notes on validating data structures common to both types. +We then provide specific validation rules for each. Finally, we include validation rules to prevent double-sigining. + +## SignedMsgType + +The `SignedMsgType` is a single byte that refers to the type of the message +being signed. It is defined in Go as follows: + +``` +// SignedMsgType is a type of signed message in the consensus. +type SignedMsgType byte + +const ( + // Votes + PrevoteType SignedMsgType = 0x01 + PrecommitType SignedMsgType = 0x02 + + // Proposals + ProposalType SignedMsgType = 0x20 +) +``` + +All signed messages must correspond to one of these types. + +## Timestamp + +Timestamp validation is subtle and there are currently no bounds placed on the +timestamp included in a proposal or vote. It is expected that validators will honestly +report their local clock time. The median of all timestamps +included in a commit is used as the timestamp for the next block height. + +Timestamps are expected to be strictly monotonic for a given validator, though +this is not currently enforced. + +## ChainID + +ChainID is an unstructured string with a max length of 50-bytes. +In the future, the ChainID may become structured, and may take on longer lengths. +For now, it is recommended that signers be configured for a particular ChainID, +and to only sign votes and proposals corresponding to that ChainID. + +## BlockID + +BlockID is the structure used to represent the block: + +``` +type BlockID struct { + Hash []byte + PartsHeader PartSetHeader +} + +type PartSetHeader struct { + Hash []byte + Total int +} +``` + +To be included in a valid vote or proposal, BlockID must either represent a `nil` block, or a complete one. +We introduce two methods, `BlockID.IsNil()` and `BlockID.IsComplete()` for these cases, respectively. + +`BlockID.IsNil()` returns true for BlockID `b` if each of the following +are true: + +``` +b.Hash == nil +b.PartsHeader.Total == 0 +b.PartsHeader.Hash == nil +``` + +`BlockID.IsComplete()` returns true for BlockID `b` if each of the following +are true: + +``` +len(b.Hash) == 32 +b.PartsHeader.Total > 0 +len(b.PartsHeader.Hash) == 32 +``` + +## Proposals + +The structure of a propsal for signing looks like: + +``` +type CanonicalProposal struct { + Type SignedMsgType // type alias for byte + Height int64 `binary:"fixed64"` + Round int64 `binary:"fixed64"` + POLRound int64 `binary:"fixed64"` + BlockID BlockID + Timestamp time.Time + ChainID string +} +``` + +A proposal is valid if each of the following lines evaluates to true for proposal `p`: + +``` +p.Type == 0x20 +p.Height > 0 +p.Round >= 0 +p.POLRound >= -1 +p.BlockID.IsComplete() +``` + +In other words, a proposal is valid for signing if it contains the type of a Proposal +(0x20), has a positive, non-zero height, a +non-negative round, a POLRound not less than -1, and a complete BlockID. + +## Votes + +The structure of a vote for signing looks like: + +``` +type CanonicalVote struct { + Type SignedMsgType // type alias for byte + Height int64 `binary:"fixed64"` + Round int64 `binary:"fixed64"` + Timestamp time.Time + BlockID BlockID + ChainID string +} +``` + +A vote is valid if each of the following lines evaluates to true for vote `v`: + +``` +v.Type == 0x1 || v.Type == 0x2 +v.Height > 0 +v.Round >= 0 +v.BlockID.IsNil() || v.BlockID.IsValid() +``` + +In other words, a vote is valid for signing if it contains the type of a Prevote +or Precommit (0x1 or 0x2, respectively), has a positive, non-zero height, a +non-negative round, and an empty or valid BlockID. + +## Invalid Votes and Proposals + +Votes and proposals which do not satisfy the above rules are considered invalid. +Peers gossipping invalid votes and proposals may be disconnected from other peers on the network. +Note, however, that there is not currently any explicit mechanism to punish validators signing votes or proposals that fail +these basic validation rules. + +## Double Signing + +Signers must be careful not to sign conflicting messages, also known as "double signing" or "equivocating". +Tendermint has mechanisms to publish evidence of validators that signed conflicting votes, so they can be punished +by the application. Note Tendermint does not currently handle evidence of conflciting proposals, though it may in the future. + +### State + +To prevent such double signing, signers must track the height, round, and type of the last message signed. +Assume the signer keeps the following state, `s`: + +``` +type LastSigned struct { + Height int64 + Round int64 + Type SignedMsgType // byte +} +``` + +After signing a vote or proposal `m`, the signer sets: + +``` +s.Height = m.Height +s.Round = m.Round +s.Type = m.Type +``` + +### Proposals + +A signer should only sign a proposal `p` if any of the following lines are true: + +``` +p.Height > s.Height +p.Height == s.Height && p.Round > s.Round +``` + +In other words, a proposal should only be signed if it's at a higher height, or a higher round for the same height. +Once a proposal or vote has been signed for a given height and round, a proposal should never be signed for the same height and round. + +### Votes + +A signer should only sign a vote `v` if any of the following lines are true: + +``` +v.Height > s.Height +v.Height == s.Height && v.Round > s.Round +v.Height == s.Height && v.Round == s.Round && v.Step == 0x1 && s.Step == 0x20 +v.Height == s.Height && v.Round == s.Round && v.Step == 0x2 && s.Step != 0x2 +``` + +In other words, a vote should only be signed if it's: + +- at a higher height +- at a higher round for the same height +- a prevote for the same height and round where we haven't signed a prevote or precommit (but have signed a proposal) +- a precommit for the same height and round where we haven't signed a precommit (but have signed a proposal and/or a prevote) + +This means that once a validator signs a prevote for a given height and round, the only other message it can sign for that height and round is a precommit. +And once a validator signs a precommit for a given height and round, it must not sign any other message for that same height and round. From 04e97f599aeafa939b55a9c81a7714335bd0596b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Husiaty=C5=84ski?= Date: Sun, 6 Jan 2019 09:40:15 +0100 Subject: [PATCH 12/31] fix build scripts (#3085) * fix build scripts Search for the right variable when introspecting Go code. `Version` was renamed to `TMCoreSemVer`. This is regression introduced in b95ac688af14d130e6ad0b580ed9a8181f6c487c * fix all `Version` introspections. Use `TMCoreSemVer` instead of `Version` --- DOCKER/build.sh | 2 +- DOCKER/push.sh | 2 +- scripts/dist.sh | 2 +- scripts/publish.sh | 2 +- scripts/release.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DOCKER/build.sh b/DOCKER/build.sh index ee617cc6..2aa42a6c 100755 --- a/DOCKER/build.sh +++ b/DOCKER/build.sh @@ -3,7 +3,7 @@ set -e # Get the tag from the version, or try to figure it out. if [ -z "$TAG" ]; then - TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go) + TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go) fi if [ -z "$TAG" ]; then echo "Please specify a tag." diff --git a/DOCKER/push.sh b/DOCKER/push.sh index 32741dce..f228406d 100755 --- a/DOCKER/push.sh +++ b/DOCKER/push.sh @@ -3,7 +3,7 @@ set -e # Get the tag from the version, or try to figure it out. if [ -z "$TAG" ]; then - TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go) + TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go) fi if [ -z "$TAG" ]; then echo "Please specify a tag." diff --git a/scripts/dist.sh b/scripts/dist.sh index 40aa71e9..f999c537 100755 --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -6,7 +6,7 @@ set -e # Get the version from the environment, or try to figure it out. if [ -z $VERSION ]; then - VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) + VERSION=$(awk -F\" 'TMCoreSemVer =/ { print $2; exit }' < version/version.go) fi if [ -z "$VERSION" ]; then echo "Please specify a version." diff --git a/scripts/publish.sh b/scripts/publish.sh index ba944087..7da299aa 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -6,7 +6,7 @@ DIST_DIR=./build/dist # Get the version from the environment, or try to figure it out. if [ -z $VERSION ]; then - VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) + VERSION=$(awk -F\" 'TMCoreSemVer =/ { print $2; exit }' < version/version.go) fi if [ -z "$VERSION" ]; then echo "Please specify a version." diff --git a/scripts/release.sh b/scripts/release.sh index 9a4e508e..8c40d36b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -3,7 +3,7 @@ set -e # Get the version from the environment, or try to figure it out. if [ -z $VERSION ]; then - VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) + VERSION=$(awk -F\" 'TMCoreSemVer =/ { print $2; exit }' < version/version.go) fi if [ -z "$VERSION" ]; then echo "Please specify a version." From 616c3a4baeb1f52589f6afe09bc2be256b9626d2 Mon Sep 17 00:00:00 2001 From: srmo Date: Sun, 6 Jan 2019 10:00:12 +0100 Subject: [PATCH 13/31] cs: prettify logging of ignored votes (#3086) Refs #3038 --- consensus/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/state.go b/consensus/state.go index 1693e36b..c6f73d35 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1529,7 +1529,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, // Not necessarily a bad peer, but not favourable behaviour. if vote.Height != cs.Height { err = ErrVoteHeightMismatch - cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err) + cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID) return } From 764cfe33aa55acb46316b3e8fdfa72c7bd49a6e4 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 10 Jan 2019 22:47:20 +0000 Subject: [PATCH 14/31] Don't use pointer receivers for PubKeyMultisigThreshold (#3100) * Don't use pointer receivers for PubKeyMultisigThreshold * test that showcases panic when PubKeyMultisigThreshold are used in sdk: - deserialization will fail in `readInfo` which tries to read a `crypto.PubKey` into a `localInfo` (called by cosmos-sdk/client/keys.GetKeyInfo) * Update changelog * Rename routeTable to nameTable, multisig key is no longer a pointer * sed -i 's/PubKeyAminoRoute/PubKeyAminoName/g' `grep -lrw PubKeyAminoRoute .` upon Jae's request * AminoRoutes -> AminoNames * sed -e 's/PrivKeyAminoRoute/PrivKeyAminoName/g' * Update crypto/encoding/amino/amino.go Co-Authored-By: alessio --- CHANGELOG_PENDING.md | 1 + crypto/ed25519/ed25519.go | 8 +++---- crypto/encoding/amino/amino.go | 28 ++++++++++++------------ crypto/encoding/amino/encode_test.go | 10 ++++----- crypto/multisig/threshold_pubkey.go | 12 +++++----- crypto/multisig/threshold_pubkey_test.go | 16 ++++++++++++++ crypto/multisig/wire.go | 4 ++-- crypto/secp256k1/secp256k1.go | 8 +++---- types/params.go | 4 ++-- types/protobuf.go | 6 ++--- 10 files changed, 57 insertions(+), 40 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index aaf80a4f..2cedb02f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -32,3 +32,4 @@ Special thanks to external contributors on this release: ### BUG FIXES: - [types] \#2926 do not panic if retrieving the private validator's public key fails +- [crypto/encoding] \#3101 Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index 0c659e73..bc60838d 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -18,8 +18,8 @@ import ( var _ crypto.PrivKey = PrivKeyEd25519{} const ( - PrivKeyAminoRoute = "tendermint/PrivKeyEd25519" - PubKeyAminoRoute = "tendermint/PubKeyEd25519" + PrivKeyAminoName = "tendermint/PrivKeyEd25519" + PubKeyAminoName = "tendermint/PubKeyEd25519" // Size of an Edwards25519 signature. Namely the size of a compressed // Edwards25519 point, and a field element. Both of which are 32 bytes. SignatureSize = 64 @@ -30,11 +30,11 @@ var cdc = amino.NewCodec() func init() { cdc.RegisterInterface((*crypto.PubKey)(nil), nil) cdc.RegisterConcrete(PubKeyEd25519{}, - PubKeyAminoRoute, nil) + PubKeyAminoName, nil) cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) cdc.RegisterConcrete(PrivKeyEd25519{}, - PrivKeyAminoRoute, nil) + PrivKeyAminoName, nil) } // PrivKeyEd25519 implements crypto.PrivKey. diff --git a/crypto/encoding/amino/amino.go b/crypto/encoding/amino/amino.go index d66ecd9b..f7be3a20 100644 --- a/crypto/encoding/amino/amino.go +++ b/crypto/encoding/amino/amino.go @@ -12,11 +12,11 @@ import ( var cdc = amino.NewCodec() -// routeTable is used to map public key concrete types back -// to their amino routes. This should eventually be handled +// nameTable is used to map public key concrete types back +// to their registered amino names. This should eventually be handled // by amino. Example usage: -// routeTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoRoute -var routeTable = make(map[reflect.Type]string, 3) +// nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName +var nameTable = make(map[reflect.Type]string, 3) func init() { // NOTE: It's important that there be no conflicts here, @@ -29,16 +29,16 @@ func init() { // TODO: Have amino provide a way to go from concrete struct to route directly. // Its currently a private API - routeTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoRoute - routeTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoRoute - routeTable[reflect.TypeOf(&multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute + nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName + nameTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoName + nameTable[reflect.TypeOf(multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute } -// PubkeyAminoRoute returns the amino route of a pubkey +// PubkeyAminoName returns the amino route of a pubkey // cdc is currently passed in, as eventually this will not be using // a package level codec. -func PubkeyAminoRoute(cdc *amino.Codec, key crypto.PubKey) (string, bool) { - route, found := routeTable[reflect.TypeOf(key)] +func PubkeyAminoName(cdc *amino.Codec, key crypto.PubKey) (string, bool) { + route, found := nameTable[reflect.TypeOf(key)] return route, found } @@ -47,17 +47,17 @@ func RegisterAmino(cdc *amino.Codec) { // These are all written here instead of cdc.RegisterInterface((*crypto.PubKey)(nil), nil) cdc.RegisterConcrete(ed25519.PubKeyEd25519{}, - ed25519.PubKeyAminoRoute, nil) + ed25519.PubKeyAminoName, nil) cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{}, - secp256k1.PubKeyAminoRoute, nil) + secp256k1.PubKeyAminoName, nil) cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, nil) cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) cdc.RegisterConcrete(ed25519.PrivKeyEd25519{}, - ed25519.PrivKeyAminoRoute, nil) + ed25519.PrivKeyAminoName, nil) cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{}, - secp256k1.PrivKeyAminoRoute, nil) + secp256k1.PrivKeyAminoName, nil) } func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) { diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index 056dbec4..c8cb2413 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -128,18 +128,18 @@ func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) { require.Nil(t, pk) } -func TestPubkeyAminoRoute(t *testing.T) { +func TestPubkeyAminoName(t *testing.T) { tests := []struct { key crypto.PubKey want string found bool }{ - {ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoRoute, true}, - {secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoRoute, true}, - {&multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true}, + {ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoName, true}, + {secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoName, true}, + {multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true}, } for i, tc := range tests { - got, found := PubkeyAminoRoute(cdc, tc.key) + got, found := PubkeyAminoName(cdc, tc.key) require.Equal(t, tc.found, found, "not equal on tc %d", i) if tc.found { require.Equal(t, tc.want, got, "not equal on tc %d", i) diff --git a/crypto/multisig/threshold_pubkey.go b/crypto/multisig/threshold_pubkey.go index ca8d4230..41abc1e5 100644 --- a/crypto/multisig/threshold_pubkey.go +++ b/crypto/multisig/threshold_pubkey.go @@ -11,7 +11,7 @@ type PubKeyMultisigThreshold struct { PubKeys []crypto.PubKey `json:"pubkeys"` } -var _ crypto.PubKey = &PubKeyMultisigThreshold{} +var _ crypto.PubKey = PubKeyMultisigThreshold{} // NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold. // Panics if len(pubkeys) < k or 0 >= k. @@ -22,7 +22,7 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey { if len(pubkeys) < k { panic("threshold k of n multisignature: len(pubkeys) < k") } - return &PubKeyMultisigThreshold{uint(k), pubkeys} + return PubKeyMultisigThreshold{uint(k), pubkeys} } // VerifyBytes expects sig to be an amino encoded version of a MultiSignature. @@ -31,7 +31,7 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey { // and all signatures are valid. (Not just k of the signatures) // The multisig uses a bitarray, so multiple signatures for the same key is not // a concern. -func (pk *PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool { +func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool { var sig *Multisignature err := cdc.UnmarshalBinaryBare(marshalledSig, &sig) if err != nil { @@ -64,18 +64,18 @@ func (pk *PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) } // Bytes returns the amino encoded version of the PubKeyMultisigThreshold -func (pk *PubKeyMultisigThreshold) Bytes() []byte { +func (pk PubKeyMultisigThreshold) Bytes() []byte { return cdc.MustMarshalBinaryBare(pk) } // Address returns tmhash(PubKeyMultisigThreshold.Bytes()) -func (pk *PubKeyMultisigThreshold) Address() crypto.Address { +func (pk PubKeyMultisigThreshold) Address() crypto.Address { return crypto.Address(tmhash.Sum(pk.Bytes())) } // Equals returns true iff pk and other both have the same number of keys, and // all constituent keys are the same, and in the same order. -func (pk *PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool { +func (pk PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool { otherKey, sameType := other.(*PubKeyMultisigThreshold) if !sameType { return false diff --git a/crypto/multisig/threshold_pubkey_test.go b/crypto/multisig/threshold_pubkey_test.go index bfc874eb..d442e649 100644 --- a/crypto/multisig/threshold_pubkey_test.go +++ b/crypto/multisig/threshold_pubkey_test.go @@ -95,6 +95,22 @@ func TestMultiSigPubKeyEquality(t *testing.T) { require.False(t, multisigKey.Equals(multisigKey2)) } +func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) { + msg := []byte{1, 2, 3, 4} + pubkeys, _ := generatePubKeysAndSignatures(5, msg) + multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) + + ab, err := cdc.MarshalBinaryLengthPrefixed(multisigKey) + require.NoError(t, err) + // like other crypto.Pubkey implementations (e.g. ed25519.PubKeyEd25519), + // PubKeyMultisigThreshold should be deserializable into a crypto.PubKey: + var pubKey crypto.PubKey + err = cdc.UnmarshalBinaryLengthPrefixed(ab, &pubKey) + require.NoError(t, err) + + require.Equal(t, multisigKey, pubKey) +} + func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) { pubkeys = make([]crypto.PubKey, n) signatures = make([][]byte, n) diff --git a/crypto/multisig/wire.go b/crypto/multisig/wire.go index 68b84fbf..71e0db14 100644 --- a/crypto/multisig/wire.go +++ b/crypto/multisig/wire.go @@ -20,7 +20,7 @@ func init() { cdc.RegisterConcrete(PubKeyMultisigThreshold{}, PubKeyMultisigThresholdAminoRoute, nil) cdc.RegisterConcrete(ed25519.PubKeyEd25519{}, - ed25519.PubKeyAminoRoute, nil) + ed25519.PubKeyAminoName, nil) cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{}, - secp256k1.PubKeyAminoRoute, nil) + secp256k1.PubKeyAminoName, nil) } diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index 7fc46d63..d3528fdd 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -16,8 +16,8 @@ import ( //------------------------------------- const ( - PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1" - PubKeyAminoRoute = "tendermint/PubKeySecp256k1" + PrivKeyAminoName = "tendermint/PrivKeySecp256k1" + PubKeyAminoName = "tendermint/PubKeySecp256k1" ) var cdc = amino.NewCodec() @@ -25,11 +25,11 @@ var cdc = amino.NewCodec() func init() { cdc.RegisterInterface((*crypto.PubKey)(nil), nil) cdc.RegisterConcrete(PubKeySecp256k1{}, - PubKeyAminoRoute, nil) + PubKeyAminoName, nil) cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) cdc.RegisterConcrete(PrivKeySecp256k1{}, - PrivKeyAminoRoute, nil) + PrivKeyAminoName, nil) } //------------------------------------- diff --git a/types/params.go b/types/params.go index ec8a8f57..91079e76 100644 --- a/types/params.go +++ b/types/params.go @@ -34,7 +34,7 @@ type EvidenceParams struct { } // ValidatorParams restrict the public key types validators can use. -// NOTE: uses ABCI pubkey naming, not Amino routes. +// NOTE: uses ABCI pubkey naming, not Amino names. type ValidatorParams struct { PubKeyTypes []string `json:"pub_key_types"` } @@ -107,7 +107,7 @@ func (params *ConsensusParams) Validate() error { // Check if keyType is a known ABCIPubKeyType for i := 0; i < len(params.Validator.PubKeyTypes); i++ { keyType := params.Validator.PubKeyTypes[i] - if _, ok := ABCIPubKeyTypesToAminoRoutes[keyType]; !ok { + if _, ok := ABCIPubKeyTypesToAminoNames[keyType]; !ok { return cmn.NewError("params.Validator.PubKeyTypes[%d], %s, is an unknown pubkey type", i, keyType) } diff --git a/types/protobuf.go b/types/protobuf.go index 0f0d25de..eed73b56 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -25,9 +25,9 @@ const ( ) // TODO: Make non-global by allowing for registration of more pubkey types -var ABCIPubKeyTypesToAminoRoutes = map[string]string{ - ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoRoute, - ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyAminoRoute, +var ABCIPubKeyTypesToAminoNames = map[string]string{ + ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoName, + ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyAminoName, } //------------------------------------------------------- From 7644d273077a3e33fb8b46950bbf2367931aec06 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 10 Jan 2019 23:37:34 +0000 Subject: [PATCH 15/31] Ensure multisig keys have 20-byte address (#3103) * Ensure multisig keys have 20-byte address Use crypto.AddressHash() to avoid returning 32-byte long address. Closes: #3102 * fix pointer * fix test --- CHANGELOG_PENDING.md | 1 + crypto/multisig/threshold_pubkey.go | 7 +++---- crypto/multisig/threshold_pubkey_test.go | 9 ++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2cedb02f..6614f792 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -32,4 +32,5 @@ Special thanks to external contributors on this release: ### BUG FIXES: - [types] \#2926 do not panic if retrieving the private validator's public key fails +- [crypto/multisig] \#3102 fix multisig keys address length - [crypto/encoding] \#3101 Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface diff --git a/crypto/multisig/threshold_pubkey.go b/crypto/multisig/threshold_pubkey.go index 41abc1e5..234d420f 100644 --- a/crypto/multisig/threshold_pubkey.go +++ b/crypto/multisig/threshold_pubkey.go @@ -2,7 +2,6 @@ package multisig import ( "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/tmhash" ) // PubKeyMultisigThreshold implements a K of N threshold multisig. @@ -32,7 +31,7 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey { // The multisig uses a bitarray, so multiple signatures for the same key is not // a concern. func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool { - var sig *Multisignature + var sig Multisignature err := cdc.UnmarshalBinaryBare(marshalledSig, &sig) if err != nil { return false @@ -70,13 +69,13 @@ func (pk PubKeyMultisigThreshold) Bytes() []byte { // Address returns tmhash(PubKeyMultisigThreshold.Bytes()) func (pk PubKeyMultisigThreshold) Address() crypto.Address { - return crypto.Address(tmhash.Sum(pk.Bytes())) + return crypto.AddressHash(pk.Bytes()) } // Equals returns true iff pk and other both have the same number of keys, and // all constituent keys are the same, and in the same order. func (pk PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool { - otherKey, sameType := other.(*PubKeyMultisigThreshold) + otherKey, sameType := other.(PubKeyMultisigThreshold) if !sameType { return false } diff --git a/crypto/multisig/threshold_pubkey_test.go b/crypto/multisig/threshold_pubkey_test.go index d442e649..2d2632ab 100644 --- a/crypto/multisig/threshold_pubkey_test.go +++ b/crypto/multisig/threshold_pubkey_test.go @@ -82,7 +82,7 @@ func TestMultiSigPubKeyEquality(t *testing.T) { msg := []byte{1, 2, 3, 4} pubkeys, _ := generatePubKeysAndSignatures(5, msg) multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) - var unmarshalledMultisig *PubKeyMultisigThreshold + var unmarshalledMultisig PubKeyMultisigThreshold cdc.MustUnmarshalBinaryBare(multisigKey.Bytes(), &unmarshalledMultisig) require.True(t, multisigKey.Equals(unmarshalledMultisig)) @@ -95,6 +95,13 @@ func TestMultiSigPubKeyEquality(t *testing.T) { require.False(t, multisigKey.Equals(multisigKey2)) } +func TestAddress(t *testing.T) { + msg := []byte{1, 2, 3, 4} + pubkeys, _ := generatePubKeysAndSignatures(5, msg) + multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) + require.Len(t, multisigKey.Address().Bytes(), 20) +} + func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) { msg := []byte{1, 2, 3, 4} pubkeys, _ := generatePubKeysAndSignatures(5, msg) From 51094f9417c83bd7a5f314cface6134250b2bcaa Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 11 Jan 2019 08:28:29 -0500 Subject: [PATCH 16/31] update README (#3097) * update README * fix from review --- README.md | 93 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 6e5c9e9a..601e3830 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Tendermint [Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance) -[State Machine Replication](https://en.wikipedia.org/wiki/State_machine_replication). -Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)) for short. +[State Machines](https://en.wikipedia.org/wiki/State_machine_replication). +Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)), for short. [![version](https://img.shields.io/github/tag/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/releases/latest) [![API Reference]( @@ -66,49 +66,26 @@ See the [install instructions](/docs/introduction/install.md) - [Remote cluster using terraform and ansible](/docs/networks/terraform-and-ansible.md) - [Join the Cosmos testnet](https://cosmos.network/testnet) -## Resources - -### Tendermint Core - -For details about the blockchain data structures and the p2p protocols, see the -the [Tendermint specification](/docs/spec). - -For details on using the software, see the [documentation](/docs/) which is also -hosted at: https://tendermint.com/docs/ - -### Tools - -Benchmarking and monitoring is provided by `tm-bench` and `tm-monitor`, respectively. -Their code is found [here](/tools) and these binaries need to be built seperately. -Additional documentation is found [here](/docs/tools). - -### Sub-projects - -* [Amino](http://github.com/tendermint/go-amino), a reflection-based improvement on proto3 -* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation - -### Applications - -* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework -* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint -* [Many more](https://tendermint.com/ecosystem) - -### Research - -* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938) -* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769) -* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf) -* [Blog](https://blog.cosmos.network/tendermint/home) - ## Contributing -Yay open source! Please see our [contributing guidelines](CONTRIBUTING.md). +Please abide by the [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions, +and the [contributing guidelines](CONTRIBUTING.md) when submitting code. + +Join the larger community on the [forum](https://forum.cosmos.network/) and the [chat](https://riot.im/app/#/room/#tendermint:matrix.org). + +To learn more about the structure of the software, watch the [Developer +Sessions](https://www.youtube.com/playlist?list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv) +and read some [Architectural +Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture). + +Learn more by reading the code and comparing it to the +[specification](https://github.com/tendermint/tendermint/tree/develop/docs/spec). ## Versioning -### SemVer +### Semantic Versioning -Tendermint uses [SemVer](http://semver.org/) to determine when and how the version changes. +Tendermint uses [Semantic Versioning](http://semver.org/) to determine when and how the version changes. According to SemVer, anything in the public API can change at any time before version 1.0.0 To provide some stability to Tendermint users in these 0.X.X days, the MINOR version is used @@ -145,8 +122,40 @@ data into the new chain. However, any bump in the PATCH version should be compatible with existing histories (if not please open an [issue](https://github.com/tendermint/tendermint/issues)). -For more information on upgrading, see [here](./UPGRADING.md) +For more information on upgrading, see [UPGRADING.md](./UPGRADING.md) -## Code of Conduct +## Resources + +### Tendermint Core + +For details about the blockchain data structures and the p2p protocols, see the +[Tendermint specification](/docs/spec). + +For details on using the software, see the [documentation](/docs/) which is also +hosted at: https://tendermint.com/docs/ + +### Tools + +Benchmarking and monitoring is provided by `tm-bench` and `tm-monitor`, respectively. +Their code is found [here](/tools) and these binaries need to be built seperately. +Additional documentation is found [here](/docs/tools). + +### Sub-projects + +* [Amino](http://github.com/tendermint/go-amino), reflection-based proto3, with + interfaces +* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation + +### Applications + +* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework +* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint +* [Many more](https://tendermint.com/ecosystem) + +### Research + +* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938) +* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769) +* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf) +* [Blog](https://blog.cosmos.network/tendermint/home) -Please read, understand and adhere to our [code of conduct](CODE_OF_CONDUCT.md). From 81c51cd4fcc8e826ba4dd4e6d4da696ba5c3fe37 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 11 Jan 2019 17:24:45 +0300 Subject: [PATCH 17/31] rpc: include peer's remote IP in `/net_info` (#3052) Refs #3047 --- CHANGELOG_PENDING.md | 1 + rpc/core/net.go | 1 + rpc/core/types/responses.go | 2 ++ 3 files changed, 4 insertions(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 6614f792..6bfdf845 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -29,6 +29,7 @@ Special thanks to external contributors on this release: - [privval] \#1181 Split immutable and mutable parts of priv_validator.json ### IMPROVEMENTS: +- [rpc] \#3047 Include peer's remote IP in `/net_info` ### BUG FIXES: - [types] \#2926 do not panic if retrieving the private validator's public key fails diff --git a/rpc/core/net.go b/rpc/core/net.go index b80902da..4d95c2ef 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -53,6 +53,7 @@ func NetInfo() (*ctypes.ResultNetInfo, error) { NodeInfo: nodeInfo, IsOutbound: peer.IsOutbound(), ConnectionStatus: peer.Status(), + RemoteIP: peer.RemoteIP(), }) } // TODO: Should we include PersistentPeers and Seeds in here? diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index af5c4947..62be1caf 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -2,6 +2,7 @@ package core_types import ( "encoding/json" + "net" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -110,6 +111,7 @@ type Peer struct { NodeInfo p2p.DefaultNodeInfo `json:"node_info"` IsOutbound bool `json:"is_outbound"` ConnectionStatus p2p.ConnectionStatus `json:"connection_status"` + RemoteIP net.IP `json:"remote_ip"` } // Validators for a height From 7f607d0ce23d5c578325b2d2a6bfe2273191cf9b Mon Sep 17 00:00:00 2001 From: Mauricio Serna Date: Fri, 11 Jan 2019 17:41:02 -0500 Subject: [PATCH 18/31] docs: fix p2p readme links (#3109) --- p2p/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/p2p/README.md b/p2p/README.md index 819a5056..df4c2ae0 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 From ef94a322b8726974f7483deae88cccb5f1c1fe0e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 13 Jan 2019 13:46:25 -0500 Subject: [PATCH 19/31] Make SecretConnection thread safe (#3111) * p2p/conn: add failing tests * p2p/conn: make SecretConnection thread safe * changelog * fix from review --- CHANGELOG_PENDING.md | 9 +++-- p2p/conn/secret_connection.go | 65 +++++++++++++++++++++--------- p2p/conn/secret_connection_test.go | 65 ++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 22 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 6bfdf845..7fc9bead 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.27.4 +## v0.28.0 *TBD* @@ -15,9 +15,9 @@ Special thanks to external contributors on this release: * Apps -* Go API -- [types] \#2926 memoize consensus public key on initialization of remote signer and return the memoized key on -`PrivValidator.GetPubKey()` instead of requesting it again +* Go API +- [types] \#2926 memoize consensus public key on initialization of remote signer and return the memoized key on +`PrivValidator.GetPubKey()` instead of requesting it again - [types] \#2981 Remove `PrivValidator.GetAddress()` * Blockchain Protocol @@ -29,6 +29,7 @@ Special thanks to external contributors on this release: - [privval] \#1181 Split immutable and mutable parts of priv_validator.json ### IMPROVEMENTS: +- [p2p/conn] \#3111 make SecretConnection thread safe - [rpc] \#3047 Include peer's remote IP in `/net_info` ### BUG FIXES: diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index d1b6bce6..aa019aa3 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -8,6 +8,7 @@ import ( "errors" "io" "net" + "sync" "time" "golang.org/x/crypto/chacha20poly1305" @@ -27,20 +28,36 @@ const aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag const aeadKeySize = chacha20poly1305.KeySize const aeadNonceSize = chacha20poly1305.NonceSize -// SecretConnection implements net.conn. +// SecretConnection implements net.Conn. // It is an implementation of the STS protocol. -// Note we do not (yet) assume that a remote peer's pubkey -// is known ahead of time, and thus we are technically -// still vulnerable to MITM. (TODO!) -// See docs/sts-final.pdf for more info +// See https://github.com/tendermint/tendermint/blob/0.1/docs/sts-final.pdf for +// details on the protocol. +// +// Consumers of the SecretConnection are responsible for authenticating +// the remote peer's pubkey against known information, like a nodeID. +// Otherwise they are vulnerable to MITM. +// (TODO(ismail): see also https://github.com/tendermint/tendermint/issues/3010) type SecretConnection struct { - conn io.ReadWriteCloser - recvBuffer []byte - recvNonce *[aeadNonceSize]byte - sendNonce *[aeadNonceSize]byte + + // immutable recvSecret *[aeadKeySize]byte sendSecret *[aeadKeySize]byte remPubKey crypto.PubKey + conn io.ReadWriteCloser + + // net.Conn must be thread safe: + // https://golang.org/pkg/net/#Conn. + // Since we have internal mutable state, + // we need mtxs. But recv and send states + // are independent, so we can use two mtxs. + // All .Read are covered by recvMtx, + // all .Write are covered by sendMtx. + recvMtx sync.Mutex + recvBuffer []byte + recvNonce *[aeadNonceSize]byte + + sendMtx sync.Mutex + sendNonce *[aeadNonceSize]byte } // MakeSecretConnection performs handshake and returns a new authenticated @@ -109,9 +126,12 @@ func (sc *SecretConnection) RemotePubKey() crypto.PubKey { return sc.remPubKey } -// Writes encrypted frames of `sealedFrameSize` -// CONTRACT: data smaller than dataMaxSize is read atomically. +// Writes encrypted frames of `totalFrameSize + aeadSizeOverhead`. +// CONTRACT: data smaller than dataMaxSize is written atomically. func (sc *SecretConnection) Write(data []byte) (n int, err error) { + sc.sendMtx.Lock() + defer sc.sendMtx.Unlock() + for 0 < len(data) { var frame = make([]byte, totalFrameSize) var chunk []byte @@ -130,6 +150,7 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) { 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) @@ -147,23 +168,30 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) { // CONTRACT: data smaller than dataMaxSize is read atomically. func (sc *SecretConnection) Read(data []byte) (n int, err error) { + sc.recvMtx.Lock() + defer sc.recvMtx.Unlock() + + // read off and update the recvBuffer, if non-empty if 0 < len(sc.recvBuffer) { n = copy(data, sc.recvBuffer) sc.recvBuffer = sc.recvBuffer[n:] return } + // read off the conn + sealedFrame := make([]byte, totalFrameSize+aeadSizeOverhead) + _, err = io.ReadFull(sc.conn, sealedFrame) + if err != nil { + return + } + aead, err := chacha20poly1305.New(sc.recvSecret[:]) if err != nil { return n, errors.New("Invalid SecretConnection Key") } - sealedFrame := make([]byte, totalFrameSize+aeadSizeOverhead) - _, err = io.ReadFull(sc.conn, sealedFrame) - if err != nil { - return - } - // decrypt the frame + // decrypt the frame. + // reads and updates the sc.recvNonce var frame = make([]byte, totalFrameSize) _, err = aead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil) if err != nil { @@ -172,12 +200,13 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) { incrNonce(sc.recvNonce) // end decryption + // copy checkLength worth into data, + // set recvBuffer to the rest. var chunkLength = binary.LittleEndian.Uint32(frame) // read the first four bytes if chunkLength > dataMaxSize { return 0, errors.New("chunkLength is greater than dataMaxSize") } var chunk = frame[dataLenSize : dataLenSize+chunkLength] - n = copy(data, chunk) sc.recvBuffer = chunk[n:] return diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 75ed8fe0..131ab922 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -7,10 +7,12 @@ import ( "fmt" "io" "log" + "net" "os" "path/filepath" "strconv" "strings" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -98,6 +100,69 @@ func TestSecretConnectionHandshake(t *testing.T) { } } +func TestConcurrentWrite(t *testing.T) { + fooSecConn, barSecConn := makeSecretConnPair(t) + fooWriteText := cmn.RandStr(dataMaxSize) + + // write from two routines. + // should be safe from race according to net.Conn: + // https://golang.org/pkg/net/#Conn + n := 100 + wg := new(sync.WaitGroup) + wg.Add(3) + go writeLots(t, wg, fooSecConn, fooWriteText, n) + go writeLots(t, wg, fooSecConn, fooWriteText, n) + + // Consume reads from bar's reader + readLots(t, wg, barSecConn, n*2) + wg.Wait() + + if err := fooSecConn.Close(); err != nil { + t.Error(err) + } +} + +func TestConcurrentRead(t *testing.T) { + fooSecConn, barSecConn := makeSecretConnPair(t) + fooWriteText := cmn.RandStr(dataMaxSize) + n := 100 + + // read from two routines. + // should be safe from race according to net.Conn: + // https://golang.org/pkg/net/#Conn + wg := new(sync.WaitGroup) + wg.Add(3) + go readLots(t, wg, fooSecConn, n/2) + go readLots(t, wg, fooSecConn, n/2) + + // write to bar + writeLots(t, wg, barSecConn, fooWriteText, n) + wg.Wait() + + if err := fooSecConn.Close(); err != nil { + t.Error(err) + } +} + +func writeLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, txt string, n int) { + defer wg.Done() + for i := 0; i < n; i++ { + _, err := conn.Write([]byte(txt)) + if err != nil { + t.Fatalf("Failed to write to fooSecConn: %v", err) + } + } +} + +func readLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, n int) { + readBuffer := make([]byte, dataMaxSize) + for i := 0; i < n; i++ { + _, err := conn.Read(readBuffer) + assert.NoError(t, err) + } + wg.Done() +} + func TestSecretConnectionReadWrite(t *testing.T) { fooConn, barConn := makeKVStoreConnPair() fooWrites, barWrites := []string{}, []string{} From a6011c007db764b722c5fe6c5ccb9d85edbb92fe Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Sun, 13 Jan 2019 20:31:31 +0100 Subject: [PATCH 20/31] Close and retry a RemoteSigner on err (#2923) * Close and recreate a RemoteSigner on err * Update changelog * Address Anton's comments / suggestions: - update changelog - restart TCPVal - shut down on `ErrUnexpectedResponse` * re-init remote signer client with fresh connection if Ping fails - add/update TODOs in secret connection - rename tcp.go -> tcp_client.go, same with ipc to clarify their purpose * account for `conn returned by waitConnection can be `nil` - also add TODO about RemoteSigner conn field * Tests for retrying: IPC / TCP - shorter info log on success - set conn and use it in tests to close conn * Tests for retrying: IPC / TCP - shorter info log on success - set conn and use it in tests to close conn - add rwmutex for conn field in IPC * comments and doc.go * fix ipc tests. fixes #2677 * use constants for tests * cleanup some error statements * fixes #2784, race in tests * remove print statement * minor fixes from review * update comment on sts spec * cosmetics * p2p/conn: add failing tests * p2p/conn: make SecretConnection thread safe * changelog * IPCVal signer refactor - use a .reset() method - don't use embedded RemoteSignerClient - guard RemoteSignerClient with mutex - drop the .conn - expose Close() on RemoteSignerClient * apply IPCVal refactor to TCPVal * remove mtx from RemoteSignerClient * consolidate IPCVal and TCPVal, fixes #3104 - done in tcp_client.go - now called SocketVal - takes a listener in the constructor - make tcpListener and unixListener contain all the differences * delete ipc files * introduce unix and tcp dialer for RemoteSigner * rename files - drop tcp_ prefix - rename priv_validator.go to file.go * bring back listener options * fix node * fix priv_val_server * fix node test * minor cleanup and comments --- CHANGELOG_PENDING.md | 6 +- cmd/priv_val_server/main.go | 22 +- node/node.go | 17 +- node/node_test.go | 26 +- privval/client.go | 263 ++++++++++++++++++ privval/{tcp_test.go => client_test.go} | 152 ++++++---- privval/doc.go | 21 ++ privval/{priv_validator.go => file.go} | 3 +- .../{priv_validator_test.go => file_test.go} | 0 privval/ipc.go | 123 -------- privval/ipc_server.go | 132 --------- privval/ipc_test.go | 147 ---------- .../{old_priv_validator.go => old_file.go} | 0 ...riv_validator_test.go => old_file_test.go} | 0 privval/remote_signer.go | 56 ++-- privval/{tcp_server.go => server.go} | 88 +++--- privval/socket.go | 184 ++++++++++++ .../{tcp_socket_test.go => socket_test.go} | 35 ++- privval/tcp.go | 216 -------------- privval/tcp_socket.go | 90 ------ 20 files changed, 710 insertions(+), 871 deletions(-) create mode 100644 privval/client.go rename privval/{tcp_test.go => client_test.go} (70%) create mode 100644 privval/doc.go rename privval/{priv_validator.go => file.go} (99%) rename privval/{priv_validator_test.go => file_test.go} (100%) delete mode 100644 privval/ipc.go delete mode 100644 privval/ipc_server.go delete mode 100644 privval/ipc_test.go rename privval/{old_priv_validator.go => old_file.go} (100%) rename privval/{old_priv_validator_test.go => old_file_test.go} (100%) rename privval/{tcp_server.go => server.go} (63%) create mode 100644 privval/socket.go rename privval/{tcp_socket_test.go => socket_test.go} (52%) delete mode 100644 privval/tcp.go delete mode 100644 privval/tcp_socket.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 7fc9bead..fd95a944 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -10,8 +10,8 @@ Special thanks to external contributors on this release: - [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. - [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. - [config] \#2992 `allow_duplicate_ip` is now set to false - - [privval] \#2926 split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types +- [privval] \#2923 listen for unix socket connections instead of dialing them * Apps @@ -23,13 +23,13 @@ Special thanks to external contributors on this release: * Blockchain Protocol * P2P Protocol -- multiple connections from the same IP are now disabled by default (see `allow_duplicate_ip` config option) ### FEATURES: -- [privval] \#1181 Split immutable and mutable parts of priv_validator.json +- [privval] \#1181 Split immutable and mutable parts of `priv_validator.json` ### IMPROVEMENTS: - [p2p/conn] \#3111 make SecretConnection thread safe +- [privval] \#2923 retry RemoteSigner connections on error - [rpc] \#3047 Include peer's remote IP in `/net_info` ### BUG FIXES: diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 54602558..6949e878 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "os" + "time" "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" @@ -34,13 +35,20 @@ func main() { pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath) - rs := privval.NewRemoteSigner( - logger, - *chainID, - *addr, - pv, - ed25519.GenPrivKey(), - ) + var dialer privval.Dialer + protocol, address := cmn.ProtocolAndAddress(*addr) + switch protocol { + case "unix": + dialer = privval.DialUnixFn(address) + case "tcp": + connTimeout := 3 * time.Second // TODO + dialer = privval.DialTCPFn(address, connTimeout, ed25519.GenPrivKey()) + default: + logger.Error("Unknown protocol", "protocol", protocol) + return + } + + rs := privval.NewRemoteSigner(logger, *chainID, pv, dialer) err := rs.Start() if err != nil { panic(err) diff --git a/node/node.go b/node/node.go index be4c7cc7..b7998dac 100644 --- a/node/node.go +++ b/node/node.go @@ -878,16 +878,20 @@ func createAndStartPrivValidatorSocketClient( listenAddr string, logger log.Logger, ) (types.PrivValidator, error) { - var pvsc types.PrivValidator + var listener net.Listener protocol, address := cmn.ProtocolAndAddress(listenAddr) + ln, err := net.Listen(protocol, address) + if err != nil { + return nil, err + } switch protocol { case "unix": - pvsc = privval.NewIPCVal(logger.With("module", "privval"), address) + listener = privval.NewUnixListener(ln) case "tcp": // TODO: persist this key so external signer // can actually authenticate us - pvsc = privval.NewTCPVal(logger.With("module", "privval"), listenAddr, ed25519.GenPrivKey()) + listener = privval.NewTCPListener(ln, ed25519.GenPrivKey()) default: return nil, fmt.Errorf( "Wrong listen address: expected either 'tcp' or 'unix' protocols, got %s", @@ -895,10 +899,9 @@ func createAndStartPrivValidatorSocketClient( ) } - if pvsc, ok := pvsc.(cmn.Service); ok { - if err := pvsc.Start(); err != nil { - return nil, errors.Wrap(err, "failed to start") - } + pvsc := privval.NewSocketVal(logger.With("module", "privval"), listener) + if err := pvsc.Start(); err != nil { + return nil, errors.Wrap(err, "failed to start") } return pvsc, nil diff --git a/node/node_test.go b/node/node_test.go index e675eb9a..96d779d4 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -122,25 +122,25 @@ func TestNodeSetPrivValTCP(t *testing.T) { config := cfg.ResetTestRoot("node_priv_val_tcp_test") config.BaseConfig.PrivValidatorListenAddr = addr - rs := privval.NewRemoteSigner( + dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey()) + pvsc := privval.NewRemoteSigner( log.TestingLogger(), config.ChainID(), - addr, types.NewMockPV(), - ed25519.GenPrivKey(), + dialer, ) - privval.RemoteSignerConnDeadline(5 * time.Millisecond)(rs) + go func() { - err := rs.Start() + err := pvsc.Start() if err != nil { panic(err) } }() - defer rs.Stop() + defer pvsc.Stop() n, err := DefaultNewNode(config, log.TestingLogger()) require.NoError(t, err) - assert.IsType(t, &privval.TCPVal{}, n.PrivValidator()) + assert.IsType(t, &privval.SocketVal{}, n.PrivValidator()) } // address without a protocol must result in error @@ -161,25 +161,25 @@ func TestNodeSetPrivValIPC(t *testing.T) { config := cfg.ResetTestRoot("node_priv_val_tcp_test") config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile - rs := privval.NewIPCRemoteSigner( + dialer := privval.DialUnixFn(tmpfile) + pvsc := privval.NewRemoteSigner( log.TestingLogger(), config.ChainID(), - tmpfile, types.NewMockPV(), + dialer, ) - privval.IPCRemoteSignerConnDeadline(3 * time.Second)(rs) done := make(chan struct{}) go func() { defer close(done) n, err := DefaultNewNode(config, log.TestingLogger()) require.NoError(t, err) - assert.IsType(t, &privval.IPCVal{}, n.PrivValidator()) + assert.IsType(t, &privval.SocketVal{}, n.PrivValidator()) }() - err := rs.Start() + err := pvsc.Start() require.NoError(t, err) - defer rs.Stop() + defer pvsc.Stop() <-done } diff --git a/privval/client.go b/privval/client.go new file mode 100644 index 00000000..4d4395fd --- /dev/null +++ b/privval/client.go @@ -0,0 +1,263 @@ +package privval + +import ( + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/tendermint/tendermint/crypto" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" +) + +const ( + defaultConnHeartBeatSeconds = 2 + defaultDialRetries = 10 +) + +// Socket errors. +var ( + ErrUnexpectedResponse = errors.New("received unexpected response") +) + +var ( + connHeartbeat = time.Second * defaultConnHeartBeatSeconds +) + +// SocketValOption sets an optional parameter on the SocketVal. +type SocketValOption func(*SocketVal) + +// SocketValHeartbeat sets the period on which to check the liveness of the +// connected Signer connections. +func SocketValHeartbeat(period time.Duration) SocketValOption { + return func(sc *SocketVal) { sc.connHeartbeat = period } +} + +// SocketVal implements PrivValidator. +// It listens for an external process to dial in and uses +// the socket to request signatures. +type SocketVal struct { + cmn.BaseService + + listener net.Listener + + // ping + cancelPing chan struct{} + pingTicker *time.Ticker + connHeartbeat time.Duration + + // signer is mutable since it can be + // reset if the connection fails. + // failures are detected by a background + // ping routine. + // Methods on the underlying net.Conn itself + // are already gorountine safe. + mtx sync.RWMutex + signer *RemoteSignerClient +} + +// Check that SocketVal implements PrivValidator. +var _ types.PrivValidator = (*SocketVal)(nil) + +// NewSocketVal returns an instance of SocketVal. +func NewSocketVal( + logger log.Logger, + listener net.Listener, +) *SocketVal { + sc := &SocketVal{ + listener: listener, + connHeartbeat: connHeartbeat, + } + + sc.BaseService = *cmn.NewBaseService(logger, "SocketVal", sc) + + return sc +} + +//-------------------------------------------------------- +// Implement PrivValidator + +// GetPubKey implements PrivValidator. +func (sc *SocketVal) GetPubKey() crypto.PubKey { + sc.mtx.RLock() + defer sc.mtx.RUnlock() + return sc.signer.GetPubKey() +} + +// SignVote implements PrivValidator. +func (sc *SocketVal) SignVote(chainID string, vote *types.Vote) error { + sc.mtx.RLock() + defer sc.mtx.RUnlock() + return sc.signer.SignVote(chainID, vote) +} + +// SignProposal implements PrivValidator. +func (sc *SocketVal) SignProposal(chainID string, proposal *types.Proposal) error { + sc.mtx.RLock() + defer sc.mtx.RUnlock() + return sc.signer.SignProposal(chainID, proposal) +} + +//-------------------------------------------------------- +// More thread safe methods proxied to the signer + +// Ping is used to check connection health. +func (sc *SocketVal) Ping() error { + sc.mtx.RLock() + defer sc.mtx.RUnlock() + return sc.signer.Ping() +} + +// Close closes the underlying net.Conn. +func (sc *SocketVal) Close() { + sc.mtx.RLock() + defer sc.mtx.RUnlock() + if sc.signer != nil { + if err := sc.signer.Close(); err != nil { + sc.Logger.Error("OnStop", "err", err) + } + } + + if sc.listener != nil { + if err := sc.listener.Close(); err != nil { + sc.Logger.Error("OnStop", "err", err) + } + } +} + +//-------------------------------------------------------- +// Service start and stop + +// OnStart implements cmn.Service. +func (sc *SocketVal) OnStart() error { + if closed, err := sc.reset(); err != nil { + sc.Logger.Error("OnStart", "err", err) + return err + } else if closed { + return fmt.Errorf("listener is closed") + } + + // Start a routine to keep the connection alive + sc.cancelPing = make(chan struct{}, 1) + sc.pingTicker = time.NewTicker(sc.connHeartbeat) + go func() { + for { + select { + case <-sc.pingTicker.C: + err := sc.Ping() + if err != nil { + sc.Logger.Error("Ping", "err", err) + if err == ErrUnexpectedResponse { + return + } + + closed, err := sc.reset() + if err != nil { + sc.Logger.Error("Reconnecting to remote signer failed", "err", err) + continue + } + if closed { + sc.Logger.Info("listener is closing") + return + } + + sc.Logger.Info("Re-created connection to remote signer", "impl", sc) + } + case <-sc.cancelPing: + sc.pingTicker.Stop() + return + } + } + }() + + return nil +} + +// OnStop implements cmn.Service. +func (sc *SocketVal) OnStop() { + if sc.cancelPing != nil { + close(sc.cancelPing) + } + sc.Close() +} + +//-------------------------------------------------------- +// Connection and signer management + +// waits to accept and sets a new connection. +// connection is closed in OnStop. +// returns true if the listener is closed +// (ie. it returns a nil conn). +func (sc *SocketVal) reset() (bool, error) { + sc.mtx.Lock() + defer sc.mtx.Unlock() + + // first check if the conn already exists and close it. + if sc.signer != nil { + if err := sc.signer.Close(); err != nil { + sc.Logger.Error("error closing connection", "err", err) + } + } + + // wait for a new conn + conn, err := sc.waitConnection() + if err != nil { + return false, err + } + + // listener is closed + if conn == nil { + return true, nil + } + + sc.signer, err = NewRemoteSignerClient(conn) + if err != nil { + // failed to fetch the pubkey. close out the connection. + if err := conn.Close(); err != nil { + sc.Logger.Error("error closing connection", "err", err) + } + return false, err + } + return false, nil +} + +func (sc *SocketVal) acceptConnection() (net.Conn, error) { + conn, err := sc.listener.Accept() + if err != nil { + if !sc.IsRunning() { + return nil, nil // Ignore error from listener closing. + } + return nil, err + + } + return conn, nil +} + +// waitConnection uses the configured wait timeout to error if no external +// process connects in the time period. +func (sc *SocketVal) waitConnection() (net.Conn, error) { + var ( + connc = make(chan net.Conn, 1) + errc = make(chan error, 1) + ) + + go func(connc chan<- net.Conn, errc chan<- error) { + conn, err := sc.acceptConnection() + if err != nil { + errc <- err + return + } + + connc <- conn + }(connc, errc) + + select { + case conn := <-connc: + return conn, nil + case err := <-errc: + return nil, err + } +} diff --git a/privval/tcp_test.go b/privval/client_test.go similarity index 70% rename from privval/tcp_test.go rename to privval/client_test.go index e893ef40..7fae6bf8 100644 --- a/privval/tcp_test.go +++ b/privval/client_test.go @@ -17,6 +17,16 @@ import ( "github.com/tendermint/tendermint/types" ) +var ( + testAcceptDeadline = defaultAcceptDeadlineSeconds * time.Second + + testConnDeadline = 100 * time.Millisecond + testConnDeadline2o3 = 66 * time.Millisecond // 2/3 of the other one + + testHeartbeatTimeout = 10 * time.Millisecond + testHeartbeatTimeout3o2 = 6 * time.Millisecond // 3/2 of the other one +) + func TestSocketPVAddress(t *testing.T) { var ( chainID = cmn.RandStr(12) @@ -39,8 +49,7 @@ func TestSocketPVPubKey(t *testing.T) { defer sc.Stop() defer rs.Stop() - clientKey, err := sc.getPubKey() - require.NoError(t, err) + clientKey := sc.GetPubKey() privvalPubKey := rs.privVal.GetPubKey() @@ -95,14 +104,14 @@ func TestSocketPVVoteResetDeadline(t *testing.T) { defer sc.Stop() defer rs.Stop() - time.Sleep(3 * time.Millisecond) + time.Sleep(testConnDeadline2o3) require.NoError(t, rs.privVal.SignVote(chainID, want)) require.NoError(t, sc.SignVote(chainID, have)) assert.Equal(t, want.Signature, have.Signature) // This would exceed the deadline if it was not extended by the previous message - time.Sleep(3 * time.Millisecond) + time.Sleep(testConnDeadline2o3) require.NoError(t, rs.privVal.SignVote(chainID, want)) require.NoError(t, sc.SignVote(chainID, have)) @@ -122,7 +131,7 @@ func TestSocketPVVoteKeepalive(t *testing.T) { defer sc.Stop() defer rs.Stop() - time.Sleep(10 * time.Millisecond) + time.Sleep(testConnDeadline * 2) require.NoError(t, rs.privVal.SignVote(chainID, want)) require.NoError(t, sc.SignVote(chainID, have)) @@ -131,18 +140,13 @@ func TestSocketPVVoteKeepalive(t *testing.T) { func TestSocketPVDeadline(t *testing.T) { var ( - addr = testFreeAddr(t) - listenc = make(chan struct{}) - sc = NewTCPVal( - log.TestingLogger(), - addr, - ed25519.GenPrivKey(), - ) + addr = testFreeAddr(t) + listenc = make(chan struct{}) + thisConnTimeout = 100 * time.Millisecond + sc = newSocketVal(log.TestingLogger(), addr, thisConnTimeout) ) - TCPValConnTimeout(100 * time.Millisecond)(sc) - - go func(sc *TCPVal) { + go func(sc *SocketVal) { defer close(listenc) assert.Equal(t, sc.Start().(cmn.Error).Data(), ErrConnTimeout) @@ -199,9 +203,8 @@ func TestRemoteSignerRetry(t *testing.T) { rs := NewRemoteSigner( log.TestingLogger(), cmn.RandStr(12), - ln.Addr().String(), types.NewMockPV(), - ed25519.GenPrivKey(), + DialTCPFn(ln.Addr().String(), testConnDeadline, ed25519.GenPrivKey()), ) defer rs.Stop() @@ -230,15 +233,8 @@ func TestRemoteSignVoteErrors(t *testing.T) { defer sc.Stop() defer rs.Stop() - err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote}) - require.NoError(t, err) - - res, err := readMsg(sc.conn) - require.NoError(t, err) - - resp := *res.(*SignedVoteResponse) - require.NotNil(t, resp.Error) - require.Equal(t, resp.Error.Description, types.ErroringMockPVErr.Error()) + err := sc.SignVote("", vote) + require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) err = rs.privVal.SignVote(chainID, vote) require.Error(t, err) @@ -257,15 +253,8 @@ func TestRemoteSignProposalErrors(t *testing.T) { defer sc.Stop() defer rs.Stop() - err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal}) - require.NoError(t, err) - - res, err := readMsg(sc.conn) - require.NoError(t, err) - - resp := *res.(*SignedProposalResponse) - require.NotNil(t, resp.Error) - require.Equal(t, resp.Error.Description, types.ErroringMockPVErr.Error()) + err := sc.SignProposal("", proposal) + require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) err = rs.privVal.SignProposal(chainID, proposal) require.Error(t, err) @@ -285,15 +274,10 @@ func TestErrUnexpectedResponse(t *testing.T) { rs = NewRemoteSigner( logger, chainID, - addr, types.NewMockPV(), - ed25519.GenPrivKey(), - ) - sc = NewTCPVal( - logger, - addr, - ed25519.GenPrivKey(), + DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), ) + sc = newSocketVal(logger, addr, testConnDeadline) ) testStartSocketPV(t, readyc, sc) @@ -331,11 +315,73 @@ func TestErrUnexpectedResponse(t *testing.T) { require.Equal(t, err, ErrUnexpectedResponse) } +func TestRetryTCPConnToRemoteSigner(t *testing.T) { + var ( + addr = testFreeAddr(t) + logger = log.TestingLogger() + chainID = cmn.RandStr(12) + readyc = make(chan struct{}) + + rs = NewRemoteSigner( + logger, + chainID, + types.NewMockPV(), + DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), + ) + thisConnTimeout = testConnDeadline + sc = newSocketVal(logger, addr, thisConnTimeout) + ) + // Ping every: + SocketValHeartbeat(testHeartbeatTimeout)(sc) + + RemoteSignerConnDeadline(testConnDeadline)(rs) + RemoteSignerConnRetries(10)(rs) + + testStartSocketPV(t, readyc, sc) + defer sc.Stop() + require.NoError(t, rs.Start()) + assert.True(t, rs.IsRunning()) + + <-readyc + time.Sleep(testHeartbeatTimeout * 2) + + rs.Stop() + rs2 := NewRemoteSigner( + logger, + chainID, + types.NewMockPV(), + DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), + ) + // let some pings pass + time.Sleep(testHeartbeatTimeout3o2) + require.NoError(t, rs2.Start()) + assert.True(t, rs2.IsRunning()) + defer rs2.Stop() + + // give the client some time to re-establish the conn to the remote signer + // should see sth like this in the logs: + // + // E[10016-01-10|17:12:46.128] Ping err="remote signer timed out" + // I[10016-01-10|17:16:42.447] Re-created connection to remote signer impl=SocketVal + time.Sleep(testConnDeadline * 2) +} + +func newSocketVal(logger log.Logger, addr string, connDeadline time.Duration) *SocketVal { + ln, err := net.Listen(cmn.ProtocolAndAddress(addr)) + if err != nil { + panic(err) + } + tcpLn := NewTCPListener(ln, ed25519.GenPrivKey()) + TCPListenerAcceptDeadline(testAcceptDeadline)(tcpLn) + TCPListenerConnDeadline(testConnDeadline)(tcpLn) + return NewSocketVal(logger, tcpLn) +} + func testSetupSocketPair( t *testing.T, chainID string, privValidator types.PrivValidator, -) (*TCPVal, *RemoteSigner) { +) (*SocketVal, *RemoteSigner) { var ( addr = testFreeAddr(t) logger = log.TestingLogger() @@ -344,20 +390,16 @@ func testSetupSocketPair( rs = NewRemoteSigner( logger, chainID, - addr, privVal, - ed25519.GenPrivKey(), - ) - sc = NewTCPVal( - logger, - addr, - ed25519.GenPrivKey(), + DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), ) + + thisConnTimeout = testConnDeadline + sc = newSocketVal(logger, addr, thisConnTimeout) ) - TCPValConnTimeout(5 * time.Millisecond)(sc) - TCPValHeartbeat(2 * time.Millisecond)(sc) - RemoteSignerConnDeadline(5 * time.Millisecond)(rs) + SocketValHeartbeat(testHeartbeatTimeout)(sc) + RemoteSignerConnDeadline(testConnDeadline)(rs) RemoteSignerConnRetries(1e6)(rs) testStartSocketPV(t, readyc, sc) @@ -378,8 +420,8 @@ func testReadWriteResponse(t *testing.T, resp RemoteSignerMsg, rsConn net.Conn) require.NoError(t, err) } -func testStartSocketPV(t *testing.T, readyc chan struct{}, sc *TCPVal) { - go func(sc *TCPVal) { +func testStartSocketPV(t *testing.T, readyc chan struct{}, sc *SocketVal) { + go func(sc *SocketVal) { require.NoError(t, sc.Start()) assert.True(t, sc.IsRunning()) diff --git a/privval/doc.go b/privval/doc.go new file mode 100644 index 00000000..ed378c19 --- /dev/null +++ b/privval/doc.go @@ -0,0 +1,21 @@ +/* + +Package privval provides different implementations of the types.PrivValidator. + +FilePV + +FilePV is the simplest implementation and developer default. It uses one file for the private key and another to store state. + +SocketVal + +SocketVal establishes a connection to an external process, like a Key Management Server (KMS), using a socket. +SocketVal listens for the external KMS process to dial in. +SocketVal takes a listener, which determines the type of connection +(ie. encrypted over tcp, or unencrypted over unix). + +RemoteSigner + +RemoteSigner is a simple wrapper around a net.Conn. It's used by both IPCVal and TCPVal. + +*/ +package privval diff --git a/privval/priv_validator.go b/privval/file.go similarity index 99% rename from privval/priv_validator.go rename to privval/file.go index 1ee5b4d8..8072cfa4 100644 --- a/privval/priv_validator.go +++ b/privval/file.go @@ -22,6 +22,7 @@ const ( stepPrecommit int8 = 3 ) +// A vote is either stepPrevote or stepPrecommit. func voteToStep(vote *types.Vote) int8 { switch vote.Type { case types.PrevoteType: @@ -29,7 +30,7 @@ func voteToStep(vote *types.Vote) int8 { case types.PrecommitType: return stepPrecommit default: - cmn.PanicSanity("Unknown vote type") + panic("Unknown vote type") return 0 } } diff --git a/privval/priv_validator_test.go b/privval/file_test.go similarity index 100% rename from privval/priv_validator_test.go rename to privval/file_test.go diff --git a/privval/ipc.go b/privval/ipc.go deleted file mode 100644 index 1c82db33..00000000 --- a/privval/ipc.go +++ /dev/null @@ -1,123 +0,0 @@ -package privval - -import ( - "net" - "time" - - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/types" -) - -// IPCValOption sets an optional parameter on the SocketPV. -type IPCValOption func(*IPCVal) - -// IPCValConnTimeout sets the read and write timeout for connections -// from external signing processes. -func IPCValConnTimeout(timeout time.Duration) IPCValOption { - return func(sc *IPCVal) { sc.connTimeout = timeout } -} - -// IPCValHeartbeat sets the period on which to check the liveness of the -// connected Signer connections. -func IPCValHeartbeat(period time.Duration) IPCValOption { - return func(sc *IPCVal) { sc.connHeartbeat = period } -} - -// IPCVal implements PrivValidator, it uses a unix socket to request signatures -// from an external process. -type IPCVal struct { - cmn.BaseService - *RemoteSignerClient - - addr string - - connTimeout time.Duration - connHeartbeat time.Duration - - conn net.Conn - cancelPing chan struct{} - pingTicker *time.Ticker -} - -// Check that IPCVal implements PrivValidator. -var _ types.PrivValidator = (*IPCVal)(nil) - -// NewIPCVal returns an instance of IPCVal. -func NewIPCVal( - logger log.Logger, - socketAddr string, -) *IPCVal { - sc := &IPCVal{ - addr: socketAddr, - connTimeout: connTimeout, - connHeartbeat: connHeartbeat, - } - - sc.BaseService = *cmn.NewBaseService(logger, "IPCVal", sc) - - return sc -} - -// OnStart implements cmn.Service. -func (sc *IPCVal) OnStart() error { - err := sc.connect() - if err != nil { - sc.Logger.Error("OnStart", "err", err) - return err - } - - sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn) - if err != nil { - return err - } - - // Start a routine to keep the connection alive - sc.cancelPing = make(chan struct{}, 1) - sc.pingTicker = time.NewTicker(sc.connHeartbeat) - go func() { - for { - select { - case <-sc.pingTicker.C: - err := sc.Ping() - if err != nil { - sc.Logger.Error("Ping", "err", err) - } - case <-sc.cancelPing: - sc.pingTicker.Stop() - return - } - } - }() - - return nil -} - -// OnStop implements cmn.Service. -func (sc *IPCVal) OnStop() { - if sc.cancelPing != nil { - close(sc.cancelPing) - } - - if sc.conn != nil { - if err := sc.conn.Close(); err != nil { - sc.Logger.Error("OnStop", "err", err) - } - } -} - -func (sc *IPCVal) connect() error { - la, err := net.ResolveUnixAddr("unix", sc.addr) - if err != nil { - return err - } - - conn, err := net.DialUnix("unix", nil, la) - if err != nil { - return err - } - - sc.conn = newTimeoutConn(conn, sc.connTimeout) - - return nil -} diff --git a/privval/ipc_server.go b/privval/ipc_server.go deleted file mode 100644 index ba957477..00000000 --- a/privval/ipc_server.go +++ /dev/null @@ -1,132 +0,0 @@ -package privval - -import ( - "io" - "net" - "time" - - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/types" -) - -// IPCRemoteSignerOption sets an optional parameter on the IPCRemoteSigner. -type IPCRemoteSignerOption func(*IPCRemoteSigner) - -// IPCRemoteSignerConnDeadline sets the read and write deadline for connections -// from external signing processes. -func IPCRemoteSignerConnDeadline(deadline time.Duration) IPCRemoteSignerOption { - return func(ss *IPCRemoteSigner) { ss.connDeadline = deadline } -} - -// IPCRemoteSignerConnRetries sets the amount of attempted retries to connect. -func IPCRemoteSignerConnRetries(retries int) IPCRemoteSignerOption { - return func(ss *IPCRemoteSigner) { ss.connRetries = retries } -} - -// IPCRemoteSigner is a RPC implementation of PrivValidator that listens on a unix socket. -type IPCRemoteSigner struct { - cmn.BaseService - - addr string - chainID string - connDeadline time.Duration - connRetries int - privVal types.PrivValidator - - listener *net.UnixListener -} - -// NewIPCRemoteSigner returns an instance of IPCRemoteSigner. -func NewIPCRemoteSigner( - logger log.Logger, - chainID, socketAddr string, - privVal types.PrivValidator, -) *IPCRemoteSigner { - rs := &IPCRemoteSigner{ - addr: socketAddr, - chainID: chainID, - connDeadline: time.Second * defaultConnDeadlineSeconds, - connRetries: defaultDialRetries, - privVal: privVal, - } - - rs.BaseService = *cmn.NewBaseService(logger, "IPCRemoteSigner", rs) - - return rs -} - -// OnStart implements cmn.Service. -func (rs *IPCRemoteSigner) OnStart() error { - err := rs.listen() - if err != nil { - err = cmn.ErrorWrap(err, "listen") - rs.Logger.Error("OnStart", "err", err) - return err - } - - go func() { - for { - conn, err := rs.listener.AcceptUnix() - if err != nil { - rs.Logger.Error("AcceptUnix", "err", err) - return - } - go rs.handleConnection(conn) - } - }() - - return nil -} - -// OnStop implements cmn.Service. -func (rs *IPCRemoteSigner) OnStop() { - if rs.listener != nil { - if err := rs.listener.Close(); err != nil { - rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed")) - } - } -} - -func (rs *IPCRemoteSigner) listen() error { - la, err := net.ResolveUnixAddr("unix", rs.addr) - if err != nil { - return err - } - - rs.listener, err = net.ListenUnix("unix", la) - - return err -} - -func (rs *IPCRemoteSigner) handleConnection(conn net.Conn) { - for { - if !rs.IsRunning() { - return // Ignore error from listener closing. - } - - // Reset the connection deadline - conn.SetDeadline(time.Now().Add(rs.connDeadline)) - - req, err := readMsg(conn) - if err != nil { - if err != io.EOF { - rs.Logger.Error("handleConnection", "err", err) - } - return - } - - res, err := handleRequest(req, rs.chainID, rs.privVal) - - if err != nil { - // only log the error; we'll reply with an error in res - rs.Logger.Error("handleConnection", "err", err) - } - - err = writeMsg(conn, res) - if err != nil { - rs.Logger.Error("handleConnection", "err", err) - return - } - } -} diff --git a/privval/ipc_test.go b/privval/ipc_test.go deleted file mode 100644 index c8d6dfc7..00000000 --- a/privval/ipc_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package privval - -import ( - "io/ioutil" - "os" - "testing" - "time" - - "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/types" -) - -func TestIPCPVVote(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupIPCSocketPair(t, chainID, types.NewMockPV()) - - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) -} - -func TestIPCPVVoteResetDeadline(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupIPCSocketPair(t, chainID, types.NewMockPV()) - - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - time.Sleep(3 * time.Millisecond) - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) - - // This would exceed the deadline if it was not extended by the previous message - time.Sleep(3 * time.Millisecond) - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) -} - -func TestIPCPVVoteKeepalive(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupIPCSocketPair(t, chainID, types.NewMockPV()) - - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - time.Sleep(10 * time.Millisecond) - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) -} - -func testSetupIPCSocketPair( - t *testing.T, - chainID string, - privValidator types.PrivValidator, -) (*IPCVal, *IPCRemoteSigner) { - addr, err := testUnixAddr() - require.NoError(t, err) - - var ( - logger = log.TestingLogger() - privVal = privValidator - readyc = make(chan struct{}) - rs = NewIPCRemoteSigner( - logger, - chainID, - addr, - privVal, - ) - sc = NewIPCVal( - logger, - addr, - ) - ) - - IPCValConnTimeout(5 * time.Millisecond)(sc) - IPCValHeartbeat(time.Millisecond)(sc) - - IPCRemoteSignerConnDeadline(time.Millisecond * 5)(rs) - - testStartIPCRemoteSigner(t, readyc, rs) - - <-readyc - - require.NoError(t, sc.Start()) - assert.True(t, sc.IsRunning()) - - return sc, rs -} - -func testStartIPCRemoteSigner(t *testing.T, readyc chan struct{}, rs *IPCRemoteSigner) { - go func(rs *IPCRemoteSigner) { - require.NoError(t, rs.Start()) - assert.True(t, rs.IsRunning()) - - readyc <- struct{}{} - }(rs) -} - -func testUnixAddr() (string, error) { - f, err := ioutil.TempFile("/tmp", "nettest") - if err != nil { - return "", err - } - - addr := f.Name() - err = f.Close() - if err != nil { - return "", err - } - err = os.Remove(addr) - if err != nil { - return "", err - } - - return addr, nil -} diff --git a/privval/old_priv_validator.go b/privval/old_file.go similarity index 100% rename from privval/old_priv_validator.go rename to privval/old_file.go diff --git a/privval/old_priv_validator_test.go b/privval/old_file_test.go similarity index 100% rename from privval/old_priv_validator_test.go rename to privval/old_file_test.go diff --git a/privval/remote_signer.go b/privval/remote_signer.go index b80884de..d928b198 100644 --- a/privval/remote_signer.go +++ b/privval/remote_signer.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "net" - "sync" "github.com/pkg/errors" @@ -14,31 +13,41 @@ import ( "github.com/tendermint/tendermint/types" ) -// RemoteSignerClient implements PrivValidator, it uses a socket to request signatures +// Socket errors. +var ( + ErrConnTimeout = errors.New("remote signer timed out") +) + +// RemoteSignerClient implements PrivValidator. +// It uses a net.Conn to request signatures // from an external process. type RemoteSignerClient struct { - conn net.Conn + conn net.Conn + + // memoized consensusPubKey crypto.PubKey - mtx sync.Mutex } // Check that RemoteSignerClient implements PrivValidator. var _ types.PrivValidator = (*RemoteSignerClient)(nil) // NewRemoteSignerClient returns an instance of RemoteSignerClient. -func NewRemoteSignerClient( - conn net.Conn, -) (*RemoteSignerClient, error) { - sc := &RemoteSignerClient{ - conn: conn, - } - pubKey, err := sc.getPubKey() +func NewRemoteSignerClient(conn net.Conn) (*RemoteSignerClient, error) { + + // retrieve and memoize the consensus public key once. + pubKey, err := getPubKey(conn) if err != nil { return nil, cmn.ErrorWrap(err, "error while retrieving public key for remote signer") } - // retrieve and memoize the consensus public key once: - sc.consensusPubKey = pubKey - return sc, nil + return &RemoteSignerClient{ + conn: conn, + consensusPubKey: pubKey, + }, nil +} + +// Close calls Close on the underlying net.Conn. +func (sc *RemoteSignerClient) Close() error { + return sc.conn.Close() } // GetPubKey implements PrivValidator. @@ -46,16 +55,14 @@ func (sc *RemoteSignerClient) GetPubKey() crypto.PubKey { return sc.consensusPubKey } -func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) { - sc.mtx.Lock() - defer sc.mtx.Unlock() - - err := writeMsg(sc.conn, &PubKeyRequest{}) +// not thread-safe (only called on startup). +func getPubKey(conn net.Conn) (crypto.PubKey, error) { + err := writeMsg(conn, &PubKeyRequest{}) if err != nil { return nil, err } - res, err := readMsg(sc.conn) + res, err := readMsg(conn) if err != nil { return nil, err } @@ -73,9 +80,6 @@ func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) { // SignVote implements PrivValidator. func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error { - sc.mtx.Lock() - defer sc.mtx.Unlock() - err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote}) if err != nil { return err @@ -103,9 +107,6 @@ func (sc *RemoteSignerClient) SignProposal( chainID string, proposal *types.Proposal, ) error { - sc.mtx.Lock() - defer sc.mtx.Unlock() - err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal}) if err != nil { return err @@ -129,9 +130,6 @@ func (sc *RemoteSignerClient) SignProposal( // Ping is used to check connection health. func (sc *RemoteSignerClient) Ping() error { - sc.mtx.Lock() - defer sc.mtx.Unlock() - err := writeMsg(sc.conn, &PingRequest{}) if err != nil { return err diff --git a/privval/tcp_server.go b/privval/server.go similarity index 63% rename from privval/tcp_server.go rename to privval/server.go index 694023d7..8b22c69e 100644 --- a/privval/tcp_server.go +++ b/privval/server.go @@ -5,6 +5,7 @@ import ( "net" "time" + "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" @@ -12,6 +13,11 @@ import ( "github.com/tendermint/tendermint/types" ) +// Socket errors. +var ( + ErrDialRetryMax = errors.New("dialed maximum retries") +) + // RemoteSignerOption sets an optional parameter on the RemoteSigner. type RemoteSignerOption func(*RemoteSigner) @@ -26,38 +32,64 @@ func RemoteSignerConnRetries(retries int) RemoteSignerOption { return func(ss *RemoteSigner) { ss.connRetries = retries } } -// RemoteSigner implements PrivValidator by dialing to a socket. +// RemoteSigner dials using its dialer and responds to any +// signature requests using its privVal. type RemoteSigner struct { cmn.BaseService - addr string chainID string connDeadline time.Duration connRetries int - privKey ed25519.PrivKeyEd25519 privVal types.PrivValidator - conn net.Conn + dialer Dialer + conn net.Conn } -// NewRemoteSigner returns an instance of RemoteSigner. +// Dialer dials a remote address and returns a net.Conn or an error. +type Dialer func() (net.Conn, error) + +// DialTCPFn dials the given tcp addr, using the given connTimeout and privKey for the +// authenticated encryption handshake. +func DialTCPFn(addr string, connTimeout time.Duration, privKey ed25519.PrivKeyEd25519) Dialer { + return func() (net.Conn, error) { + conn, err := cmn.Connect(addr) + if err == nil { + err = conn.SetDeadline(time.Now().Add(connTimeout)) + } + if err == nil { + conn, err = p2pconn.MakeSecretConnection(conn, privKey) + } + return conn, err + } +} + +// DialUnixFn dials the given unix socket. +func DialUnixFn(addr string) Dialer { + return func() (net.Conn, error) { + unixAddr := &net.UnixAddr{addr, "unix"} + return net.DialUnix("unix", nil, unixAddr) + } +} + +// NewRemoteSigner return a RemoteSigner that will dial using the given +// dialer and respond to any signature requests over the connection +// using the given privVal. func NewRemoteSigner( logger log.Logger, - chainID, socketAddr string, + chainID string, privVal types.PrivValidator, - privKey ed25519.PrivKeyEd25519, + dialer Dialer, ) *RemoteSigner { rs := &RemoteSigner{ - addr: socketAddr, chainID: chainID, connDeadline: time.Second * defaultConnDeadlineSeconds, connRetries: defaultDialRetries, - privKey: privKey, privVal: privVal, + dialer: dialer, } rs.BaseService = *cmn.NewBaseService(logger, "RemoteSigner", rs) - return rs } @@ -68,6 +100,7 @@ func (rs *RemoteSigner) OnStart() error { rs.Logger.Error("OnStart", "err", err) return err } + rs.conn = conn go rs.handleConnection(conn) @@ -91,36 +124,11 @@ func (rs *RemoteSigner) connect() (net.Conn, error) { if retries != rs.connRetries { time.Sleep(rs.connDeadline) } - - conn, err := cmn.Connect(rs.addr) + conn, err := rs.dialer() if err != nil { - rs.Logger.Error( - "connect", - "addr", rs.addr, - "err", err, - ) - + rs.Logger.Error("dialing", "err", err) continue } - - if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil { - rs.Logger.Error( - "connect", - "err", err, - ) - continue - } - - conn, err = p2pconn.MakeSecretConnection(conn, rs.privKey) - if err != nil { - rs.Logger.Error( - "connect", - "err", err, - ) - - continue - } - return conn, nil } @@ -139,7 +147,7 @@ func (rs *RemoteSigner) handleConnection(conn net.Conn) { req, err := readMsg(conn) if err != nil { if err != io.EOF { - rs.Logger.Error("handleConnection", "err", err) + rs.Logger.Error("handleConnection readMsg", "err", err) } return } @@ -148,12 +156,12 @@ func (rs *RemoteSigner) handleConnection(conn net.Conn) { if err != nil { // only log the error; we'll reply with an error in res - rs.Logger.Error("handleConnection", "err", err) + rs.Logger.Error("handleConnection handleRequest", "err", err) } err = writeMsg(conn, res) if err != nil { - rs.Logger.Error("handleConnection", "err", err) + rs.Logger.Error("handleConnection writeMsg", "err", err) return } } diff --git a/privval/socket.go b/privval/socket.go new file mode 100644 index 00000000..96fa6c8e --- /dev/null +++ b/privval/socket.go @@ -0,0 +1,184 @@ +package privval + +import ( + "net" + "time" + + "github.com/tendermint/tendermint/crypto/ed25519" + p2pconn "github.com/tendermint/tendermint/p2p/conn" +) + +const ( + defaultAcceptDeadlineSeconds = 3 + defaultConnDeadlineSeconds = 3 +) + +// timeoutError can be used to check if an error returned from the netp package +// was due to a timeout. +type timeoutError interface { + Timeout() bool +} + +//------------------------------------------------------------------ +// TCP Listener + +// TCPListenerOption sets an optional parameter on the tcpListener. +type TCPListenerOption func(*tcpListener) + +// TCPListenerAcceptDeadline sets the deadline for the listener. +// A zero time value disables the deadline. +func TCPListenerAcceptDeadline(deadline time.Duration) TCPListenerOption { + return func(tl *tcpListener) { tl.acceptDeadline = deadline } +} + +// TCPListenerConnDeadline sets the read and write deadline for connections +// from external signing processes. +func TCPListenerConnDeadline(deadline time.Duration) TCPListenerOption { + return func(tl *tcpListener) { tl.connDeadline = deadline } +} + +// tcpListener implements net.Listener. +var _ net.Listener = (*tcpListener)(nil) + +// tcpListener wraps a *net.TCPListener to standardise protocol timeouts +// and potentially other tuning parameters. It also returns encrypted connections. +type tcpListener struct { + *net.TCPListener + + secretConnKey ed25519.PrivKeyEd25519 + + acceptDeadline time.Duration + connDeadline time.Duration +} + +// NewTCPListener returns a listener that accepts authenticated encrypted connections +// using the given secretConnKey and the default timeout values. +func NewTCPListener(ln net.Listener, secretConnKey ed25519.PrivKeyEd25519) *tcpListener { + return &tcpListener{ + TCPListener: ln.(*net.TCPListener), + secretConnKey: secretConnKey, + acceptDeadline: time.Second * defaultAcceptDeadlineSeconds, + connDeadline: time.Second * defaultConnDeadlineSeconds, + } +} + +// Accept implements net.Listener. +func (ln *tcpListener) Accept() (net.Conn, error) { + err := ln.SetDeadline(time.Now().Add(ln.acceptDeadline)) + if err != nil { + return nil, err + } + + tc, err := ln.AcceptTCP() + if err != nil { + return nil, err + } + + // Wrap the conn in our timeout and encryption wrappers + timeoutConn := newTimeoutConn(tc, ln.connDeadline) + secretConn, err := p2pconn.MakeSecretConnection(timeoutConn, ln.secretConnKey) + if err != nil { + return nil, err + } + + return secretConn, nil +} + +//------------------------------------------------------------------ +// Unix Listener + +// unixListener implements net.Listener. +var _ net.Listener = (*unixListener)(nil) + +type UnixListenerOption func(*unixListener) + +// UnixListenerAcceptDeadline sets the deadline for the listener. +// A zero time value disables the deadline. +func UnixListenerAcceptDeadline(deadline time.Duration) UnixListenerOption { + return func(ul *unixListener) { ul.acceptDeadline = deadline } +} + +// UnixListenerConnDeadline sets the read and write deadline for connections +// from external signing processes. +func UnixListenerConnDeadline(deadline time.Duration) UnixListenerOption { + return func(ul *unixListener) { ul.connDeadline = deadline } +} + +// unixListener wraps a *net.UnixListener to standardise protocol timeouts +// and potentially other tuning parameters. It returns unencrypted connections. +type unixListener struct { + *net.UnixListener + + acceptDeadline time.Duration + connDeadline time.Duration +} + +// NewUnixListener returns a listener that accepts unencrypted connections +// using the default timeout values. +func NewUnixListener(ln net.Listener) *unixListener { + return &unixListener{ + UnixListener: ln.(*net.UnixListener), + acceptDeadline: time.Second * defaultAcceptDeadlineSeconds, + connDeadline: time.Second * defaultConnDeadlineSeconds, + } +} + +// Accept implements net.Listener. +func (ln *unixListener) Accept() (net.Conn, error) { + err := ln.SetDeadline(time.Now().Add(ln.acceptDeadline)) + if err != nil { + return nil, err + } + + tc, err := ln.AcceptUnix() + if err != nil { + return nil, err + } + + // Wrap the conn in our timeout wrapper + conn := newTimeoutConn(tc, ln.connDeadline) + + // TODO: wrap in something that authenticates + // with a MAC - https://github.com/tendermint/tendermint/issues/3099 + + return conn, nil +} + +//------------------------------------------------------------------ +// Connection + +// timeoutConn implements net.Conn. +var _ net.Conn = (*timeoutConn)(nil) + +// timeoutConn wraps a net.Conn to standardise protocol timeouts / deadline resets. +type timeoutConn struct { + net.Conn + + connDeadline time.Duration +} + +// newTimeoutConn returns an instance of newTCPTimeoutConn. +func newTimeoutConn( + conn net.Conn, + connDeadline time.Duration) *timeoutConn { + return &timeoutConn{ + conn, + connDeadline, + } +} + +// Read implements net.Conn. +func (c timeoutConn) Read(b []byte) (n int, err error) { + // Reset deadline + c.Conn.SetReadDeadline(time.Now().Add(c.connDeadline)) + + return c.Conn.Read(b) +} + +// Write implements net.Conn. +func (c timeoutConn) Write(b []byte) (n int, err error) { + // Reset deadline + c.Conn.SetWriteDeadline(time.Now().Add(c.connDeadline)) + + return c.Conn.Write(b) +} diff --git a/privval/tcp_socket_test.go b/privval/socket_test.go similarity index 52% rename from privval/tcp_socket_test.go rename to privval/socket_test.go index 285e73ed..0c05fa3a 100644 --- a/privval/tcp_socket_test.go +++ b/privval/socket_test.go @@ -4,17 +4,31 @@ import ( "net" "testing" "time" + + "github.com/tendermint/tendermint/crypto/ed25519" ) -func TestTCPTimeoutListenerAcceptDeadline(t *testing.T) { +//------------------------------------------- +// helper funcs + +func newPrivKey() ed25519.PrivKeyEd25519 { + return ed25519.GenPrivKey() +} + +//------------------------------------------- +// tests + +func TestTCPListenerAcceptDeadline(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } - ln = newTCPTimeoutListener(ln, time.Millisecond, time.Second, time.Second) + tcpLn := NewTCPListener(ln, newPrivKey()) + TCPListenerAcceptDeadline(time.Millisecond)(tcpLn) + TCPListenerConnDeadline(time.Second)(tcpLn) - _, err = ln.Accept() + _, err = tcpLn.Accept() opErr, ok := err.(*net.OpError) if !ok { t.Fatalf("have %v, want *net.OpError", err) @@ -25,14 +39,17 @@ func TestTCPTimeoutListenerAcceptDeadline(t *testing.T) { } } -func TestTCPTimeoutListenerConnDeadline(t *testing.T) { +func TestTCPListenerConnDeadline(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } - ln = newTCPTimeoutListener(ln, time.Second, time.Millisecond, time.Second) + tcpLn := NewTCPListener(ln, newPrivKey()) + TCPListenerAcceptDeadline(time.Second)(tcpLn) + TCPListenerConnDeadline(time.Millisecond)(tcpLn) + readyc := make(chan struct{}) donec := make(chan struct{}) go func(ln net.Listener) { defer close(donec) @@ -41,6 +58,7 @@ func TestTCPTimeoutListenerConnDeadline(t *testing.T) { if err != nil { t.Fatal(err) } + <-readyc time.Sleep(2 * time.Millisecond) @@ -54,12 +72,13 @@ func TestTCPTimeoutListenerConnDeadline(t *testing.T) { if have, want := opErr.Op, "read"; have != want { t.Errorf("have %v, want %v", have, want) } - }(ln) + }(tcpLn) - _, err = net.Dial("tcp", ln.Addr().String()) + dialer := DialTCPFn(ln.Addr().String(), testConnDeadline, newPrivKey()) + _, err = dialer() if err != nil { t.Fatal(err) } - + close(readyc) <-donec } diff --git a/privval/tcp.go b/privval/tcp.go deleted file mode 100644 index 1fb736e6..00000000 --- a/privval/tcp.go +++ /dev/null @@ -1,216 +0,0 @@ -package privval - -import ( - "errors" - "net" - "time" - - "github.com/tendermint/tendermint/crypto/ed25519" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - p2pconn "github.com/tendermint/tendermint/p2p/conn" - "github.com/tendermint/tendermint/types" -) - -const ( - defaultAcceptDeadlineSeconds = 3 - defaultConnDeadlineSeconds = 3 - defaultConnHeartBeatSeconds = 2 - defaultDialRetries = 10 -) - -// Socket errors. -var ( - ErrDialRetryMax = errors.New("dialed maximum retries") - ErrConnTimeout = errors.New("remote signer timed out") - ErrUnexpectedResponse = errors.New("received unexpected response") -) - -var ( - acceptDeadline = time.Second * defaultAcceptDeadlineSeconds - connTimeout = time.Second * defaultConnDeadlineSeconds - connHeartbeat = time.Second * defaultConnHeartBeatSeconds -) - -// TCPValOption sets an optional parameter on the SocketPV. -type TCPValOption func(*TCPVal) - -// TCPValAcceptDeadline sets the deadline for the TCPVal listener. -// A zero time value disables the deadline. -func TCPValAcceptDeadline(deadline time.Duration) TCPValOption { - return func(sc *TCPVal) { sc.acceptDeadline = deadline } -} - -// TCPValConnTimeout sets the read and write timeout for connections -// from external signing processes. -func TCPValConnTimeout(timeout time.Duration) TCPValOption { - return func(sc *TCPVal) { sc.connTimeout = timeout } -} - -// TCPValHeartbeat sets the period on which to check the liveness of the -// connected Signer connections. -func TCPValHeartbeat(period time.Duration) TCPValOption { - return func(sc *TCPVal) { sc.connHeartbeat = period } -} - -// TCPVal implements PrivValidator, it uses a socket to request signatures -// from an external process. -type TCPVal struct { - cmn.BaseService - *RemoteSignerClient - - addr string - acceptDeadline time.Duration - connTimeout time.Duration - connHeartbeat time.Duration - privKey ed25519.PrivKeyEd25519 - - conn net.Conn - listener net.Listener - cancelPing chan struct{} - pingTicker *time.Ticker -} - -// Check that TCPVal implements PrivValidator. -var _ types.PrivValidator = (*TCPVal)(nil) - -// NewTCPVal returns an instance of TCPVal. -func NewTCPVal( - logger log.Logger, - socketAddr string, - privKey ed25519.PrivKeyEd25519, -) *TCPVal { - sc := &TCPVal{ - addr: socketAddr, - acceptDeadline: acceptDeadline, - connTimeout: connTimeout, - connHeartbeat: connHeartbeat, - privKey: privKey, - } - - sc.BaseService = *cmn.NewBaseService(logger, "TCPVal", sc) - - return sc -} - -// OnStart implements cmn.Service. -func (sc *TCPVal) OnStart() error { - if err := sc.listen(); err != nil { - sc.Logger.Error("OnStart", "err", err) - return err - } - - conn, err := sc.waitConnection() - if err != nil { - sc.Logger.Error("OnStart", "err", err) - return err - } - - sc.conn = conn - sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn) - if err != nil { - return err - } - - // Start a routine to keep the connection alive - sc.cancelPing = make(chan struct{}, 1) - sc.pingTicker = time.NewTicker(sc.connHeartbeat) - go func() { - for { - select { - case <-sc.pingTicker.C: - err := sc.Ping() - if err != nil { - sc.Logger.Error( - "Ping", - "err", err, - ) - } - case <-sc.cancelPing: - sc.pingTicker.Stop() - return - } - } - }() - - return nil -} - -// OnStop implements cmn.Service. -func (sc *TCPVal) OnStop() { - if sc.cancelPing != nil { - close(sc.cancelPing) - } - - if sc.conn != nil { - if err := sc.conn.Close(); err != nil { - sc.Logger.Error("OnStop", "err", err) - } - } - - if sc.listener != nil { - if err := sc.listener.Close(); err != nil { - sc.Logger.Error("OnStop", "err", err) - } - } -} - -func (sc *TCPVal) acceptConnection() (net.Conn, error) { - conn, err := sc.listener.Accept() - if err != nil { - if !sc.IsRunning() { - return nil, nil // Ignore error from listener closing. - } - return nil, err - - } - - conn, err = p2pconn.MakeSecretConnection(conn, sc.privKey) - if err != nil { - return nil, err - } - - return conn, nil -} - -func (sc *TCPVal) listen() error { - ln, err := net.Listen(cmn.ProtocolAndAddress(sc.addr)) - if err != nil { - return err - } - - sc.listener = newTCPTimeoutListener( - ln, - sc.acceptDeadline, - sc.connTimeout, - sc.connHeartbeat, - ) - - return nil -} - -// waitConnection uses the configured wait timeout to error if no external -// process connects in the time period. -func (sc *TCPVal) waitConnection() (net.Conn, error) { - var ( - connc = make(chan net.Conn, 1) - errc = make(chan error, 1) - ) - - go func(connc chan<- net.Conn, errc chan<- error) { - conn, err := sc.acceptConnection() - if err != nil { - errc <- err - return - } - - connc <- conn - }(connc, errc) - - select { - case conn := <-connc: - return conn, nil - case err := <-errc: - return nil, err - } -} diff --git a/privval/tcp_socket.go b/privval/tcp_socket.go deleted file mode 100644 index 2b17bf26..00000000 --- a/privval/tcp_socket.go +++ /dev/null @@ -1,90 +0,0 @@ -package privval - -import ( - "net" - "time" -) - -// timeoutError can be used to check if an error returned from the netp package -// was due to a timeout. -type timeoutError interface { - Timeout() bool -} - -// tcpTimeoutListener implements net.Listener. -var _ net.Listener = (*tcpTimeoutListener)(nil) - -// tcpTimeoutListener wraps a *net.TCPListener to standardise protocol timeouts -// and potentially other tuning parameters. -type tcpTimeoutListener struct { - *net.TCPListener - - acceptDeadline time.Duration - connDeadline time.Duration - period time.Duration -} - -// timeoutConn wraps a net.Conn to standardise protocol timeouts / deadline resets. -type timeoutConn struct { - net.Conn - - connDeadline time.Duration -} - -// newTCPTimeoutListener returns an instance of tcpTimeoutListener. -func newTCPTimeoutListener( - ln net.Listener, - acceptDeadline, connDeadline time.Duration, - period time.Duration, -) tcpTimeoutListener { - return tcpTimeoutListener{ - TCPListener: ln.(*net.TCPListener), - acceptDeadline: acceptDeadline, - connDeadline: connDeadline, - period: period, - } -} - -// newTimeoutConn returns an instance of newTCPTimeoutConn. -func newTimeoutConn( - conn net.Conn, - connDeadline time.Duration) *timeoutConn { - return &timeoutConn{ - conn, - connDeadline, - } -} - -// Accept implements net.Listener. -func (ln tcpTimeoutListener) Accept() (net.Conn, error) { - err := ln.SetDeadline(time.Now().Add(ln.acceptDeadline)) - if err != nil { - return nil, err - } - - tc, err := ln.AcceptTCP() - if err != nil { - return nil, err - } - - // Wrap the conn in our timeout wrapper - conn := newTimeoutConn(tc, ln.connDeadline) - - return conn, nil -} - -// Read implements net.Listener. -func (c timeoutConn) Read(b []byte) (n int, err error) { - // Reset deadline - c.Conn.SetReadDeadline(time.Now().Add(c.connDeadline)) - - return c.Conn.Read(b) -} - -// Write implements net.Listener. -func (c timeoutConn) Write(b []byte) (n int, err error) { - // Reset deadline - c.Conn.SetWriteDeadline(time.Now().Add(c.connDeadline)) - - return c.Conn.Write(b) -} From be00cd1adde6e452cf90c5ee2c525c5e13ebdca9 Mon Sep 17 00:00:00 2001 From: Gian Felipe Date: Mon, 14 Jan 2019 06:34:29 +1100 Subject: [PATCH 21/31] Hotfix/validating query result length (#3053) * Validating that there are txs in the query results before loop throught the array * Created tests to validate the error has been fixed * Added comments * Fixing misspeling * check if the variable "skipCount" is bigger than zero. If it is not, we set it to 0. If it, we do not do anything. * using function that validates the skipCount variable * undo Gopkg.lock changes --- CHANGELOG_PENDING.md | 2 ++ rpc/client/rpc_test.go | 5 +++++ rpc/core/pipe.go | 9 +++++++++ rpc/core/tx.go | 3 ++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fd95a944..41e92255 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -33,6 +33,8 @@ Special thanks to external contributors on this release: - [rpc] \#3047 Include peer's remote IP in `/net_info` ### BUG FIXES: + - [types] \#2926 do not panic if retrieving the private validator's public key fails +- [rpc] \#3080 check if the variable "skipCount" is bigger than zero. If it is not, we set it to 0. If it, we do not do anything. - [crypto/multisig] \#3102 fix multisig keys address length - [crypto/encoding] \#3101 Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index fa5080f9..dac7ec12 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -428,5 +428,10 @@ func TestTxSearch(t *testing.T) { if len(result.Txs) == 0 { t.Fatal("expected a lot of transactions") } + + // query a non existing tx with page 1 and txsPerPage 1 + result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1) + require.Nil(t, err, "%+v", err) + require.Len(t, result.Txs, 0) } } diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 7f459654..3d745e6a 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -154,3 +154,12 @@ func validatePerPage(perPage int) int { } return perPage } + +func validateSkipCount(page, perPage int) int { + skipCount := (page - 1) * perPage + if skipCount < 0 { + return 0 + } + + return skipCount +} diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 3bb0f28e..f1bfd56a 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -201,10 +201,11 @@ func TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSear totalCount := len(results) perPage = validatePerPage(perPage) page = validatePage(page, perPage, totalCount) - skipCount := (page - 1) * perPage + skipCount := validateSkipCount(page, perPage) apiResults := make([]*ctypes.ResultTx, cmn.MinInt(perPage, totalCount-skipCount)) var proof types.TxProof + // if there's no tx in the results array, we don't need to loop through the apiResults array for i := 0; i < len(apiResults); i++ { r := results[skipCount+i] height := r.Height From 1895cde590f7abd041daa0d4751983bc9efd7ad0 Mon Sep 17 00:00:00 2001 From: Zarko Milosevic Date: Sun, 13 Jan 2019 20:47:00 +0100 Subject: [PATCH 22/31] [WIP] Fill in consensus core details in ADR 030 (#2696) * Initial work towards making ConsensusCore spec complete * Initial version of executor and complete consensus --- .../adr-030-consensus-refactor.md | 306 ++++++++++++++++++ types/evidence_test.go | 2 +- 2 files changed, 307 insertions(+), 1 deletion(-) diff --git a/docs/architecture/adr-030-consensus-refactor.md b/docs/architecture/adr-030-consensus-refactor.md index d48cfe10..5c8c3d75 100644 --- a/docs/architecture/adr-030-consensus-refactor.md +++ b/docs/architecture/adr-030-consensus-refactor.md @@ -126,6 +126,312 @@ func TestConsensusXXX(t *testing.T) { } ``` + +## Consensus Executor + +## Consensus Core + +```go +type Event interface{} + +type EventNewHeight struct { + Height int64 + ValidatorId int +} + +type EventNewRound HeightAndRound + +type EventProposal struct { + Height int64 + Round int + Timestamp Time + BlockID BlockID + POLRound int + Sender int +} + +type Majority23PrevotesBlock struct { + Height int64 + Round int + BlockID BlockID +} + +type Majority23PrecommitBlock struct { + Height int64 + Round int + BlockID BlockID +} + +type HeightAndRound struct { + Height int64 + Round int +} + +type Majority23PrevotesAny HeightAndRound +type Majority23PrecommitAny HeightAndRound +type TimeoutPropose HeightAndRound +type TimeoutPrevotes HeightAndRound +type TimeoutPrecommit HeightAndRound + + +type Message interface{} + +type MessageProposal struct { + Height int64 + Round int + BlockID BlockID + POLRound int +} + +type VoteType int + +const ( + VoteTypeUnknown VoteType = iota + Prevote + Precommit +) + + +type MessageVote struct { + Height int64 + Round int + BlockID BlockID + Type VoteType +} + + +type MessageDecision struct { + Height int64 + Round int + BlockID BlockID +} + +type TriggerTimeout struct { + Height int64 + Round int + Duration Duration +} + + +type RoundStep int + +const ( + RoundStepUnknown RoundStep = iota + RoundStepPropose + RoundStepPrevote + RoundStepPrecommit + RoundStepCommit +) + +type State struct { + Height int64 + Round int + Step RoundStep + LockedValue BlockID + LockedRound int + ValidValue BlockID + ValidRound int + ValidatorId int + ValidatorSetSize int +} + +func proposer(height int64, round int) int {} +func getValue() BlockID {} + +func Consensus(event Event, state State) (State, Message, TriggerTimeout) { + msg = nil + timeout = nil + switch event := event.(type) { + case EventNewHeight: + if event.Height > state.Height { + state.Height = event.Height + state.Round = -1 + state.Step = RoundStepPropose + state.LockedValue = nil + state.LockedRound = -1 + state.ValidValue = nil + state.ValidRound = -1 + state.ValidatorId = event.ValidatorId + } + return state, msg, timeout + + case EventNewRound: + if event.Height == state.Height and event.Round > state.Round { + state.Round = eventRound + state.Step = RoundStepPropose + if proposer(state.Height, state.Round) == state.ValidatorId { + proposal = state.ValidValue + if proposal == nil { + proposal = getValue() + } + msg = MessageProposal { state.Height, state.Round, proposal, state.ValidRound } + } + timeout = TriggerTimeout { state.Height, state.Round, timeoutPropose(state.Round) } + } + return state, msg, timeout + + case EventProposal: + if event.Height == state.Height and event.Round == state.Round and + event.Sender == proposal(state.Height, state.Round) and state.Step == RoundStepPropose { + if event.POLRound >= state.LockedRound or event.BlockID == state.BlockID or state.LockedRound == -1 { + msg = MessageVote { state.Height, state.Round, event.BlockID, Prevote } + } + state.Step = RoundStepPrevote + } + return state, msg, timeout + + case TimeoutPropose: + if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPropose { + msg = MessageVote { state.Height, state.Round, nil, Prevote } + state.Step = RoundStepPrevote + } + return state, msg, timeout + + case Majority23PrevotesBlock: + if event.Height == state.Height and event.Round == state.Round and state.Step >= RoundStepPrevote and event.Round > state.ValidRound { + state.ValidRound = event.Round + state.ValidValue = event.BlockID + if state.Step == RoundStepPrevote { + state.LockedRound = event.Round + state.LockedValue = event.BlockID + msg = MessageVote { state.Height, state.Round, event.BlockID, Precommit } + state.Step = RoundStepPrecommit + } + } + return state, msg, timeout + + case Majority23PrevotesAny: + if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPrevote { + timeout = TriggerTimeout { state.Height, state.Round, timeoutPrevote(state.Round) } + } + return state, msg, timeout + + case TimeoutPrevote: + if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPrevote { + msg = MessageVote { state.Height, state.Round, nil, Precommit } + state.Step = RoundStepPrecommit + } + return state, msg, timeout + + case Majority23PrecommitBlock: + if event.Height == state.Height { + state.Step = RoundStepCommit + state.LockedValue = event.BlockID + } + return state, msg, timeout + + case Majority23PrecommitAny: + if event.Height == state.Height and event.Round == state.Round { + timeout = TriggerTimeout { state.Height, state.Round, timeoutPrecommit(state.Round) } + } + return state, msg, timeout + + case TimeoutPrecommit: + if event.Height == state.Height and event.Round == state.Round { + state.Round = state.Round + 1 + } + return state, msg, timeout + } +} + +func ConsensusExecutor() { + proposal = nil + votes = HeightVoteSet { Height: 1 } + state = State { + Height: 1 + Round: 0 + Step: RoundStepPropose + LockedValue: nil + LockedRound: -1 + ValidValue: nil + ValidRound: -1 + } + + event = EventNewHeight {1, id} + state, msg, timeout = Consensus(event, state) + + event = EventNewRound {state.Height, 0} + state, msg, timeout = Consensus(event, state) + + if msg != nil { + send msg + } + + if timeout != nil { + trigger timeout + } + + for { + select { + case message := <- msgCh: + switch msg := message.(type) { + case MessageProposal: + + case MessageVote: + if msg.Height == state.Height { + newVote = votes.AddVote(msg) + if newVote { + switch msg.Type { + case Prevote: + prevotes = votes.Prevotes(msg.Round) + if prevotes.WeakCertificate() and msg.Round > state.Round { + event = EventNewRound { msg.Height, msg.Round } + state, msg, timeout = Consensus(event, state) + state = handleStateChange(state, msg, timeout) + } + + if blockID, ok = prevotes.TwoThirdsMajority(); ok and blockID != nil { + if msg.Round == state.Round and hasBlock(blockID) { + event = Majority23PrevotesBlock { msg.Height, msg.Round, blockID } + state, msg, timeout = Consensus(event, state) + state = handleStateChange(state, msg, timeout) + } + if proposal != nil and proposal.POLRound == msg.Round and hasBlock(blockID) { + event = EventProposal { + Height: state.Height + Round: state.Round + BlockID: blockID + POLRound: proposal.POLRound + Sender: message.Sender + } + state, msg, timeout = Consensus(event, state) + state = handleStateChange(state, msg, timeout) + } + } + + if prevotes.HasTwoThirdsAny() and msg.Round == state.Round { + event = Majority23PrevotesAny { msg.Height, msg.Round, blockID } + state, msg, timeout = Consensus(event, state) + state = handleStateChange(state, msg, timeout) + } + + case Precommit: + + } + } + } + case timeout := <- timeoutCh: + + case block := <- blockCh: + + } + } +} + +func handleStateChange(state, msg, timeout) State { + if state.Step == Commit { + state = ExecuteBlock(state.LockedValue) + } + if msg != nil { + send msg + } + if timeout != nil { + trigger timeout + } +} + +``` + ### Implementation roadmap * implement proposed implementation diff --git a/types/evidence_test.go b/types/evidence_test.go index 19427150..1f1338ca 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -62,7 +62,7 @@ func TestEvidence(t *testing.T) { {vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round {vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step {vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator - {vote1, badVote, false}, // signed by wrong key + {vote1, badVote, false}, // signed by wrong key } pubKey := val.GetPubKey() From fc031d980b9801470f8eb7e7ed52a0d49e8f3724 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 13 Jan 2019 17:15:34 -0500 Subject: [PATCH 23/31] Bucky/v0.28.0 (#3119) * changelog pending and upgrading * linkify and version bump * changelog shuffle --- CHANGELOG.md | 53 ++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 19 +--------------- UPGRADING.md | 50 +++++++++++++++++++++++++++++++++++++++++ version/version.go | 2 +- 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e9cb37..75ca299c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,58 @@ # Changelog +## v0.28.0 + +*January 14th, 2019* + +Special thanks to external contributors on this release: +@fmauricios, @gianfelipe93, @husio, @needkane, @srmo, @yutianwu + +This release is primarily about upgrades to the `privval` system - +separating the `priv_validator.json` into distinct config and data files, and +refactoring the socket validator to support reconnections. + +See [UPGRADING.md](UPGRADING.md) for more details. + +### BREAKING CHANGES: + +* CLI/RPC/Config +- [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. +- [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. +- [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false +- [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split immutable and mutable parts of `priv_validator.json` + (@yutianwu) +- [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types +- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them + +* Apps + +* Go API +- [types] [\#2981](https://github.com/tendermint/tendermint/issues/2981) Remove `PrivValidator.GetAddress()` + +* Blockchain Protocol + +* P2P Protocol + +### FEATURES: +- [rpc] [\#3052](https://github.com/tendermint/tendermint/issues/3052) Include peer's remote IP in `/net_info` + +### IMPROVEMENTS: +- [consensus] [\#3086](https://github.com/tendermint/tendermint/issues/3086) Log peerID on ignored votes (@srmo) +- [docs] [\#3061](https://github.com/tendermint/tendermint/issues/3061) Added spec on signing consensus msgs at + ./docs/spec/consensus/signing.md +- [privval] [\#2948](https://github.com/tendermint/tendermint/issues/2948) Memoize pubkey so it's only requested once on startup +- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Retry RemoteSigner connections on error + +### BUG FIXES: + +- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the private validator's public key fails +- [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty + (@gianfelipe93) +- [crypto/multisig] [\#3102](https://github.com/tendermint/tendermint/issues/3102) Fix multisig keys address length +- [crypto/encoding] [\#3101](https://github.com/tendermint/tendermint/issues/3101) Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface +- [build] [\#3085](https://github.com/tendermint/tendermint/issues/3085) Fix `Version` field in build scripts (@husio) +- [p2p/conn] [\#3111](https://github.com/tendermint/tendermint/issues/3111) Make SecretConnection thread safe + ## v0.27.4 *December 21st, 2018* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 41e92255..332cfbf7 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.28.0 +## v0.29.0 *TBD* @@ -7,34 +7,17 @@ Special thanks to external contributors on this release: ### BREAKING CHANGES: * CLI/RPC/Config -- [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. -- [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. -- [config] \#2992 `allow_duplicate_ip` is now set to false -- [privval] \#2926 split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types -- [privval] \#2923 listen for unix socket connections instead of dialing them * Apps * Go API -- [types] \#2926 memoize consensus public key on initialization of remote signer and return the memoized key on -`PrivValidator.GetPubKey()` instead of requesting it again -- [types] \#2981 Remove `PrivValidator.GetAddress()` * Blockchain Protocol * P2P Protocol ### FEATURES: -- [privval] \#1181 Split immutable and mutable parts of `priv_validator.json` ### IMPROVEMENTS: -- [p2p/conn] \#3111 make SecretConnection thread safe -- [privval] \#2923 retry RemoteSigner connections on error -- [rpc] \#3047 Include peer's remote IP in `/net_info` ### BUG FIXES: - -- [types] \#2926 do not panic if retrieving the private validator's public key fails -- [rpc] \#3080 check if the variable "skipCount" is bigger than zero. If it is not, we set it to 0. If it, we do not do anything. -- [crypto/multisig] \#3102 fix multisig keys address length -- [crypto/encoding] \#3101 Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface diff --git a/UPGRADING.md b/UPGRADING.md index 63f000f5..3e2d1f69 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,56 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. +## v0.28.0 + +This release breaks the format for the `priv_validator.json` file +and the protocol used for the external validator process. +It is compatible with v0.27.0 blockchains (neither the BlockProtocol or the +P2PProtocol have changed). + +Please read carefully for details about upgrading. + +XXX: Backup your `config/priv_validator.json` +before proceeding. + +### `priv_validator.json` + +The `config/priv_validator.json` is now two files: +`config/priv_validator_key.json` and `data/priv_validator_state.json`. +The former contains the key material, the later contains the details on the last +thing signed. + +When running v0.28.0 for the first time, it will back up any pre-existing +`priv_validator.json` file and proceed to split it into the two new files. +Upgrading should happen automatically without problem. + +To upgrade manually, use the provided `privValUpgrade.go` script, with exact paths for the old +`priv_validator.json` and the locations for the two new files. It's recomended +to use the default paths, of `config/priv_validator_key.json` and +`data/priv_validator_state.json`, respectively: + +``` +go run scripts/privValUpgrade.go +``` + +### External validator signers + +The Unix and TCP implementations of the remote signing validator +have been consolidated into a single implementation. +Thus in both cases, the external process is expected to dial +Tendermint. This is different from how Unix sockets used to work, where +Tendermint dialed the external process. + +The `PubKeyMsg` was also split into two for consistency with other message +types. + +Note that the TCP sockets don't yet use a persistent key, +so while they're encrypted, they can't yet be properly authenticated. +See [#3105](https://github.com/tendermint/tendermint/issues/3105). +Note the Unix socket has neither encryption nor authentication, but will +add a shared-secret in [#3099](https://github.com/tendermint/tendermint/issues/3099). + + ## v0.27.0 This release contains some breaking changes to the block and p2p protocols, diff --git a/version/version.go b/version/version.go index 3cbdab02..658e0e89 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.27.4" + TMCoreSemVer = "0.28.0" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From 5f93220c614c349d0018bb6728da31aa37cb24e3 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Mon, 14 Jan 2019 11:41:09 +0200 Subject: [PATCH 24/31] Adds tests for Unix sockets As per #3115, adds simple Unix socket connect/accept deadline tests in pretty much the same way as the TCP connect/accept deadline tests work. --- privval/socket_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/privval/socket_test.go b/privval/socket_test.go index 0c05fa3a..808a57ae 100644 --- a/privval/socket_test.go +++ b/privval/socket_test.go @@ -1,7 +1,9 @@ package privval import ( + "io/ioutil" "net" + "os" "testing" "time" @@ -82,3 +84,89 @@ func TestTCPListenerConnDeadline(t *testing.T) { close(readyc) <-donec } + +// testUnixAddr will attempt to obtain a platform-independent temporary file +// name for a Unix socket +func testUnixAddr() (string, error) { + f, err := ioutil.TempFile("", "tendermint-privval-test") + if err != nil { + return "", err + } + addr := f.Name() + f.Close() + os.Remove(addr) + return addr, nil +} + +func TestUnixListenerAcceptDeadline(t *testing.T) { + addr, err := testUnixAddr() + if err != nil { + t.Fatal(err) + } + ln, err := net.Listen("unix", addr) + if err != nil { + t.Fatal(err) + } + + unixLn := NewUnixListener(ln) + UnixListenerAcceptDeadline(time.Millisecond)(unixLn) + UnixListenerConnDeadline(time.Second)(unixLn) + + _, err = unixLn.Accept() + opErr, ok := err.(*net.OpError) + if !ok { + t.Fatalf("have %v, want *net.OpError", err) + } + + if have, want := opErr.Op, "accept"; have != want { + t.Errorf("have %v, want %v", have, want) + } +} + +func TestUnixListenerConnDeadline(t *testing.T) { + addr, err := testUnixAddr() + if err != nil { + t.Fatal(err) + } + ln, err := net.Listen("unix", addr) + if err != nil { + t.Fatal(err) + } + + unixLn := NewUnixListener(ln) + UnixListenerAcceptDeadline(time.Second)(unixLn) + UnixListenerConnDeadline(time.Millisecond)(unixLn) + + readyc := make(chan struct{}) + donec := make(chan struct{}) + go func(ln net.Listener) { + defer close(donec) + + c, err := ln.Accept() + if err != nil { + t.Fatal(err) + } + <-readyc + + time.Sleep(2 * time.Millisecond) + + msg := make([]byte, 200) + _, err = c.Read(msg) + opErr, ok := err.(*net.OpError) + if !ok { + t.Fatalf("have %v, want *net.OpError", err) + } + + if have, want := opErr.Op, "read"; have != want { + t.Errorf("have %v, want %v", have, want) + } + }(unixLn) + + dialer := DialUnixFn(addr) + _, err = dialer() + if err != nil { + t.Fatal(err) + } + close(readyc) + <-donec +} From ca00cd6a78be56884873257ed4bd5f61f3210d5b Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Tue, 15 Jan 2019 10:14:41 +0200 Subject: [PATCH 25/31] Make privval listener testing generic This cuts out two tests by constructing test cases and iterating through them, rather than having separate sets of tests for TCP and Unix listeners. This is as per the feedback from #3121. --- privval/socket_test.go | 189 ++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 114 deletions(-) diff --git a/privval/socket_test.go b/privval/socket_test.go index 808a57ae..88d9ef8e 100644 --- a/privval/socket_test.go +++ b/privval/socket_test.go @@ -20,74 +20,15 @@ func newPrivKey() ed25519.PrivKeyEd25519 { //------------------------------------------- // tests -func TestTCPListenerAcceptDeadline(t *testing.T) { - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - tcpLn := NewTCPListener(ln, newPrivKey()) - TCPListenerAcceptDeadline(time.Millisecond)(tcpLn) - TCPListenerConnDeadline(time.Second)(tcpLn) - - _, err = tcpLn.Accept() - opErr, ok := err.(*net.OpError) - if !ok { - t.Fatalf("have %v, want *net.OpError", err) - } - - if have, want := opErr.Op, "accept"; have != want { - t.Errorf("have %v, want %v", have, want) - } +type listenerTestCase struct { + description string // For test reporting purposes. + listener net.Listener + dialer Dialer } -func TestTCPListenerConnDeadline(t *testing.T) { - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - - tcpLn := NewTCPListener(ln, newPrivKey()) - TCPListenerAcceptDeadline(time.Second)(tcpLn) - TCPListenerConnDeadline(time.Millisecond)(tcpLn) - - readyc := make(chan struct{}) - donec := make(chan struct{}) - go func(ln net.Listener) { - defer close(donec) - - c, err := ln.Accept() - if err != nil { - t.Fatal(err) - } - <-readyc - - time.Sleep(2 * time.Millisecond) - - msg := make([]byte, 200) - _, err = c.Read(msg) - opErr, ok := err.(*net.OpError) - if !ok { - t.Fatalf("have %v, want *net.OpError", err) - } - - if have, want := opErr.Op, "read"; have != want { - t.Errorf("have %v, want %v", have, want) - } - }(tcpLn) - - dialer := DialTCPFn(ln.Addr().String(), testConnDeadline, newPrivKey()) - _, err = dialer() - if err != nil { - t.Fatal(err) - } - close(readyc) - <-donec -} - -// testUnixAddr will attempt to obtain a platform-independent temporary file +// getTestUnixAddr will attempt to obtain a platform-independent temporary file // name for a Unix socket -func testUnixAddr() (string, error) { +func getTestUnixAddr() (string, error) { f, err := ioutil.TempFile("", "tendermint-privval-test") if err != nil { return "", err @@ -98,33 +39,24 @@ func testUnixAddr() (string, error) { return addr, nil } -func TestUnixListenerAcceptDeadline(t *testing.T) { - addr, err := testUnixAddr() - if err != nil { - t.Fatal(err) - } - ln, err := net.Listen("unix", addr) +func constructTCPListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { + ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } - unixLn := NewUnixListener(ln) - UnixListenerAcceptDeadline(time.Millisecond)(unixLn) - UnixListenerConnDeadline(time.Second)(unixLn) - - _, err = unixLn.Accept() - opErr, ok := err.(*net.OpError) - if !ok { - t.Fatalf("have %v, want *net.OpError", err) - } - - if have, want := opErr.Op, "accept"; have != want { - t.Errorf("have %v, want %v", have, want) + tcpLn := NewTCPListener(ln, newPrivKey()) + TCPListenerAcceptDeadline(acceptDeadline)(tcpLn) + TCPListenerConnDeadline(connectDeadline)(tcpLn) + return listenerTestCase{ + description: "TCP", + listener: tcpLn, + dialer: DialTCPFn(ln.Addr().String(), testConnDeadline, newPrivKey()), } } -func TestUnixListenerConnDeadline(t *testing.T) { - addr, err := testUnixAddr() +func constructUnixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { + addr, err := getTestUnixAddr() if err != nil { t.Fatal(err) } @@ -134,39 +66,68 @@ func TestUnixListenerConnDeadline(t *testing.T) { } unixLn := NewUnixListener(ln) - UnixListenerAcceptDeadline(time.Second)(unixLn) - UnixListenerConnDeadline(time.Millisecond)(unixLn) + UnixListenerAcceptDeadline(acceptDeadline)(unixLn) + UnixListenerConnDeadline(connectDeadline)(unixLn) + return listenerTestCase{ + description: "Unix", + listener: unixLn, + dialer: DialUnixFn(addr), + } +} - readyc := make(chan struct{}) - donec := make(chan struct{}) - go func(ln net.Listener) { - defer close(donec) +func constructListenerTestCases(t *testing.T, acceptDeadline, connectDeadline time.Duration) []listenerTestCase { + return []listenerTestCase{ + constructTCPListenerTestCase(t, acceptDeadline, connectDeadline), + constructUnixListenerTestCase(t, acceptDeadline, connectDeadline), + } +} - c, err := ln.Accept() +func TestListenerAcceptDeadlines(t *testing.T) { + for _, tc := range constructListenerTestCases(t, time.Millisecond, time.Second) { + _, err := tc.listener.Accept() + opErr, ok := err.(*net.OpError) + if !ok { + t.Fatalf("for %s listener, have %v, want *net.OpError", tc.description, err) + } + + if have, want := opErr.Op, "accept"; have != want { + t.Errorf("for %s listener, have %v, want %v", tc.description, have, want) + } + } +} + +func TestListenerConnectDeadlines(t *testing.T) { + for _, tc := range constructListenerTestCases(t, time.Second, time.Millisecond) { + readyc := make(chan struct{}) + donec := make(chan struct{}) + go func(ln net.Listener) { + defer close(donec) + + c, err := ln.Accept() + if err != nil { + t.Fatal(err) + } + <-readyc + + time.Sleep(2 * time.Millisecond) + + msg := make([]byte, 200) + _, err = c.Read(msg) + opErr, ok := err.(*net.OpError) + if !ok { + t.Fatalf("for %s listener, have %v, want *net.OpError", tc.description, err) + } + + if have, want := opErr.Op, "read"; have != want { + t.Errorf("for %s listener, have %v, want %v", tc.description, have, want) + } + }(tc.listener) + + _, err := tc.dialer() if err != nil { t.Fatal(err) } - <-readyc - - time.Sleep(2 * time.Millisecond) - - msg := make([]byte, 200) - _, err = c.Read(msg) - opErr, ok := err.(*net.OpError) - if !ok { - t.Fatalf("have %v, want *net.OpError", err) - } - - if have, want := opErr.Op, "read"; have != want { - t.Errorf("have %v, want %v", have, want) - } - }(unixLn) - - dialer := DialUnixFn(addr) - _, err = dialer() - if err != nil { - t.Fatal(err) + close(readyc) + <-donec } - close(readyc) - <-donec } From d1afa0ed6cf3d13be9ddbaf1dc60e2d46a2acf53 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 15 Jan 2019 07:55:57 -0500 Subject: [PATCH 26/31] privval: fixes from review (#3126) https://github.com/tendermint/tendermint/pull/2923#pullrequestreview-192065694 --- cmd/priv_val_server/main.go | 2 +- node/node.go | 2 +- privval/client.go | 35 +++++------------------------------ 3 files changed, 7 insertions(+), 32 deletions(-) diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 6949e878..768b9cf6 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -45,7 +45,7 @@ func main() { dialer = privval.DialTCPFn(address, connTimeout, ed25519.GenPrivKey()) default: logger.Error("Unknown protocol", "protocol", protocol) - return + os.Exit(1) } rs := privval.NewRemoteSigner(logger, *chainID, pv, dialer) diff --git a/node/node.go b/node/node.go index b7998dac..128714cb 100644 --- a/node/node.go +++ b/node/node.go @@ -901,7 +901,7 @@ func createAndStartPrivValidatorSocketClient( pvsc := privval.NewSocketVal(logger.With("module", "privval"), listener) if err := pvsc.Start(); err != nil { - return nil, errors.Wrap(err, "failed to start") + return nil, errors.Wrap(err, "failed to start private validator") } return pvsc, nil diff --git a/privval/client.go b/privval/client.go index 4d4395fd..1ad104d8 100644 --- a/privval/client.go +++ b/privval/client.go @@ -191,19 +191,19 @@ func (sc *SocketVal) OnStop() { // connection is closed in OnStop. // returns true if the listener is closed // (ie. it returns a nil conn). -func (sc *SocketVal) reset() (bool, error) { +func (sc *SocketVal) reset() (closed bool, err error) { sc.mtx.Lock() defer sc.mtx.Unlock() // first check if the conn already exists and close it. if sc.signer != nil { if err := sc.signer.Close(); err != nil { - sc.Logger.Error("error closing connection", "err", err) + sc.Logger.Error("error closing socket val connection during reset", "err", err) } } // wait for a new conn - conn, err := sc.waitConnection() + conn, err := sc.acceptConnection() if err != nil { return false, err } @@ -224,6 +224,8 @@ func (sc *SocketVal) reset() (bool, error) { return false, nil } +// Attempt to accept a connection. +// Times out after the listener's acceptDeadline func (sc *SocketVal) acceptConnection() (net.Conn, error) { conn, err := sc.listener.Accept() if err != nil { @@ -231,33 +233,6 @@ func (sc *SocketVal) acceptConnection() (net.Conn, error) { return nil, nil // Ignore error from listener closing. } return nil, err - } return conn, nil } - -// waitConnection uses the configured wait timeout to error if no external -// process connects in the time period. -func (sc *SocketVal) waitConnection() (net.Conn, error) { - var ( - connc = make(chan net.Conn, 1) - errc = make(chan error, 1) - ) - - go func(connc chan<- net.Conn, errc chan<- error) { - conn, err := sc.acceptConnection() - if err != nil { - errc <- err - return - } - - connc <- conn - }(connc, errc) - - select { - case conn := <-connc: - return conn, nil - case err := <-errc: - return nil, err - } -} From 3191ee8badde5a815da252dbcd77b641dd417521 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Tue, 15 Jan 2019 18:06:57 +0200 Subject: [PATCH 27/31] Dropping "construct" prefix as per #3121 --- privval/socket_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/privval/socket_test.go b/privval/socket_test.go index 88d9ef8e..dfce45b5 100644 --- a/privval/socket_test.go +++ b/privval/socket_test.go @@ -26,9 +26,9 @@ type listenerTestCase struct { dialer Dialer } -// getTestUnixAddr will attempt to obtain a platform-independent temporary file +// testUnixAddr will attempt to obtain a platform-independent temporary file // name for a Unix socket -func getTestUnixAddr() (string, error) { +func testUnixAddr() (string, error) { f, err := ioutil.TempFile("", "tendermint-privval-test") if err != nil { return "", err @@ -39,7 +39,7 @@ func getTestUnixAddr() (string, error) { return addr, nil } -func constructTCPListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { +func tcpListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) @@ -55,8 +55,8 @@ func constructTCPListenerTestCase(t *testing.T, acceptDeadline, connectDeadline } } -func constructUnixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { - addr, err := getTestUnixAddr() +func unixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { + addr, err := testUnixAddr() if err != nil { t.Fatal(err) } @@ -75,15 +75,15 @@ func constructUnixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline } } -func constructListenerTestCases(t *testing.T, acceptDeadline, connectDeadline time.Duration) []listenerTestCase { +func listenerTestCases(t *testing.T, acceptDeadline, connectDeadline time.Duration) []listenerTestCase { return []listenerTestCase{ - constructTCPListenerTestCase(t, acceptDeadline, connectDeadline), - constructUnixListenerTestCase(t, acceptDeadline, connectDeadline), + tcpListenerTestCase(t, acceptDeadline, connectDeadline), + unixListenerTestCase(t, acceptDeadline, connectDeadline), } } func TestListenerAcceptDeadlines(t *testing.T) { - for _, tc := range constructListenerTestCases(t, time.Millisecond, time.Second) { + for _, tc := range listenerTestCases(t, time.Millisecond, time.Second) { _, err := tc.listener.Accept() opErr, ok := err.(*net.OpError) if !ok { @@ -97,7 +97,7 @@ func TestListenerAcceptDeadlines(t *testing.T) { } func TestListenerConnectDeadlines(t *testing.T) { - for _, tc := range constructListenerTestCases(t, time.Second, time.Millisecond) { + for _, tc := range listenerTestCases(t, time.Second, time.Millisecond) { readyc := make(chan struct{}) donec := make(chan struct{}) go func(ln net.Listener) { From d4e67205411c15af756e35eed2240944aa3958b7 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Wed, 16 Jan 2019 17:05:34 +0200 Subject: [PATCH 28/31] Expanding tests to cover Unix sockets version of client (#3132) * Adds a random suffix to temporary Unix sockets during testing * Adds Unix domain socket tests for client (FAILING) This adds Unix domain socket tests for the privval client. Right now, one of the tests (TestRemoteSignerRetry) fails, probably because the Unix domain socket state is known instantaneously on both sides by the OS. Committing this to collaborate on the error. * Removes extraneous logging * Completes testing of Unix sockets client version This completes the testing of the client connecting via Unix sockets. There are two specific tests (TestSocketPVDeadline and TestRemoteSignerRetryTCPOnly) that are only relevant to TCP connections. * Renames test to show TCP-specificity * Adds testing into closures for consistency (forgot previously) * Moves test specific to RemoteSigner into own file As per discussion on #3132, `TestRemoteSignerRetryTCPOnly` doesn't really belong with the client tests. This moves it into its own file related to the `RemoteSigner` class. --- privval/client_test.go | 533 ++++++++++++++++++---------------- privval/remote_signer_test.go | 68 +++++ privval/socket.go | 2 +- privval/socket_test.go | 2 +- 4 files changed, 350 insertions(+), 255 deletions(-) create mode 100644 privval/remote_signer_test.go diff --git a/privval/client_test.go b/privval/client_test.go index 7fae6bf8..3c327064 100644 --- a/privval/client_test.go +++ b/privval/client_test.go @@ -27,120 +27,170 @@ var ( testHeartbeatTimeout3o2 = 6 * time.Millisecond // 3/2 of the other one ) +type socketTestCase struct { + addr string + dialer Dialer +} + +func socketTestCases(t *testing.T) []socketTestCase { + tcpAddr := fmt.Sprintf("tcp://%s", testFreeTCPAddr(t)) + unixFilePath, err := testUnixAddr() + require.NoError(t, err) + unixAddr := fmt.Sprintf("unix://%s", unixFilePath) + return []socketTestCase{ + socketTestCase{ + addr: tcpAddr, + dialer: DialTCPFn(tcpAddr, testConnDeadline, ed25519.GenPrivKey()), + }, + socketTestCase{ + addr: unixAddr, + dialer: DialUnixFn(unixFilePath), + }, + } +} + func TestSocketPVAddress(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) - ) - defer sc.Stop() - defer rs.Stop() + for _, tc := range socketTestCases(t) { + // Execute the test within a closure to ensure the deferred statements + // are called between each for loop iteration, for isolated test cases. + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) + ) + defer sc.Stop() + defer rs.Stop() - serverAddr := rs.privVal.GetPubKey().Address() - clientAddr := sc.GetPubKey().Address() + serverAddr := rs.privVal.GetPubKey().Address() + clientAddr := sc.GetPubKey().Address() - assert.Equal(t, serverAddr, clientAddr) + assert.Equal(t, serverAddr, clientAddr) + }() + } } func TestSocketPVPubKey(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) - ) - defer sc.Stop() - defer rs.Stop() + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) + ) + defer sc.Stop() + defer rs.Stop() - clientKey := sc.GetPubKey() + clientKey := sc.GetPubKey() - privvalPubKey := rs.privVal.GetPubKey() + privvalPubKey := rs.privVal.GetPubKey() - assert.Equal(t, privvalPubKey, clientKey) + assert.Equal(t, privvalPubKey, clientKey) + }() + } } func TestSocketPVProposal(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - ts = time.Now() - privProposal = &types.Proposal{Timestamp: ts} - clientProposal = &types.Proposal{Timestamp: ts} - ) - defer sc.Stop() - defer rs.Stop() + ts = time.Now() + privProposal = &types.Proposal{Timestamp: ts} + clientProposal = &types.Proposal{Timestamp: ts} + ) + defer sc.Stop() + defer rs.Stop() - require.NoError(t, rs.privVal.SignProposal(chainID, privProposal)) - require.NoError(t, sc.SignProposal(chainID, clientProposal)) - assert.Equal(t, privProposal.Signature, clientProposal.Signature) + require.NoError(t, rs.privVal.SignProposal(chainID, privProposal)) + require.NoError(t, sc.SignProposal(chainID, clientProposal)) + assert.Equal(t, privProposal.Signature, clientProposal.Signature) + }() + } } func TestSocketPVVote(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() + ts = time.Now() + vType = types.PrecommitType + want = &types.Vote{Timestamp: ts, Type: vType} + have = &types.Vote{Timestamp: ts, Type: vType} + ) + defer sc.Stop() + defer rs.Stop() - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) + require.NoError(t, rs.privVal.SignVote(chainID, want)) + require.NoError(t, sc.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + }() + } } func TestSocketPVVoteResetDeadline(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() + ts = time.Now() + vType = types.PrecommitType + want = &types.Vote{Timestamp: ts, Type: vType} + have = &types.Vote{Timestamp: ts, Type: vType} + ) + defer sc.Stop() + defer rs.Stop() - time.Sleep(testConnDeadline2o3) + time.Sleep(testConnDeadline2o3) - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) + require.NoError(t, rs.privVal.SignVote(chainID, want)) + require.NoError(t, sc.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) - // This would exceed the deadline if it was not extended by the previous message - time.Sleep(testConnDeadline2o3) + // This would exceed the deadline if it was not extended by the previous message + time.Sleep(testConnDeadline2o3) - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) + require.NoError(t, rs.privVal.SignVote(chainID, want)) + require.NoError(t, sc.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + }() + } } func TestSocketPVVoteKeepalive(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() + ts = time.Now() + vType = types.PrecommitType + want = &types.Vote{Timestamp: ts, Type: vType} + have = &types.Vote{Timestamp: ts, Type: vType} + ) + defer sc.Stop() + defer rs.Stop() - time.Sleep(testConnDeadline * 2) + time.Sleep(testConnDeadline * 2) - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) + require.NoError(t, rs.privVal.SignVote(chainID, want)) + require.NoError(t, sc.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + }() + } } -func TestSocketPVDeadline(t *testing.T) { +// TestSocketPVDeadlineTCPOnly is not relevant to Unix domain sockets, since the +// OS knows instantaneously the state of both sides of the connection. +func TestSocketPVDeadlineTCPOnly(t *testing.T) { var ( - addr = testFreeAddr(t) + addr = testFreeTCPAddr(t) listenc = make(chan struct{}) thisConnTimeout = 100 * time.Millisecond sc = newSocketVal(log.TestingLogger(), addr, thisConnTimeout) @@ -172,218 +222,195 @@ func TestSocketPVDeadline(t *testing.T) { <-listenc } -func TestRemoteSignerRetry(t *testing.T) { - var ( - attemptc = make(chan int) - retries = 2 - ) - - ln, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - - go func(ln net.Listener, attemptc chan<- int) { - attempts := 0 - - for { - conn, err := ln.Accept() - require.NoError(t, err) - - err = conn.Close() - require.NoError(t, err) - - attempts++ - - if attempts == retries { - attemptc <- attempts - break - } - } - }(ln, attemptc) - - rs := NewRemoteSigner( - log.TestingLogger(), - cmn.RandStr(12), - types.NewMockPV(), - DialTCPFn(ln.Addr().String(), testConnDeadline, ed25519.GenPrivKey()), - ) - defer rs.Stop() - - RemoteSignerConnDeadline(time.Millisecond)(rs) - RemoteSignerConnRetries(retries)(rs) - - assert.Equal(t, rs.Start(), ErrDialRetryMax) - - select { - case attempts := <-attemptc: - assert.Equal(t, retries, attempts) - case <-time.After(100 * time.Millisecond): - t.Error("expected remote to observe connection attempts") - } -} - func TestRemoteSignVoteErrors(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV()) + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV(), tc.addr, tc.dialer) - ts = time.Now() - vType = types.PrecommitType - vote = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() + ts = time.Now() + vType = types.PrecommitType + vote = &types.Vote{Timestamp: ts, Type: vType} + ) + defer sc.Stop() + defer rs.Stop() - err := sc.SignVote("", vote) - require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) + err := sc.SignVote("", vote) + require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) - err = rs.privVal.SignVote(chainID, vote) - require.Error(t, err) - err = sc.SignVote(chainID, vote) - require.Error(t, err) + err = rs.privVal.SignVote(chainID, vote) + require.Error(t, err) + err = sc.SignVote(chainID, vote) + require.Error(t, err) + }() + } } func TestRemoteSignProposalErrors(t *testing.T) { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV()) + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV(), tc.addr, tc.dialer) - ts = time.Now() - proposal = &types.Proposal{Timestamp: ts} - ) - defer sc.Stop() - defer rs.Stop() + ts = time.Now() + proposal = &types.Proposal{Timestamp: ts} + ) + defer sc.Stop() + defer rs.Stop() - err := sc.SignProposal("", proposal) - require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) + err := sc.SignProposal("", proposal) + require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) - err = rs.privVal.SignProposal(chainID, proposal) - require.Error(t, err) + err = rs.privVal.SignProposal(chainID, proposal) + require.Error(t, err) - err = sc.SignProposal(chainID, proposal) - require.Error(t, err) + err = sc.SignProposal(chainID, proposal) + require.Error(t, err) + }() + } } func TestErrUnexpectedResponse(t *testing.T) { - var ( - addr = testFreeAddr(t) - logger = log.TestingLogger() - chainID = cmn.RandStr(12) - readyc = make(chan struct{}) - errc = make(chan error, 1) + for _, tc := range socketTestCases(t) { + func() { + var ( + logger = log.TestingLogger() + chainID = cmn.RandStr(12) + readyc = make(chan struct{}) + errc = make(chan error, 1) - rs = NewRemoteSigner( - logger, - chainID, - types.NewMockPV(), - DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), - ) - sc = newSocketVal(logger, addr, testConnDeadline) - ) + rs = NewRemoteSigner( + logger, + chainID, + types.NewMockPV(), + tc.dialer, + ) + sc = newSocketVal(logger, tc.addr, testConnDeadline) + ) - testStartSocketPV(t, readyc, sc) - defer sc.Stop() - RemoteSignerConnDeadline(time.Millisecond)(rs) - RemoteSignerConnRetries(100)(rs) - // we do not want to Start() the remote signer here and instead use the connection to - // reply with intentionally wrong replies below: - rsConn, err := rs.connect() - defer rsConn.Close() - require.NoError(t, err) - require.NotNil(t, rsConn) - // send over public key to get the remote signer running: - go testReadWriteResponse(t, &PubKeyResponse{}, rsConn) - <-readyc + testStartSocketPV(t, readyc, sc) + defer sc.Stop() + RemoteSignerConnDeadline(time.Millisecond)(rs) + RemoteSignerConnRetries(100)(rs) + // we do not want to Start() the remote signer here and instead use the connection to + // reply with intentionally wrong replies below: + rsConn, err := rs.connect() + defer rsConn.Close() + require.NoError(t, err) + require.NotNil(t, rsConn) + // send over public key to get the remote signer running: + go testReadWriteResponse(t, &PubKeyResponse{}, rsConn) + <-readyc - // Proposal: - go func(errc chan error) { - errc <- sc.SignProposal(chainID, &types.Proposal{}) - }(errc) - // read request and write wrong response: - go testReadWriteResponse(t, &SignedVoteResponse{}, rsConn) - err = <-errc - require.Error(t, err) - require.Equal(t, err, ErrUnexpectedResponse) + // Proposal: + go func(errc chan error) { + errc <- sc.SignProposal(chainID, &types.Proposal{}) + }(errc) + // read request and write wrong response: + go testReadWriteResponse(t, &SignedVoteResponse{}, rsConn) + err = <-errc + require.Error(t, err) + require.Equal(t, err, ErrUnexpectedResponse) - // Vote: - go func(errc chan error) { - errc <- sc.SignVote(chainID, &types.Vote{}) - }(errc) - // read request and write wrong response: - go testReadWriteResponse(t, &SignedProposalResponse{}, rsConn) - err = <-errc - require.Error(t, err) - require.Equal(t, err, ErrUnexpectedResponse) + // Vote: + go func(errc chan error) { + errc <- sc.SignVote(chainID, &types.Vote{}) + }(errc) + // read request and write wrong response: + go testReadWriteResponse(t, &SignedProposalResponse{}, rsConn) + err = <-errc + require.Error(t, err) + require.Equal(t, err, ErrUnexpectedResponse) + }() + } } -func TestRetryTCPConnToRemoteSigner(t *testing.T) { - var ( - addr = testFreeAddr(t) - logger = log.TestingLogger() - chainID = cmn.RandStr(12) - readyc = make(chan struct{}) +func TestRetryConnToRemoteSigner(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + logger = log.TestingLogger() + chainID = cmn.RandStr(12) + readyc = make(chan struct{}) - rs = NewRemoteSigner( - logger, - chainID, - types.NewMockPV(), - DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), - ) - thisConnTimeout = testConnDeadline - sc = newSocketVal(logger, addr, thisConnTimeout) - ) - // Ping every: - SocketValHeartbeat(testHeartbeatTimeout)(sc) + rs = NewRemoteSigner( + logger, + chainID, + types.NewMockPV(), + tc.dialer, + ) + thisConnTimeout = testConnDeadline + sc = newSocketVal(logger, tc.addr, thisConnTimeout) + ) + // Ping every: + SocketValHeartbeat(testHeartbeatTimeout)(sc) - RemoteSignerConnDeadline(testConnDeadline)(rs) - RemoteSignerConnRetries(10)(rs) + RemoteSignerConnDeadline(testConnDeadline)(rs) + RemoteSignerConnRetries(10)(rs) - testStartSocketPV(t, readyc, sc) - defer sc.Stop() - require.NoError(t, rs.Start()) - assert.True(t, rs.IsRunning()) + testStartSocketPV(t, readyc, sc) + defer sc.Stop() + require.NoError(t, rs.Start()) + assert.True(t, rs.IsRunning()) - <-readyc - time.Sleep(testHeartbeatTimeout * 2) + <-readyc + time.Sleep(testHeartbeatTimeout * 2) - rs.Stop() - rs2 := NewRemoteSigner( - logger, - chainID, - types.NewMockPV(), - DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), - ) - // let some pings pass - time.Sleep(testHeartbeatTimeout3o2) - require.NoError(t, rs2.Start()) - assert.True(t, rs2.IsRunning()) - defer rs2.Stop() + rs.Stop() + rs2 := NewRemoteSigner( + logger, + chainID, + types.NewMockPV(), + tc.dialer, + ) + // let some pings pass + time.Sleep(testHeartbeatTimeout3o2) + require.NoError(t, rs2.Start()) + assert.True(t, rs2.IsRunning()) + defer rs2.Stop() - // give the client some time to re-establish the conn to the remote signer - // should see sth like this in the logs: - // - // E[10016-01-10|17:12:46.128] Ping err="remote signer timed out" - // I[10016-01-10|17:16:42.447] Re-created connection to remote signer impl=SocketVal - time.Sleep(testConnDeadline * 2) + // give the client some time to re-establish the conn to the remote signer + // should see sth like this in the logs: + // + // E[10016-01-10|17:12:46.128] Ping err="remote signer timed out" + // I[10016-01-10|17:16:42.447] Re-created connection to remote signer impl=SocketVal + time.Sleep(testConnDeadline * 2) + }() + } } func newSocketVal(logger log.Logger, addr string, connDeadline time.Duration) *SocketVal { - ln, err := net.Listen(cmn.ProtocolAndAddress(addr)) + proto, address := cmn.ProtocolAndAddress(addr) + ln, err := net.Listen(proto, address) + logger.Info("Listening at", "proto", proto, "address", address) if err != nil { panic(err) } - tcpLn := NewTCPListener(ln, ed25519.GenPrivKey()) - TCPListenerAcceptDeadline(testAcceptDeadline)(tcpLn) - TCPListenerConnDeadline(testConnDeadline)(tcpLn) - return NewSocketVal(logger, tcpLn) + var svln net.Listener + if proto == "unix" { + unixLn := NewUnixListener(ln) + UnixListenerAcceptDeadline(testAcceptDeadline)(unixLn) + UnixListenerConnDeadline(connDeadline)(unixLn) + svln = unixLn + } else { + tcpLn := NewTCPListener(ln, ed25519.GenPrivKey()) + TCPListenerAcceptDeadline(testAcceptDeadline)(tcpLn) + TCPListenerConnDeadline(connDeadline)(tcpLn) + svln = tcpLn + } + return NewSocketVal(logger, svln) } func testSetupSocketPair( t *testing.T, chainID string, privValidator types.PrivValidator, + addr string, + dialer Dialer, ) (*SocketVal, *RemoteSigner) { var ( - addr = testFreeAddr(t) logger = log.TestingLogger() privVal = privValidator readyc = make(chan struct{}) @@ -391,7 +418,7 @@ func testSetupSocketPair( logger, chainID, privVal, - DialTCPFn(addr, testConnDeadline, ed25519.GenPrivKey()), + dialer, ) thisConnTimeout = testConnDeadline @@ -429,8 +456,8 @@ func testStartSocketPV(t *testing.T, readyc chan struct{}, sc *SocketVal) { }(sc) } -// testFreeAddr claims a free port so we don't block on listener being ready. -func testFreeAddr(t *testing.T) string { +// testFreeTCPAddr claims a free port so we don't block on listener being ready. +func testFreeTCPAddr(t *testing.T) string { ln, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) defer ln.Close() diff --git a/privval/remote_signer_test.go b/privval/remote_signer_test.go new file mode 100644 index 00000000..8927e224 --- /dev/null +++ b/privval/remote_signer_test.go @@ -0,0 +1,68 @@ +package privval + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" +) + +// TestRemoteSignerRetryTCPOnly will test connection retry attempts over TCP. We +// don't need this for Unix sockets because the OS instantly knows the state of +// both ends of the socket connection. This basically causes the +// RemoteSigner.dialer() call inside RemoteSigner.connect() to return +// successfully immediately, putting an instant stop to any retry attempts. +func TestRemoteSignerRetryTCPOnly(t *testing.T) { + var ( + attemptc = make(chan int) + retries = 2 + ) + + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + + go func(ln net.Listener, attemptc chan<- int) { + attempts := 0 + + for { + conn, err := ln.Accept() + require.NoError(t, err) + + err = conn.Close() + require.NoError(t, err) + + attempts++ + + if attempts == retries { + attemptc <- attempts + break + } + } + }(ln, attemptc) + + rs := NewRemoteSigner( + log.TestingLogger(), + cmn.RandStr(12), + types.NewMockPV(), + DialTCPFn(ln.Addr().String(), testConnDeadline, ed25519.GenPrivKey()), + ) + defer rs.Stop() + + RemoteSignerConnDeadline(time.Millisecond)(rs) + RemoteSignerConnRetries(retries)(rs) + + assert.Equal(t, rs.Start(), ErrDialRetryMax) + + select { + case attempts := <-attemptc: + assert.Equal(t, retries, attempts) + case <-time.After(100 * time.Millisecond): + t.Error("expected remote to observe connection attempts") + } +} diff --git a/privval/socket.go b/privval/socket.go index 96fa6c8e..bd9cd920 100644 --- a/privval/socket.go +++ b/privval/socket.go @@ -157,7 +157,7 @@ type timeoutConn struct { connDeadline time.Duration } -// newTimeoutConn returns an instance of newTCPTimeoutConn. +// newTimeoutConn returns an instance of timeoutConn. func newTimeoutConn( conn net.Conn, connDeadline time.Duration) *timeoutConn { diff --git a/privval/socket_test.go b/privval/socket_test.go index dfce45b5..b411b7f3 100644 --- a/privval/socket_test.go +++ b/privval/socket_test.go @@ -29,7 +29,7 @@ type listenerTestCase struct { // testUnixAddr will attempt to obtain a platform-independent temporary file // name for a Unix socket func testUnixAddr() (string, error) { - f, err := ioutil.TempFile("", "tendermint-privval-test") + f, err := ioutil.TempFile("", "tendermint-privval-test-*") if err != nil { return "", err } From 0cba0e11b5569f7e0d8b61db3c2ce5416ce2753e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 16 Jan 2019 10:16:23 -0500 Subject: [PATCH 29/31] update changelog and upgrading (#3133) --- CHANGELOG.md | 22 ++++++++++++---------- UPGRADING.md | 9 ++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75ca299c..4fa0ff5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.28.0 -*January 14th, 2019* +*January 16th, 2019* Special thanks to external contributors on this release: @fmauricios, @gianfelipe93, @husio, @needkane, @srmo, @yutianwu @@ -11,16 +11,18 @@ This release is primarily about upgrades to the `privval` system - separating the `priv_validator.json` into distinct config and data files, and refactoring the socket validator to support reconnections. +XXX: Please backup your existing `priv_validator.json` before using this +version. + See [UPGRADING.md](UPGRADING.md) for more details. ### BREAKING CHANGES: * CLI/RPC/Config -- [cli] Removed `node` `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. -- [cli] Renamed `node` `--proxy_app=nilapp` to `--proxy_app=noop`. +- [cli] Removed `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. +- [cli] Renamed `--proxy_app=nilapp` to `--proxy_app=noop`. - [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false -- [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split immutable and mutable parts of `priv_validator.json` - (@yutianwu) +- [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split `priv_validator.json` into immutable (`config/priv_validator_key.json`) and mutable (`data/priv_validator_state.json`) parts (@yutianwu) - [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types - [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them @@ -38,20 +40,20 @@ See [UPGRADING.md](UPGRADING.md) for more details. ### IMPROVEMENTS: - [consensus] [\#3086](https://github.com/tendermint/tendermint/issues/3086) Log peerID on ignored votes (@srmo) -- [docs] [\#3061](https://github.com/tendermint/tendermint/issues/3061) Added spec on signing consensus msgs at +- [docs] [\#3061](https://github.com/tendermint/tendermint/issues/3061) Added specification for signing consensus msgs at ./docs/spec/consensus/signing.md - [privval] [\#2948](https://github.com/tendermint/tendermint/issues/2948) Memoize pubkey so it's only requested once on startup - [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Retry RemoteSigner connections on error ### BUG FIXES: -- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the private validator's public key fails -- [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty - (@gianfelipe93) +- [build] [\#3085](https://github.com/tendermint/tendermint/issues/3085) Fix `Version` field in build scripts (@husio) - [crypto/multisig] [\#3102](https://github.com/tendermint/tendermint/issues/3102) Fix multisig keys address length - [crypto/encoding] [\#3101](https://github.com/tendermint/tendermint/issues/3101) Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface -- [build] [\#3085](https://github.com/tendermint/tendermint/issues/3085) Fix `Version` field in build scripts (@husio) - [p2p/conn] [\#3111](https://github.com/tendermint/tendermint/issues/3111) Make SecretConnection thread safe +- [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty + (@gianfelipe93) +- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the private validator's public key fails ## v0.27.4 diff --git a/UPGRADING.md b/UPGRADING.md index 3e2d1f69..df950043 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -7,7 +7,7 @@ a newer version of Tendermint Core. This release breaks the format for the `priv_validator.json` file and the protocol used for the external validator process. -It is compatible with v0.27.0 blockchains (neither the BlockProtocol or the +It is compatible with v0.27.0 blockchains (neither the BlockProtocol nor the P2PProtocol have changed). Please read carefully for details about upgrading. @@ -20,7 +20,7 @@ before proceeding. The `config/priv_validator.json` is now two files: `config/priv_validator_key.json` and `data/priv_validator_state.json`. The former contains the key material, the later contains the details on the last -thing signed. +message signed. When running v0.28.0 for the first time, it will back up any pre-existing `priv_validator.json` file and proceed to split it into the two new files. @@ -43,8 +43,8 @@ Thus in both cases, the external process is expected to dial Tendermint. This is different from how Unix sockets used to work, where Tendermint dialed the external process. -The `PubKeyMsg` was also split into two for consistency with other message -types. +The `PubKeyMsg` was also split into separate `Request` and `Response` types +for consistency with other messages. Note that the TCP sockets don't yet use a persistent key, so while they're encrypted, they can't yet be properly authenticated. @@ -52,7 +52,6 @@ See [#3105](https://github.com/tendermint/tendermint/issues/3105). Note the Unix socket has neither encryption nor authentication, but will add a shared-secret in [#3099](https://github.com/tendermint/tendermint/issues/3099). - ## v0.27.0 This release contains some breaking changes to the block and p2p protocols, From 239ebe20769b79f46a2ddca88de78e8044543e70 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 16 Jan 2019 10:21:15 -0500 Subject: [PATCH 30/31] fix changelog fmt (#3134) --- CHANGELOG.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fa0ff5d..ed590005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,17 +19,17 @@ See [UPGRADING.md](UPGRADING.md) for more details. ### BREAKING CHANGES: * CLI/RPC/Config -- [cli] Removed `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. -- [cli] Renamed `--proxy_app=nilapp` to `--proxy_app=noop`. -- [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false -- [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split `priv_validator.json` into immutable (`config/priv_validator_key.json`) and mutable (`data/priv_validator_state.json`) parts (@yutianwu) -- [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types -- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them + - [cli] Removed `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. + - [cli] Renamed `--proxy_app=nilapp` to `--proxy_app=noop`. + - [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false + - [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split `priv_validator.json` into immutable (`config/priv_validator_key.json`) and mutable (`data/priv_validator_state.json`) parts (@yutianwu) + - [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types + - [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them * Apps * Go API -- [types] [\#2981](https://github.com/tendermint/tendermint/issues/2981) Remove `PrivValidator.GetAddress()` + - [types] [\#2981](https://github.com/tendermint/tendermint/issues/2981) Remove `PrivValidator.GetAddress()` * Blockchain Protocol @@ -72,9 +72,8 @@ See [UPGRADING.md](UPGRADING.md) for more details. ### BREAKING CHANGES: * Go API - -- [dep] [\#3027](https://github.com/tendermint/tendermint/issues/3027) Revert to mainline Go crypto library, eliminating the modified - `bcrypt.GenerateFromPassword` + - [dep] [\#3027](https://github.com/tendermint/tendermint/issues/3027) Revert to mainline Go crypto library, eliminating the modified + `bcrypt.GenerateFromPassword` ## v0.27.2 From 6d6d103f15dcea246b09aa0f5f40348262a21eb0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 16 Jan 2019 13:41:37 -0500 Subject: [PATCH 31/31] fixes from review (#3137) --- CHANGELOG.md | 4 ++-- UPGRADING.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed590005..236b7072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ This release is primarily about upgrades to the `privval` system - separating the `priv_validator.json` into distinct config and data files, and refactoring the socket validator to support reconnections. -XXX: Please backup your existing `priv_validator.json` before using this +**Note:** Please backup your existing `priv_validator.json` before using this version. See [UPGRADING.md](UPGRADING.md) for more details. @@ -53,7 +53,7 @@ See [UPGRADING.md](UPGRADING.md) for more details. - [p2p/conn] [\#3111](https://github.com/tendermint/tendermint/issues/3111) Make SecretConnection thread safe - [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty (@gianfelipe93) -- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the private validator's public key fails +- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the privval's public key fails ## v0.27.4 diff --git a/UPGRADING.md b/UPGRADING.md index df950043..edd50d9e 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -12,7 +12,7 @@ P2PProtocol have changed). Please read carefully for details about upgrading. -XXX: Backup your `config/priv_validator.json` +**Note:** Backup your `config/priv_validator.json` before proceeding. ### `priv_validator.json`