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:
		
					parent
					
						
							
								092533d09d
							
						
					
				
			
			
				commit
				
					
						04e4eda0c3
					
				
			
		
					 16 changed files with 610 additions and 1745 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -18,9 +18,6 @@
 | 
			
		|||
# Doxygen built HTML
 | 
			
		||||
/doc-build
 | 
			
		||||
 | 
			
		||||
# Rust generated files
 | 
			
		||||
/load-tester/target
 | 
			
		||||
 | 
			
		||||
# Executables
 | 
			
		||||
*.exe
 | 
			
		||||
*.out
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
									
									
								
							
							
						
						
									
										871
									
								
								load-tester/Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			@ -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 _
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
            ¶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<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 = ¶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<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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 _
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								nano/load_test/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								nano/load_test/CMakeLists.txt
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										593
									
								
								nano/load_test/entry.cpp
									
										
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue