Rewrite load tester in C++ (#1989)

* Rewrite load tester from Rust to C++

* Boost 1.69/mac update

* Rename results to primary_node_results to be more clear
This commit is contained in:
Wesley Shillingford 2019-05-16 13:34:28 +01:00 committed by GitHub
commit 04e4eda0c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 610 additions and 1745 deletions

3
.gitignore vendored
View file

@ -18,9 +18,6 @@
# Doxygen built HTML
/doc-build
# Rust generated files
/load-tester/target
# Executables
*.exe
*.out

View file

@ -32,7 +32,7 @@ jobs:
os: osx
compiler: clang
before_install:
- brew update && brew install qt5 && brew cask install xquartz && brew install rust && brew upgrade cmake;
- brew update && brew install qt5 && brew cask install xquartz && brew upgrade cmake;
install:
- brew install ccache;
- export PATH="/usr/local/opt/ccache/libexec:$PATH";
@ -46,11 +46,10 @@ cache:
- ccache: true
- directories:
- $HOME/Library/Caches/Homebrew
- $HOME/.cargo
- $TRAVIS_BUILD_DIR/load-tester/target
script:
- if [ -n "$ONE_TIME_TESTS" ]; then ci/check-commit-format.sh; fi
- if [ -n "$ONE_TIME_TESTS" ]; then doxygen doxygen.config; fi # TODO also deploy the built HTML
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then ci/build-travis.sh "/usr/local/opt/qt5/lib/cmake/Qt5"; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then docker run -v $TRAVIS_BUILD_DIR:/workspace -v $HOME/.ccache:/ccache -v $HOME/.cargo:/cargo nanocurrency/nano-ci-$TRAVIS_COMPILER /bin/bash -c "apt install ccache; cd /workspace && ASAN=${ASAN} TSAN=${TSAN} CCACHE_DIR=/ccache CARGO_HOME=/cargo ./ci/build-travis.sh /usr/lib/x86_64-linux-gnu/cmake/Qt5 ${PWD}"; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then docker run -v $TRAVIS_BUILD_DIR:/workspace -v $HOME/.ccache:/ccache nanocurrency/nano-ci-$TRAVIS_COMPILER /bin/bash -c "apt install ccache; cd /workspace && ASAN=${ASAN} TSAN=${TSAN} CCACHE_DIR=/ccache ./ci/build-travis.sh /usr/lib/x86_64-linux-gnu/cmake/Qt5 ${PWD}"; fi

View file

@ -312,6 +312,8 @@ if (NANO_TEST OR RAIBLOCKS_TEST)
set (gtest_force_shared_crt OFF)
endif()
add_subdirectory(nano/load_test)
add_subdirectory (gtest/googletest)
# FIXME: This fixes gtest include directories without modifying gtest's
# CMakeLists.txt. Ideally we should use GTest::GTest and GTest::Main as

View file

@ -43,9 +43,4 @@ fi
popd
pushd load-tester
cargo build --release
popd
cp ./load-tester/target/release/nano-load-tester ./build/load_test
./ci/test.sh ./build

View file

@ -71,7 +71,7 @@ run_tests() {
xvfb_run_ ./qt_test
qt_test_res=${?}
${TIMEOUT_CMD} ${TIMEOUT_TIME_ARG} ${TIMEOUT_SEC-${TIMEOUT_DEFAULT}} ./load_test ./nano_node ./nano_rpc -s 150
${TIMEOUT_CMD} ${TIMEOUT_TIME_ARG} ${TIMEOUT_SEC-${TIMEOUT_DEFAULT}} ./load_test -s 150
load_test_res=${?}
echo "Core Test return code: ${core_test_res}"

View file

@ -8,8 +8,7 @@ RUN apt-get update -qq && apt-get install -yqq \
RUN apt-get update -qq && apt-get install -yqq \
qt5-default \
valgrind \
xorg xvfb xauth xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic \
cargo
xorg xvfb xauth xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic
RUN apt-get update -qq && apt-get install -yqq \
clang-3.9 lldb-3.9

View file

@ -17,6 +17,4 @@ RUN rm bootstrap_boost.sh
RUN apt-get update -qq && apt-get install -yqq \
qt5-default \
valgrind \
xorg xvfb xauth xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic \
cargo
xorg xvfb xauth xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic

871
load-tester/Cargo.lock generated
View file

@ -1,871 +0,0 @@
[[package]]
name = "aho-corasick"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "atty"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chrono"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap"
version = "2.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-cpupool"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "httparse"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iovec"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mime"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-named-pipes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-uds"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"socket2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "net2"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num_cpus"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nano-load-tester"
version = "0.1.0"
dependencies = [
"clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-process 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "relay"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "safemem"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scoped-tls"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "slab"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "socket2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "take"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "termcolor"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"wincolor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-process"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-signal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-proto"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-signal"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicase"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wincolor"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4"
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9"
"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
"checksum clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4151c5790817c7d21bbdc6c3530811f798172915f93258244948b93ba19604a6"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f15f0b172cb4f52ed5dbf47f774a387cd2315d1bf7894ab5af9b083ae27efa5a"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1"
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
"checksum hyper 0.11.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4d6105c5eeb03068b10ff34475a0d166964f98e7b9777cc34b342a225af9b87c"
"checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd"
"checksum mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "75f72a93f046f1517e3cfddc0a096eb756a2ba727d36edc8227dee769a50a9b0"
"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
"checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9224c91f82b3c47cf53dcf78dfaa20d6888fbcc5d272d5f2fcdf8a697f3c987d"
"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca"
"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
"checksum num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9936036cc70fe4a8b2d338ab665900323290efb03983c86cbe235ae800ad8017"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5"
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum socket2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a76b792959eba82f021c9028c8ecb6396f085268d6d46af2ed96a829cc758d7c"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
"checksum termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9065bced9c3e43453aa3d56f1e98590b8455b341d2fa191a1090c0dd0b242c75"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
"checksum tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "52b4e32d8edbf29501aabb3570f027c6ceb00ccef6538f4bddba0200503e74e8"
"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743"
"checksum tokio-process 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2e76e0cd21a4ae5362697e85f98aa5d26c88f09ce9fc367b57c0643ba0b022c2"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum tokio-signal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57c4031b97651d28c87a0a071e1c2809d70609d3120ce285b302eb7d52c96906"
"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a39ee4464208f6430992ff20154216ab2357772ac871d994c51628d60e58b8b0"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"

View file

@ -1,17 +0,0 @@
[package]
name = "nano-load-tester"
version = "0.1.0"
[dependencies]
clap = "2.29.2"
env_logger = "0.5.3"
error-chain = "0.11.0"
futures = "0.1.17"
hyper = "0.11.15"
rand = "0.4.2"
serde = "1.0.27"
serde_derive = "1.0.27"
serde_json = "1.0.9"
time = "0.1.39"
tokio-core = "0.1.12"
tokio-process = "0.1.5"

View file

@ -1,185 +0,0 @@
use std::io;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use serde_json;
use futures::Future;
use hyper::client::Connect;
use tokio_core::reactor::Handle;
use tokio_process::{Child, CommandExt};
use serde_json::Value;
use errors::*;
use rpc::RpcClient;
const RPC_PORT_START: u64 = 55000;
const PEERING_PORT_START: u64 = 54000;
const IPC_PORT_START: u64 = 56000;
pub fn launch_node_and_rpc(
nano_node: &Path,
nano_rpc: &Path,
tmp_dir: &Path,
handle: Handle,
i: u64,
) -> Result<(Child, Child, RpcClient)> {
let data_dir = tmp_dir.join(format!("Nano_load_test_{}", i));
match fs::create_dir(&data_dir) {
Ok(_) => {}
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => {
let _ = fs::remove_file(data_dir.join("data.ldb"));
let _ = fs::remove_file(data_dir.join("wallets.ldb"));
}
r => r.chain_err(|| "failed to create nano_node data directory")?,
}
let peering_port = PEERING_PORT_START + i;
let ipc_port = IPC_PORT_START + i;
let config = json!({
"version": "2",
"rpc_enable": "false",
"rpc": {
"version": "1",
"enable_sign_hash": "false",
"max_work_generate_difficulty": "ffffffffc0000000",
"child_process": {
"enable": "false"
}
},
"node": {
"version": "17",
"peering_port": peering_port.to_string(),
"bootstrap_fraction_numerator": "1",
"receive_minimum": "1000000000000000000000000",
"logging": {
"version": "7",
"ledger": "false",
"ledger_duplicate": "false",
"vote": "false",
"network": "true",
"network_message": "false",
"network_publish": "false",
"network_packet": "false",
"network_keepalive": "false",
"network_node_id_handshake": "false",
"node_lifetime_tracing": "false",
"insufficient_work": "true",
"log_ipc": "true",
"bulk_pull": "false",
"work_generation_time": "true",
"upnp_details": "false",
"timing": "false",
"log_to_cerr": "false",
"max_size": "134217728",
"rotation_size": "4194304",
"flush": "true",
"min_time_between_output": "5"
},
"work_peers": "",
"preconfigured_peers": "",
"preconfigured_representatives": [
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo"
],
"online_weight_minimum": "60000000000000000000000000000000000000",
"online_weight_quorum": "50",
"password_fanout": "1024",
"io_threads": "8",
"network_threads": "8",
"work_threads": "8",
"signature_checker_threads": "7",
"enable_voting": "true",
"bootstrap_connections": "4",
"bootstrap_connections_max": "64",
"callback_address": "",
"callback_port": "0",
"callback_target": "",
"lmdb_max_dbs": "128",
"block_processor_batch_max_time": "5000",
"allow_local_peers": "true",
"vote_minimum": "1000000000000000000000000000000000",
"unchecked_cutoff_time": "14400",
"ipc": {
"tcp": {
"enable": "true",
"port": ipc_port.to_string (),
"io_timeout": "15"
},
"local": {
"enable": "false",
"path": "/tmp/nano",
"io_timeout": "15"
},
},
"tcp_client_timeout": "5",
"tcp_server_timeout": "30"
},
"opencl_enable": "false",
"opencl": {
"platform": "0",
"device": "0",
"threads": "1048576"
}
});
let rpc_port = RPC_PORT_START + i;
let rpc_config = json!({
"address": "::1",
"port": rpc_port.to_string(),
"enable_control": "true",
"max_json_depth": "20",
"version": "1",
"process": {
"ipc_port": ipc_port.to_string (),
"io_threads": "8",
"num_ipc_connections": "8"
}
});
let config_writer =
File::create(data_dir.join("config.json")).chain_err(|| "failed to create config.json")?;
serde_json::to_writer_pretty(config_writer, &config)
.chain_err(|| "failed to write config.json")?;
let child = Command::new(nano_node)
.arg("--data_path")
.arg(&data_dir)
.arg("--daemon")
.spawn_async(&handle)
.chain_err(|| "failed to spawn nano_node")?;
let rpc_config_writer =
File::create(data_dir.join("rpc_config.json")).chain_err(|| "failed to create rpc_config.json")?;
serde_json::to_writer_pretty(rpc_config_writer, &rpc_config)
.chain_err(|| "failed to write rpc_config.json")?;
let rpc_child = Command::new(nano_rpc)
.arg("--data_path")
.arg(&data_dir)
.arg("--daemon")
.spawn_async(&handle)
.chain_err(|| "failed to spawn nano_rpc")?;
let rpc_client = RpcClient::new(
handle,
format!("http://[::1]:{}/", rpc_port).parse().unwrap(),
);
Ok((child, rpc_child, rpc_client))
}
pub fn connect_node<C: Connect>(
node: &RpcClient<C>,
i: u64,
) -> Box<Future<Item = (), Error = Error>> {
Box::new(
node.call::<_, Value>(&json!({
"action": "keepalive",
"address": "::1",
"port": PEERING_PORT_START + i,
})).then(|x| x.chain_err(|| "failed to call nano_rpc"))
.map(|_| ()),
) as _
}

View file

@ -1,559 +0,0 @@
#![recursion_limit = "128"]
use std::process;
use std::path::PathBuf;
use std::thread;
use std::time::{Duration, Instant};
use std::collections::HashMap;
use std::iter;
use std::env;
extern crate clap;
use clap::Arg;
#[macro_use]
extern crate error_chain;
extern crate futures;
use futures::{stream, Future, Stream};
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
use serde_json::Value;
extern crate tokio_core;
use tokio_core::reactor::Core;
extern crate tokio_process;
extern crate hyper;
extern crate rand;
use rand::Rng;
extern crate time;
extern crate env_logger;
mod errors {
error_chain!{}
}
use errors::*;
mod rpc;
use rpc::{RpcClient, RpcError};
mod launch_node_and_rpc;
struct Parameters {
node_count: u16,
node_path: PathBuf,
rpc_path: PathBuf,
tmp_dir: PathBuf,
send_count: usize,
dest_count: usize,
simultaneous_process_calls: usize,
catch_up_timeout: u64,
generate_receives: bool,
precompute_blocks: bool,
output_stats: bool,
}
// found in secure.cpp
const GENESIS_ACCOUNT: &str = "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo";
const GENESIS_PRIVKEY: &str = "34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4";
fn run(params: Parameters) -> Result<()> {
let output_stats = params.output_stats;
macro_rules! tstat {
($( $x:tt )*) => {
if output_stats {
let now = time::now().to_timespec();
println!("{}.{:09},{}", now.sec, now.nsec, format_args!( $( $x )* ));
}
}
}
if params.dest_count > params.send_count {
bail!("send count should be greater than or equal to the destination count")
}
let mut tokio_core = Core::new().chain_err(|| "failed to create tokio Core")?;
let mut children = Vec::with_capacity(params.node_count as _);
let mut rpc_children = Vec::with_capacity(params.node_count as _);
let mut nodes: Vec<RpcClient<_>> = Vec::with_capacity(params.node_count as _);
for i in 0..params.node_count {
let (child, rpc_child, rpc_client) = launch_node_and_rpc::launch_node_and_rpc(
&params.node_path,
&params.rpc_path,
&params.tmp_dir,
tokio_core.handle(),
i as _,
)?;
children.push(child);
rpc_children.push(rpc_child);
nodes.push(rpc_client);
}
if nodes.is_empty() {
bail!("no nodes spun up");
}
eprintln!("Waiting for nodes to spin up...");
thread::sleep(Duration::from_secs(7));
eprintln!("Connecting nodes...");
let primary_node = nodes.first().unwrap();
for (a, node) in nodes.iter().enumerate() {
for b in 0..nodes.len() {
if a != b {
tokio_core.run(launch_node_and_rpc::connect_node(node, b as _))?;
}
}
}
thread::sleep(Duration::from_secs(5));
eprintln!("Beginning tests");
tstat!("start");
#[derive(Debug, Deserialize, PartialEq, Eq)]
struct AccountInfo {
frontier: String,
balance: String,
block_count: String,
}
tstat!("genesis_info,start");
let genesis_initial = primary_node.call(&json!({
"action": "account_info",
"account": GENESIS_ACCOUNT,
}));
let genesis_initial: AccountInfo = tokio_core
.run(genesis_initial)
.chain_err(|| "failed to get genesis account info")?;
tstat!("genesis_info,done");
let genesis_initial_balance = genesis_initial.balance;
#[derive(Deserialize)]
#[allow(dead_code)]
struct Account {
account: String,
private: String,
public: String,
}
tstat!("key_create,start");
let dest_accts = stream::iter_ok(0..params.dest_count)
.map(|_| {
primary_node.call(&json!({
"action": "key_create",
}))
})
.buffer_unordered(10) // execute 10 `key_create`s simultaneously
.inspect(|_| {
tstat!("key_create,progress");
})
.collect();
tstat!("key_create,done");
let dest_accts: Vec<Account> = tokio_core
.run(dest_accts)
.chain_err(|| "failed to generate destination accounts")?;
if dest_accts.is_empty() {
bail!("no destination accounts generated");
}
let mut frontiers = HashMap::new();
#[derive(Deserialize)]
struct BlockInfo {
hash: String,
block: String,
}
let mut rng: rand::XorShiftRng = rand::thread_rng().gen();
if params.precompute_blocks {
frontiers.insert(GENESIS_ACCOUNT, genesis_initial.frontier);
let mut last_percent = 0;
eprint!("Creating blocks: 00%");
tstat!("block_create,start");
let mut blocks: Vec<String> = Vec::new();
for i in 0..params.send_count {
let dest_acct = &rng.choose(&dest_accts).unwrap();
// since only `balance - amount` matters, each block spends 1 raw
let send_future = primary_node.call(&json!({
"action": "block_create",
"type": "send",
"key": GENESIS_PRIVKEY,
"balance": genesis_initial_balance,
"amount": i + 1,
"destination": dest_acct.account,
"previous": frontiers[GENESIS_ACCOUNT],
}));
// needs to be synchronous because of frontier ordering
let send: BlockInfo = tokio_core
.run(send_future)
.chain_err(|| "failed to create send block")?;
tstat!("block_create,progress,send");
blocks.push(send.block);
frontiers.insert(GENESIS_ACCOUNT, send.hash.clone());
if params.generate_receives {
let recv_future = if let Some(frontier) = frontiers.get(dest_acct.account.as_str())
{
primary_node.call(&json!({
"action": "block_create",
"type": "receive",
"key": dest_acct.private,
"previous": frontier,
"source": send.hash,
}))
} else {
primary_node.call(&json!({
"action": "block_create",
"type": "open",
"key": dest_acct.private,
"source": send.hash,
"representative": GENESIS_ACCOUNT,
}))
};
let recv: BlockInfo = tokio_core
.run(recv_future)
.chain_err(|| "failed to create receive block")?;
tstat!("block_create,progress,receive");
frontiers.insert(&dest_acct.account, recv.hash);
blocks.push(recv.block);
}
let new_percent = (100 * i) / params.send_count;
if last_percent == new_percent {
continue;
}
last_percent = new_percent;
eprint!("\rCreating blocks: {:02}%", new_percent);
}
eprintln!("\rCreated blocks ");
tstat!("block_create,done");
let mut process_calls_completed = 0;
last_percent = 0;
let n_blocks = blocks.len();
eprint!("Primary node processing blocks: 00%");
tstat!("process,start");
let process = stream::iter_ok(blocks.iter())
.map(|block| {
primary_node.call::<_, Value>(&json!({
"action": "process",
"block": block,
}))
})
.buffer_unordered(params.simultaneous_process_calls)
.then(|r| match r {
Ok(_) => Ok(()),
Err(RpcError::RpcError(Value::String(ref s)))
if params.simultaneous_process_calls != 1 && s.starts_with("Gap") =>
{
Ok(())
}
Err(err) => Err(err),
})
.inspect(|_| {
process_calls_completed += 1;
let new_percent = (100 * process_calls_completed) / n_blocks;
if last_percent == new_percent {
return;
}
last_percent = new_percent;
eprint!("\rPrimary node processing blocks: {:02}%", new_percent);
tstat!("process,progress");
});
tokio_core
.run(process.fold((), |_, _| Ok(())))
.chain_err(|| "failed to process blocks")?;
tstat!("process,done");
eprintln!("\rPrimary node processed blocks ");
} else {
#[derive(Deserialize)]
struct WalletInfo {
wallet: String,
}
let wallet = primary_node.call(&json!({
"action": "wallet_create",
}));
let wallet: WalletInfo = tokio_core
.run(wallet)
.chain_err(|| "failed to create wallet")?;
let wallet = wallet.wallet;
tstat!("wallet_add_key,start");
let add_genesis = primary_node.call::<_, Value>(&json!({
"action": "wallet_add",
"wallet": wallet,
"key": GENESIS_PRIVKEY,
}));
tokio_core
.run(add_genesis)
.chain_err(|| "failed to add genesis key to wallet")?;
tstat!("wallet_add_key,progress");
let add_keys = stream::iter_ok(dest_accts.iter())
.map(|acct| {
primary_node.call::<_, Value>(&json!({
"action": "wallet_add",
"wallet": wallet,
"key": acct.private,
"work": false, // We always manually call `receive`
}))
})
.buffer_unordered(10)
.inspect(|_| tstat!("wallet_add_key,progress"));
tokio_core
.run(add_keys.fold((), |_, _| Ok(())))
.chain_err(|| "failed to add keys to wallet")?;
tstat!("wallet_add_key,done");
let mut send_calls_completed = 0;
let mut last_percent = 0;
#[derive(Deserialize)]
struct TransactionInfo {
block: String,
}
eprint!("\rPrimary node processing transactions: 00%");
tstat!("transaction,start");
let wallet = wallet.as_str();
let dest_accts = dest_accts.as_slice();
let params = &params;
let stream = stream::iter_ok(0..params.send_count)
.map(move |i| {
// Make sure that every account is sent a block first, then pick randomly
let mut dest_acct = rng.choose(dest_accts).unwrap();
if i < params.dest_count {
dest_acct = &dest_accts[i];
}
let send_future = primary_node
.call::<_, TransactionInfo>(&json!({
"action": "send",
"wallet": wallet,
"source": GENESIS_ACCOUNT,
"destination": dest_acct.account,
"amount": "1",
}))
.inspect(move |_| {
tstat!("transaction,progress,send");
});
if params.generate_receives {
Box::new(send_future.and_then(move |send| {
primary_node
.call::<_, TransactionInfo>(&json!({
"action": "receive",
"wallet": wallet,
"account": dest_acct.account,
"block": send.block,
}))
.inspect(move |_| {
tstat!("transaction,progress,receive");
})
})) as Box<Future<Item = TransactionInfo, Error = RpcError>>
} else {
Box::new(send_future) as Box<Future<Item = TransactionInfo, Error = RpcError>>
}
})
.buffer_unordered(params.simultaneous_process_calls)
.inspect(|_| {
send_calls_completed += 1;
let new_percent = (100 * send_calls_completed) / params.send_count;
if last_percent == new_percent {
return;
}
last_percent = new_percent;
eprint!(
"\rPrimary node processing transactions: {:02}%",
new_percent
);
});
tokio_core
.run(stream.fold((), |_, _| Ok(())))
.chain_err(|| "failed to process transactions")?;
eprintln!("\rPrimary node processed transactions ");
tstat!("transaction,progress,done");
}
let broadcasted_at = Instant::now();
eprintln!("Waiting for nodes to catch up...");
let timeout = Duration::from_secs(params.catch_up_timeout);
let mut known_account_info = HashMap::new();
tstat!("check,start");
for node in &nodes {
for (&acct, frontier) in frontiers.iter() {
// We only know frontiers if `precompute_blocks` is enabled.
loop {
if Instant::now() - broadcasted_at > timeout {
bail!("timed out while waiting for nodes to catch up");
}
let acct_info = node.call::<_, AccountInfo>(&json!({
"action": "account_info",
"account": acct,
}));
let acct_info = tokio_core
.run(acct_info)
.chain_err(|| "failed to check genesis account info")?;
if &acct_info.frontier == frontier {
break;
}
thread::sleep(Duration::from_secs(1));
}
}
if !frontiers.is_empty() {
tstat!("check,progress");
}
if node as *const _ == primary_node as *const _ {
for acct in dest_accts
.iter()
.map(|a| a.account.as_str())
.chain(iter::once(GENESIS_ACCOUNT))
{
let acct_info = node.call(&json!({
"action": "account_info",
"account": acct,
}));
let acct_info: AccountInfo = tokio_core.run(acct_info).chain_err(|| {
format!("failed to check account {} info on primary node", acct)
})?;
known_account_info.insert(acct, acct_info);
}
} else {
for (&acct, acct_info) in known_account_info.iter() {
loop {
if Instant::now() - broadcasted_at > timeout {
bail!("timed out while waiting for nodes to catch up");
}
let node_acct_info = node.call(&json!({
"action": "account_info",
"account": acct,
}));
let node_acct_info = tokio_core.run(node_acct_info);
match node_acct_info {
Err(RpcError::RpcError(ref s)) if s == "Account not found" => {}
Ok(node_acct_info) => if acct_info == &node_acct_info {
break;
},
r => {
r.chain_err(|| {
format!("failed to check account {} info on secondary node", acct)
})?;
}
}
thread::sleep(Duration::from_secs(1));
}
}
}
tstat!("check,progress");
}
tstat!("check,done");
eprintln!("Done!");
tstat!("done");
Ok(())
}
fn main() {
env_logger::init();
let matches = clap::App::new("nano-load-tester")
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::with_name("node_count")
.short("n")
.long("node-count")
.value_name("N")
.default_value("10")
.help("The number of nodes to spin up"),
)
.arg(
Arg::with_name("tmp_dir")
.long("tmp-dir")
.value_name("PATH")
.help("The path to a temporary directory for nano_node data"),
)
.arg(
Arg::with_name("node_path")
.value_name("PATH")
.required(true)
.help("The path to the nano_node to test"),
)
.arg(
Arg::with_name("rpc_path")
.value_name("PATH")
.required(true)
.help("The path to the nano_rpc to test"),
)
.arg(
Arg::with_name("send_count")
.short("s")
.long("send-count")
.value_name("N")
.default_value("2000")
.help("How many send blocks to generate"),
)
.arg(
Arg::with_name("destination_count")
.short("d")
.long("destination-count")
.value_name("N")
.default_value("2")
.help("How many destination accounts to choose between"),
)
.arg(
Arg::with_name("simultaneous_process_calls")
.long("simultaneous-process-calls")
.value_name("N")
.default_value("20")
.help("How many `process` or `send` calls to send at a given time"),
)
.arg(
Arg::with_name("catch_up_timeout")
.long("catch-up-timeout")
.value_name("SECONDS")
.default_value("120")
.help("The maximum number of seconds to wait for nodes to catch up"),
)
.arg(
Arg::with_name("no_receives")
.long("no-receives")
.help("Do not generate receives for the generated sends"),
)
.arg(
Arg::with_name("precompute_blocks")
.long("precompute-blocks")
.help("Use the `block_create` and `process` endpoints to precompute blocks"),
)
.arg(
Arg::with_name("stats")
.long("stats")
.help("Output stats on how long steps take in CSV format"),
)
.get_matches();
macro_rules! num_arg {
($arg:expr) => {
match matches.value_of($arg).unwrap().parse() {
Ok(n) => n,
Err(err) => {
eprintln!("Failed to parse {}: {}", $arg, err);
process::exit(2);
}
}
};
}
let params = Parameters {
node_count: num_arg!("node_count"),
node_path: matches.value_of("node_path").unwrap().into(),
rpc_path: matches.value_of("rpc_path").unwrap().into(),
tmp_dir: matches
.value_of("tmp_dir")
.or(env::var("TMPDIR").ok().as_ref().map(|x| x.as_str()))
.unwrap_or("/tmp")
.into(),
send_count: num_arg!("send_count"),
dest_count: num_arg!("destination_count"),
simultaneous_process_calls: num_arg!("simultaneous_process_calls"),
catch_up_timeout: num_arg!("catch_up_timeout"),
generate_receives: !matches.is_present("no_receives"),
precompute_blocks: matches.is_present("precompute_blocks"),
output_stats: matches.is_present("stats"),
};
if let Err(ref e) = run(params) {
eprintln!("Error: {}", e);
for e in e.iter().skip(1) {
eprintln!(" caused by: {}", e);
}
if let Some(backtrace) = e.backtrace() {
eprintln!("\nBacktrace:\n{:?}", backtrace);
}
process::exit(1);
}
}

View file

@ -1,95 +0,0 @@
use std::fmt;
use futures::{Future, Stream};
use futures::future;
use tokio_core::reactor::Handle;
use hyper::{self, Request, Uri};
use hyper::client::{Connect, HttpConnector};
use hyper::header::{ContentLength, ContentType};
use serde::{Deserialize, Serialize};
use serde_json::{self, Value};
#[derive(Debug)]
pub enum RpcError {
Http(hyper::Error),
Json(serde_json::Error),
/// An error returned from the RPC
RpcError(Value),
}
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RpcError::Http(ref err) => fmt::Display::fmt(err, f),
RpcError::Json(ref err) => fmt::Display::fmt(err, f),
RpcError::RpcError(ref val) => if let Some(s) = val.as_str() {
fmt::Display::fmt(s, f)
} else {
fmt::Display::fmt(val, f)
},
}
}
}
impl ::std::error::Error for RpcError {
fn description(&self) -> &str {
match *self {
RpcError::Http(_) => "HTTP error",
RpcError::Json(_) => "JSON parsing error",
RpcError::RpcError(_) => "RPC returned error",
}
}
}
pub struct RpcClient<C: Connect = HttpConnector> {
http_client: hyper::Client<C>,
uri: Uri,
}
impl RpcClient<HttpConnector> {
pub fn new(tokio_handle: Handle, rpc_uri: Uri) -> RpcClient<HttpConnector> {
RpcClient::from_hyper_client(hyper::Client::new(&tokio_handle), rpc_uri)
}
}
impl<C: Connect> RpcClient<C> {
pub fn from_hyper_client(hyper_client: hyper::Client<C>, rpc_uri: Uri) -> RpcClient<C> {
RpcClient {
http_client: hyper_client,
uri: rpc_uri,
}
}
pub fn call<'a, I: Serialize, O: Deserialize<'a> + 'static>(
&self,
request: &'a I,
) -> Box<Future<Item = O, Error = RpcError>> {
let json = match serde_json::to_vec(request).map_err(RpcError::Json) {
Ok(json) => json,
Err(err) => return Box::new(future::err(err)) as _,
};
let mut req = Request::new(hyper::Method::Post, self.uri.clone());
req.headers_mut().set(ContentType::json());
req.headers_mut().set(ContentLength(json.len() as u64));
req.set_body(json);
Box::new(
self.http_client
.request(req)
.and_then(|res| res.body().concat2())
.map_err(RpcError::Http)
.and_then(|slice| serde_json::from_slice(&slice).map_err(RpcError::Json))
.and_then(|mut json: Value| {
if let Some(obj) = json.as_object_mut() {
if let Some(err) = obj.remove("error") {
return Err(RpcError::RpcError(err));
}
}
O::deserialize(json).map_err(RpcError::Json)
}),
) as _
}
}

