From 04e4eda0c3e647277c220e911f990ca98b7c3aed Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Thu, 16 May 2019 13:34:28 +0100 Subject: [PATCH] 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 --- .gitignore | 3 - .travis.yml | 5 +- CMakeLists.txt | 2 + ci/build-travis.sh | 5 - ci/test.sh | 2 +- docker/ci/Dockerfile-clang | 3 +- docker/ci/Dockerfile-gcc | 4 +- load-tester/Cargo.lock | 871 ------------------------- load-tester/Cargo.toml | 17 - load-tester/src/launch_node_and_rpc.rs | 185 ------ load-tester/src/main.rs | 559 ---------------- load-tester/src/rpc.rs | 95 --- nano/core_test/CMakeLists.txt | 3 +- nano/core_test/test_messages.cpp | 0 nano/load_test/CMakeLists.txt | 8 + nano/load_test/entry.cpp | 593 +++++++++++++++++ 16 files changed, 610 insertions(+), 1745 deletions(-) delete mode 100644 load-tester/Cargo.lock delete mode 100644 load-tester/Cargo.toml delete mode 100644 load-tester/src/launch_node_and_rpc.rs delete mode 100644 load-tester/src/main.rs delete mode 100644 load-tester/src/rpc.rs delete mode 100644 nano/core_test/test_messages.cpp create mode 100644 nano/load_test/CMakeLists.txt create mode 100644 nano/load_test/entry.cpp diff --git a/.gitignore b/.gitignore index 9572aa88..d7e79709 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,6 @@ # Doxygen built HTML /doc-build -# Rust generated files -/load-tester/target - # Executables *.exe *.out diff --git a/.travis.yml b/.travis.yml index 155f1ec0..012a2554 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index d5956b0d..bd19926d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/ci/build-travis.sh b/ci/build-travis.sh index b4fe76f5..76b0b44a 100755 --- a/ci/build-travis.sh +++ b/ci/build-travis.sh @@ -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 diff --git a/ci/test.sh b/ci/test.sh index 7f64be3a..b35bc45e 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -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}" diff --git a/docker/ci/Dockerfile-clang b/docker/ci/Dockerfile-clang index 83faa714..d165a686 100644 --- a/docker/ci/Dockerfile-clang +++ b/docker/ci/Dockerfile-clang @@ -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 diff --git a/docker/ci/Dockerfile-gcc b/docker/ci/Dockerfile-gcc index 1dd69c6b..a1dc493f 100644 --- a/docker/ci/Dockerfile-gcc +++ b/docker/ci/Dockerfile-gcc @@ -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 diff --git a/load-tester/Cargo.lock b/load-tester/Cargo.lock deleted file mode 100644 index 249c05f0..00000000 --- a/load-tester/Cargo.lock +++ /dev/null @@ -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" diff --git a/load-tester/Cargo.toml b/load-tester/Cargo.toml deleted file mode 100644 index 7cf731ed..00000000 --- a/load-tester/Cargo.toml +++ /dev/null @@ -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" diff --git a/load-tester/src/launch_node_and_rpc.rs b/load-tester/src/launch_node_and_rpc.rs deleted file mode 100644 index 942e2c55..00000000 --- a/load-tester/src/launch_node_and_rpc.rs +++ /dev/null @@ -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( - node: &RpcClient, - i: u64, -) -> Box> { - 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 _ -} diff --git a/load-tester/src/main.rs b/load-tester/src/main.rs deleted file mode 100644 index 2bd546ce..00000000 --- a/load-tester/src/main.rs +++ /dev/null @@ -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> = 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( - ¶ms.node_path, - ¶ms.rpc_path, - ¶ms.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 = 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 = 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 = ¶ms; - 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> - } else { - Box::new(send_future) as Box> - } - }) - .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); - } -} diff --git a/load-tester/src/rpc.rs b/load-tester/src/rpc.rs deleted file mode 100644 index fde5cce0..00000000 --- a/load-tester/src/rpc.rs +++ /dev/null @@ -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 { - http_client: hyper::Client, - uri: Uri, -} - -impl RpcClient { - pub fn new(tokio_handle: Handle, rpc_uri: Uri) -> RpcClient { - RpcClient::from_hyper_client(hyper::Client::new(&tokio_handle), rpc_uri) - } -} - -impl RpcClient { - pub fn from_hyper_client(hyper_client: hyper::Client, rpc_uri: Uri) -> RpcClient { - RpcClient { - http_client: hyper_client, - uri: rpc_uri, - } - } - - pub fn call<'a, I: Serialize, O: Deserialize<'a> + 'static>( - &self, - request: &'a I, - ) -> Box> { - 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 _ - } -} diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index e281c724..192a7e55 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -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) diff --git a/nano/core_test/test_messages.cpp b/nano/core_test/test_messages.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/nano/load_test/CMakeLists.txt b/nano/load_test/CMakeLists.txt new file mode 100644 index 00000000..90d54d7a --- /dev/null +++ b/nano/load_test/CMakeLists.txt @@ -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}) diff --git a/nano/load_test/entry.cpp b/nano/load_test/entry.cpp new file mode 100644 index 00000000..261cda16 --- /dev/null +++ b/nano/load_test/entry.cpp @@ -0,0 +1,593 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#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 +{ +public: + receive_session (boost::asio::io_context & ioc, std::atomic & 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 strand; + boost::beast::flat_buffer buffer; + http::request req; + http::response res; + std::atomic & 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 +{ +public: + send_session (boost::asio::io_context & ioc, std::atomic & 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 ("block"); + + std::make_shared (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 strand; + boost::beast::flat_buffer buffer; + http::request req; + http::response res; + std::atomic & 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 req{ http::verb::post, "/", 11, request_string }; + req.prepare_payload (); + + http::write (socket, req); + boost::beast::flat_buffer buffer; + + http::response 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 ("private"); + account_l.public_key = json.get ("public"); + account_l.as_string = json.get ("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 ("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 ("error"); + if (error) + { + account_info.error = true; + } + else + { + account_info.balance = json.get ("balance"); + account_info.block_count = json.get ("block_count"); + account_info.frontier = json.get ("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 ()->default_value (10), "The number of nodes to spin up") + ("send_count,s", boost::program_options::value ()->default_value (2000), "How many send blocks to generate") + ("simultaneous_process_calls", boost::program_options::value ()->default_value (20), "Number of simultaneous rpc sends to do") + ("destination_count", boost::program_options::value ()->default_value (2), "How many destination accounts to choose between") + ("node_path", boost::program_options::value (), "The path to the nano_node to test") + ("rpc_path", boost::program_options::value (), "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 (); + auto destination_count = vm.find ("destination_count")->second.as (); + auto send_count = vm.find ("send_count")->second.as (); + auto simultaneous_process_calls = vm.find ("simultaneous_process_calls")->second.as (); + + 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 (); + } + 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 (); + } + 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> nodes; + std::vector> rpc_servers; +#else + std::vector> nodes; + std::vector> 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 (node_path, "--daemon", "--data_path", data_path.string (), "--network", current_network)); + rpc_servers.emplace_back (std::make_unique (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 ([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 ([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 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 dist (0, destination_accounts.size () - 1); + + std::atomic 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 (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 (100 * ((send_count - send_calls_remaining) / static_cast (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 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 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; +}