View file

@ -31,5 +31,6 @@ target_compile_definitions(core_test
PRIVATE
-DNANO_VERSION_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR}
-DNANO_VERSION_MINOR=${CPACK_PACKAGE_VERSION_MINOR}
-DNANO_VERSION_PATCH=${CPACK_PACKAGE_VERSION_PATCH})
-DNANO_VERSION_PATCH=${CPACK_PACKAGE_VERSION_PATCH}
-DBOOST_PROCESS_SUPPORTED=${BOOST_PROCESS_SUPPORTED})
target_link_libraries (core_test node secure gtest libminiupnpc-static Boost::boost)

View file

@ -0,0 +1,8 @@
add_executable (load_test
entry.cpp)
target_link_libraries (load_test node secure Boost::boost)
target_compile_definitions(load_test
PRIVATE
-DBOOST_PROCESS_SUPPORTED=${BOOST_PROCESS_SUPPORTED})

593
nano/load_test/entry.cpp Normal file
View file

@ -0,0 +1,593 @@
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/program_options.hpp>
#include <iomanip>
#include <nano/core_test/testutil.hpp>
#include <nano/node/daemonconfig.hpp>
#include <nano/node/testing.hpp>
#include <nano/secure/utility.hpp>
#include <random>
namespace nano
{
void force_nano_test_network ();
}
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
#ifndef BOOST_PROCESS_SUPPORTED
#error BOOST_PROCESS_SUPPORTED must be set, check configuration
#endif
#if BOOST_PROCESS_SUPPORTED
#include <boost/process.hpp>
#endif
constexpr auto rpc_port_start = 60000;
constexpr auto peering_port_start = 61000;
constexpr auto ipc_port_start = 62000;
void write_config_files (boost::filesystem::path const & data_path, int index)
{
nano::daemon_config daemon_config (data_path);
nano::jsonconfig json;
json.read_and_update (daemon_config, data_path / "config.json");
auto node_l = json.get_required_child ("node");
node_l.put ("peering_port", peering_port_start + index);
auto tcp = node_l.get_required_child ("ipc").get_required_child ("tcp");
tcp.put ("enable", true);
tcp.put ("port", ipc_port_start + index);
json.write (data_path / "config.json");
nano::rpc_config rpc_config;
nano::jsonconfig json1;
json1.read_and_update (rpc_config, data_path / "rpc_config.json");
json1.put ("port", rpc_port_start + index);
json1.put ("enable_control", true);
json1.get_required_child ("process").put ("ipc_port", ipc_port_start + index);
json1.write (data_path / "rpc_config.json");
}
// Report a failure
void fail (boost::system::error_code ec, char const * what)
{
std::cerr << what << ": " << ec.message () << "\n";
}
class account final
{
public:
std::string private_key;
std::string public_key;
std::string as_string;
};
class account_info final
{
public:
bool operator== (account_info const & other)
{
return frontier == other.frontier && block_count == other.block_count && balance == other.balance && error == other.error;
}
std::string frontier;
std::string block_count;
std::string balance;
bool error{ false };
};
class receive_session final : public std::enable_shared_from_this<receive_session>
{
public:
receive_session (boost::asio::io_context & ioc, std::atomic<int> & send_calls_remaining, std::string const & wallet, std::string const & account, std::string const & block, tcp::resolver::results_type const & results) :
socket (ioc),
send_calls_remaining (send_calls_remaining),
wallet (wallet),
account (account),
block (block),
results (results),
strand (socket.get_executor ())
{
}
void run ()
{
auto this_l (shared_from_this ());
boost::asio::async_connect (this_l->socket, this_l->results.cbegin (), this_l->results.cend (), boost::asio::bind_executor (strand, [this_l](boost::system::error_code const & ec, boost::asio::ip::tcp::resolver::iterator) {
if (ec)
{
return fail (ec, "connect");
}
boost::property_tree::ptree request;
request.put ("action", "receive");
request.put ("wallet", this_l->wallet);
request.put ("account", this_l->account);
request.put ("block", this_l->block);
std::stringstream ostream;
boost::property_tree::write_json (ostream, request);
this_l->req.method (http::verb::post);
this_l->req.version (11);
this_l->req.target ("/");
this_l->req.body () = ostream.str ();
this_l->req.prepare_payload ();
http::async_write (this_l->socket, this_l->req, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code ec, std::size_t) {
if (ec)
{
return fail (ec, "write");
}
http::async_read (this_l->socket, this_l->buffer, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code ec, std::size_t) {
if (ec)
{
return fail (ec, "read");
}
--this_l->send_calls_remaining;
// Gracefully close the socket
this_l->socket.shutdown (tcp::socket::shutdown_both, ec);
if (ec && ec != boost::system::errc::not_connected)
{
return fail (ec, "shutdown");
}
}));
}));
}));
}
private:
tcp::socket socket;
boost::asio::strand<boost::asio::io_context::executor_type> strand;
boost::beast::flat_buffer buffer;
http::request<http::string_body> req;
http::response<http::string_body> res;
std::atomic<int> & send_calls_remaining;
std::string wallet;
std::string account;
std::string block;
tcp::resolver::results_type const & results;
};
class send_session final : public std::enable_shared_from_this<send_session>
{
public:
send_session (boost::asio::io_context & ioc, std::atomic<int> & send_calls_remaining, std::string const & wallet, std::string const & source, std::string const & destination, tcp::resolver::results_type const & results) :
socket (ioc),
send_calls_remaining (send_calls_remaining),
wallet (wallet),
source (source),
destination (destination),
results (results),
strand (socket.get_executor ())
{
}
void run ()
{
auto this_l (shared_from_this ());
boost::asio::async_connect (this_l->socket, this_l->results.cbegin (), this_l->results.cend (), boost::asio::bind_executor (strand, [this_l](boost::system::error_code const & ec, boost::asio::ip::tcp::resolver::iterator) {
if (ec)
{
return fail (ec, "connect");
}
boost::property_tree::ptree request;
request.put ("action", "send");
request.put ("wallet", this_l->wallet);
request.put ("source", this_l->source);
request.put ("destination", this_l->destination);
request.put ("amount", "1");
std::stringstream ostream;
boost::property_tree::write_json (ostream, request);
this_l->req.method (http::verb::post);
this_l->req.version (11);
this_l->req.target ("/");
this_l->req.body () = ostream.str ();
this_l->req.prepare_payload ();
http::async_write (this_l->socket, this_l->req, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code ec, std::size_t) {
if (ec)
{
return fail (ec, "write");
}
http::async_read (this_l->socket, this_l->buffer, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code ec, std::size_t) {
if (ec)
{
return fail (ec, "read");
}
boost::property_tree::ptree json;
std::stringstream body (this_l->res.body ());
boost::property_tree::read_json (body, json);
auto block = json.get<std::string> ("block");
std::make_shared<receive_session> (this_l->socket.get_io_context (), this_l->send_calls_remaining, this_l->wallet, this_l->destination, block, this_l->results)->run ();
this_l->socket.shutdown (tcp::socket::shutdown_both, ec);
if (ec && ec != boost::system::errc::not_connected)
{
return fail (ec, "shutdown");
}
}));
}));
}));
}
private:
tcp::socket socket;
boost::asio::strand<boost::asio::io_context::executor_type> strand;
boost::beast::flat_buffer buffer;
http::request<http::string_body> req;
http::response<http::string_body> res;
std::atomic<int> & send_calls_remaining;
std::string wallet;
std::string source;
std::string destination;
tcp::resolver::results_type const & results;
};
boost::property_tree::ptree rpc_request (boost::property_tree::ptree const & request, boost::asio::io_context & ioc, tcp::resolver::results_type const & results)
{
tcp::socket socket{ ioc };
boost::asio::connect (socket, results.begin (), results.end ());
std::stringstream ostream;
boost::property_tree::write_json (ostream, request);
auto request_string = ostream.str ();
http::request<http::string_body> req{ http::verb::post, "/", 11, request_string };
req.prepare_payload ();
http::write (socket, req);
boost::beast::flat_buffer buffer;
http::response<boost::beast::http::string_body> res;
http::read (socket, buffer, res);
boost::property_tree::ptree json;
std::stringstream body (res.body ());
boost::property_tree::read_json (body, json);
// Gracefully close the socket
boost::system::error_code ec;
socket.shutdown (tcp::socket::shutdown_both, ec);
if (ec && ec != boost::system::errc::not_connected)
{
throw boost::system::system_error{ ec };
}
return json;
}
void keepalive_rpc (boost::asio::io_context & ioc, tcp::resolver::results_type const & results, uint16_t port)
{
boost::property_tree::ptree request;
request.put ("action", "keepalive");
request.put ("address", "::1");
request.put ("port", port);
rpc_request (request, ioc, results);
}
account key_create_rpc (boost::asio::io_context & ioc, tcp::resolver::results_type const & results)
{
std::string request_string;
boost::property_tree::ptree request;
request.put ("action", "key_create");
auto json = rpc_request (request, ioc, results);
account account_l;
account_l.private_key = json.get<std::string> ("private");
account_l.public_key = json.get<std::string> ("public");
account_l.as_string = json.get<std::string> ("account");
return account_l;
}
std::string wallet_create_rpc (boost::asio::io_context & ioc, tcp::resolver::results_type const & results)
{
std::string request_string;
boost::property_tree::ptree request;
request.put ("action", "wallet_create");
auto json = rpc_request (request, ioc, results);
return json.get<std::string> ("wallet");
}
void wallet_add_rpc (boost::asio::io_context & ioc, tcp::resolver::results_type const & results, std::string const & wallet, std::string const & prv_key)
{
boost::property_tree::ptree request;
request.put ("action", "wallet_add");
request.put ("wallet", wallet);
request.put ("key", prv_key);
rpc_request (request, ioc, results);
}
void stop_rpc (boost::asio::io_context & ioc, tcp::resolver::results_type const & results)
{
boost::property_tree::ptree request;
request.put ("action", "stop");
rpc_request (request, ioc, results);
}
account_info account_info_rpc (boost::asio::io_context & ioc, tcp::resolver::results_type const & results, std::string const & account)
{
boost::property_tree::ptree request;
request.put ("action", "account_info");
request.put ("account", account);
account_info account_info;
auto json = rpc_request (request, ioc, results);
auto error = json.get_optional<std::string> ("error");
if (error)
{
account_info.error = true;
}
else
{
account_info.balance = json.get<std::string> ("balance");
account_info.block_count = json.get<std::string> ("block_count");
account_info.frontier = json.get<std::string> ("frontier");
}
return account_info;
}
/** This launches a node and fires a lot of send/recieve RPC requests at it (configurable), then other nodes are tested to make sure they observe these blocks as well. */
int main (int argc, char * const * argv)
{
nano::force_nano_test_network ();
boost::program_options::options_description description ("Command line options");
// clang-format off
description.add_options ()
("help", "Print out options")
("node_count,n", boost::program_options::value<int> ()->default_value (10), "The number of nodes to spin up")
("send_count,s", boost::program_options::value<int> ()->default_value (2000), "How many send blocks to generate")
("simultaneous_process_calls", boost::program_options::value<int> ()->default_value (20), "Number of simultaneous rpc sends to do")
("destination_count", boost::program_options::value<int> ()->default_value (2), "How many destination accounts to choose between")
("node_path", boost::program_options::value<std::string> (), "The path to the nano_node to test")
("rpc_path", boost::program_options::value<std::string> (), "The path to do nano_rpc to test");
// clang-format on
boost::program_options::variables_map vm;
try
{
boost::program_options::store (boost::program_options::parse_command_line (argc, argv, description), vm);
}
catch (boost::program_options::error const & err)
{
std::cerr << err.what () << std::endl;
return 1;
}
boost::program_options::notify (vm);
int result (0);
auto node_count = vm.find ("node_count")->second.as<int> ();
auto destination_count = vm.find ("destination_count")->second.as<int> ();
auto send_count = vm.find ("send_count")->second.as<int> ();
auto simultaneous_process_calls = vm.find ("simultaneous_process_calls")->second.as<int> ();
boost::system::error_code err;
auto running_executable_filepath = boost::dll::program_location (err);
auto node_path_it (vm.find ("node_path"));
std::string node_path;
if (node_path_it != vm.end ())
{
node_path = node_path_it->second.as<std::string> ();
}
else
{
auto node_filepath = running_executable_filepath.parent_path () / "nano_node";
if (running_executable_filepath.has_extension ())
{
node_filepath.replace_extension (running_executable_filepath.extension ());
}
node_path = node_filepath.string ();
}
auto rpc_path_it (vm.find ("rpc_path"));
std::string rpc_path;
if (rpc_path_it != vm.end ())
{
rpc_path = rpc_path_it->second.as<std::string> ();
}
else
{
auto rpc_filepath = running_executable_filepath.parent_path () / "nano_rpc";
if (running_executable_filepath.has_extension ())
{
rpc_filepath.replace_extension (running_executable_filepath.extension ());
}
rpc_path = rpc_filepath.string ();
}
nano::network_constants network_constants;
#if BOOST_PROCESS_SUPPORTED
std::vector<std::unique_ptr<boost::process::child>> nodes;
std::vector<std::unique_ptr<boost::process::child>> rpc_servers;
#else
std::vector<std::unique_ptr<std::thread>> nodes;
std::vector<std::unique_ptr<std::thread>> rpc_servers;
#endif
for (auto i = 0; i < node_count; ++i)
{
auto data_path = nano::unique_path ();
boost::filesystem::create_directory (data_path);
write_config_files (data_path, i);
auto current_network = network_constants.get_current_network_as_string ();
#if BOOST_PROCESS_SUPPORTED
nodes.emplace_back (std::make_unique<boost::process::child> (node_path, "--daemon", "--data_path", data_path.string (), "--network", current_network));
rpc_servers.emplace_back (std::make_unique<boost::process::child> (rpc_path, "--daemon", "--data_path", data_path.string (), "--network", current_network));
#else
auto node_exe_command = boost::str (boost::format ("%1% --daemon --data_path=%2% --network=%3%") % node_path % data_path.string () % current_network);
nodes.emplace_back (std::make_unique<std::thread> ([node_exe_command]() {
std::system (node_exe_command.c_str ());
}));
auto rpc_exe_command = boost::str (boost::format ("%1% --daemon --data_path=%2% --network=%3%") % rpc_path % data_path.string () % current_network);
rpc_servers.emplace_back (std::make_unique<std::thread> ([rpc_exe_command]() {
std::system (rpc_exe_command.c_str ());
}));
#endif
}
std::cout << "Waiting for nodes to spin up..." << std::endl;
std::this_thread::sleep_for (std::chrono::seconds (7));
std::cout << "Connecting nodes..." << std::endl;
boost::asio::io_context ioc;
tcp::resolver resolver{ ioc };
auto const primary_node_results = resolver.resolve ("::1", std::to_string (rpc_port_start));
for (int i = 0; i < node_count; ++i)
{
keepalive_rpc (ioc, primary_node_results, peering_port_start + i);
}
std::cout << "Beginning tests" << std::endl;
// Create keys
std::vector<account> destination_accounts;
for (int i = 0; i < destination_count; ++i)
{
destination_accounts.emplace_back (key_create_rpc (ioc, primary_node_results));
}
// Create wallet
std::string wallet = wallet_create_rpc (ioc, primary_node_results);
// Add genesis account to it
wallet_add_rpc (ioc, primary_node_results, wallet, nano::test_genesis_key.prv.data.to_string ());
// Add destination accounts
for (auto & account : destination_accounts)
{
wallet_add_rpc (ioc, primary_node_results, wallet, account.private_key);
}
std::cout << "\rPrimary node processing transactions: 00%";
std::thread t ([send_count, &destination_accounts, &ioc, &primary_node_results, &wallet, &resolver, &node_count]() {
std::random_device rd;
std::mt19937 mt (rd ());
std::uniform_int_distribution<size_t> dist (0, destination_accounts.size () - 1);
std::atomic<int> send_calls_remaining{ send_count };
for (auto i = 0; i < send_count; ++i)
{
account * destination_account;
if (i < destination_accounts.size ())
{
destination_account = &destination_accounts[i];
}
else
{
auto random_account_index = dist (mt);
destination_account = &destination_accounts[random_account_index];
}
std::make_shared<send_session> (ioc, send_calls_remaining, wallet, nano::genesis_account.to_account (), destination_account->as_string, primary_node_results)->run ();
}
while (send_calls_remaining != 0)
{
static int last_percent = 0;
auto percent = static_cast<int> (100 * ((send_count - send_calls_remaining) / static_cast<double> (send_count)));
if (last_percent != percent)
{
std::cout << "\rPrimary node processing transactions: " << std::setfill ('0') << std::setw (2) << percent << "%";
last_percent = percent;
}
}
std::cout << "\rPrimary node processed transactions " << std::endl;
std::cout << "Waiting for nodes to catch up..." << std::endl;
std::map<std::string, account_info> known_account_info;
for (int i = 0; i < destination_accounts.size (); ++i)
{
known_account_info.emplace (destination_accounts[i].as_string, account_info_rpc (ioc, primary_node_results, destination_accounts[i].as_string));
}
nano::timer<std::chrono::milliseconds> timer;
timer.start ();
for (int i = 1; i < node_count; ++i)
{
auto const results = resolver.resolve ("::1", std::to_string (rpc_port_start + i));
for (auto & account_info : known_account_info)
{
while (true)
{
auto other_account_info = account_info_rpc (ioc, results, account_info.first);
if (!other_account_info.error && account_info.second == other_account_info)
{
// Found the account in this node
break;
}
if (timer.since_start () > std::chrono::seconds (120))
{
throw std::runtime_error ("Timed out");
}
std::this_thread::sleep_for (std::chrono::seconds (1));
}
}
stop_rpc (ioc, results);
}
// Stop main node
stop_rpc (ioc, primary_node_results);
});
nano::thread_runner runner (ioc, simultaneous_process_calls);
t.join ();
runner.join ();
#if BOOST_PROCESS_SUPPORTED
for (auto & node : nodes)
{
node->wait ();
}
for (auto & rpc_server : rpc_servers)
{
rpc_server->wait ();
}
#else
for (auto & node : nodes)
{
node->join ();
}
for (auto & rpc_server : rpc_servers)
{
rpc_server->join ();
}
#endif
std::cout << "Done!" << std::endl;
}