diff --git a/.travis.yml b/.travis.yml index 5d6de65c2..f7ac723e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ env: global: # GH_TOKEN - secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw= - - TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" + - TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethjson" - ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}" - KCOV_FEATURES="" - KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov" @@ -70,6 +70,7 @@ after_success: | $KCOV_CMD target/debug/deps/ethsync-* && $KCOV_CMD target/debug/deps/ethcore_rpc-* && $KCOV_CMD target/debug/deps/ethminer-* && + $KCOV_CMD target/debug/deps/ethjson-* && $KCOV_CMD target/debug/parity-* && [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && diff --git a/Cargo.lock b/Cargo.lock index aa4f0a902..56bd823c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,6 @@ dependencies = [ "ethcore-devtools 1.1.0", "ethcore-rpc 1.1.0", "ethcore-util 1.1.0", - "ethjson 0.1.0", "ethminer 1.1.0", "ethsync 1.1.0", "fdlimit 0.1.0", @@ -20,7 +19,6 @@ dependencies = [ "rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -43,10 +41,10 @@ dependencies = [ [[package]] name = "aster" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syntex_syntax 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -54,7 +52,7 @@ name = "bigint" version = "0.1.0" dependencies = [ "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -141,7 +139,7 @@ source = "git+https://github.com/tomusdrw/rust-ctrlc.git#f4927770f89eca80ec25091 dependencies = [ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -165,7 +163,7 @@ name = "docopt" version = "0.6.78" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.58 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -181,7 +179,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -218,7 +216,7 @@ dependencies = [ "ethcore-devtools 1.1.0", "ethcore-util 1.1.0", "ethjson 0.1.0", - "heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -244,14 +242,14 @@ dependencies = [ "ethcore-util 1.1.0", "ethminer 1.1.0", "ethsync 1.1.0", - "jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-http-server 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -268,7 +266,7 @@ dependencies = [ "env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "ethcore-devtools 1.1.0", - "heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "igd 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "json-tests 0.1.0", @@ -297,9 +295,9 @@ dependencies = [ "ethcore-util 1.1.0", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -325,7 +323,7 @@ dependencies = [ "ethcore 1.1.0", "ethcore-util 1.1.0", "ethminer 1.1.0", - "heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", @@ -355,11 +353,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "heapsize" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -391,27 +388,27 @@ dependencies = [ "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hyper" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cookie 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.1 (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.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -422,7 +419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hyper 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.58 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "xmltree 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -442,23 +439,23 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-http-server" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -466,7 +463,7 @@ name = "kernel32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -498,7 +495,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "librocksdb-sys" version = "0.2.3" -source = "git+https://github.com/arkpar/rust-rocksdb.git#ebb602fc74b4067f9f51310bdc0401b8e59b7156" +source = "git+https://github.com/arkpar/rust-rocksdb.git#ae44ef33ed1358ffc79aa05ed77839d555daba33" dependencies = [ "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -533,6 +530,14 @@ dependencies = [ "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mime" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "mio" version = "0.5.0" @@ -542,11 +547,11 @@ dependencies = [ "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -555,20 +560,20 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (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 = "net2" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -667,20 +672,20 @@ dependencies = [ [[package]] name = "quasi" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syntex_syntax 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quasi_codegen" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aster 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aster 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -703,7 +708,7 @@ dependencies = [ [[package]] name = "regex" -version = "0.1.56" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -720,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rocksdb" version = "0.4.3" -source = "git+https://github.com/arkpar/rust-rocksdb.git#ebb602fc74b4067f9f51310bdc0401b8e59b7156" +source = "git+https://github.com/arkpar/rust-rocksdb.git#ae44ef33ed1358ffc79aa05ed77839d555daba33" dependencies = [ "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "librocksdb-sys 0.2.3 (git+https://github.com/arkpar/rust-rocksdb.git)", @@ -734,7 +739,7 @@ dependencies = [ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -790,14 +795,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_codegen" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aster 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aster 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quasi 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quasi_codegen 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -837,18 +842,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syntex" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syntex_syntax 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syntex_syntax" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -867,7 +872,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -885,7 +890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -921,7 +926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicase" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -992,7 +997,7 @@ dependencies = [ [[package]] name = "winapi" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1005,7 +1010,7 @@ name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 62a3c31c7..e468799ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,8 +27,6 @@ ethsync = { path = "sync" } ethminer = { path = "miner" } ethcore-devtools = { path = "devtools" } ethcore-rpc = { path = "rpc", optional = true } -ethjson = { path = "json" } -serde_json = "0.7.0" [features] default = ["rpc"] @@ -41,10 +39,6 @@ travis-nightly = ["ethcore/json-tests", "dev"] path = "parity/main.rs" name = "parity" -[[bin]] -path = "parity/rpctest.rs" -name = "rpctest" - [profile.release] debug = false lto = false diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d4aa35445..d700b854f 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -24,7 +24,6 @@ use state::*; use verification::PreverifiedBlock; /// A block, encoded as it is on the block chain. -// TODO: rename to Block #[derive(Default, Debug, Clone)] pub struct Block { /// The header of this block. @@ -76,8 +75,6 @@ impl Decodable for Block { } /// Internal type for a block's common elements. -// TODO: rename to ExecutedBlock -// TODO: use BareBlock #[derive(Debug)] pub struct ExecutedBlock { base: Block, @@ -85,6 +82,7 @@ pub struct ExecutedBlock { receipts: Vec, transactions_set: HashSet, state: State, + traces: Option>, } /// A set of references to `ExecutedBlock` fields that are publicly accessible. @@ -99,11 +97,21 @@ pub struct BlockRefMut<'a> { pub receipts: &'a Vec, /// State. pub state: &'a mut State, + /// Traces. + pub traces: &'a Option>, } impl ExecutedBlock { /// Create a new block from the given `state`. - fn new(state: State) -> ExecutedBlock { ExecutedBlock { base: Default::default(), receipts: Default::default(), transactions_set: Default::default(), state: state } } + fn new(state: State, tracing: bool) -> ExecutedBlock { + ExecutedBlock { + base: Default::default(), + receipts: Default::default(), + transactions_set: Default::default(), + state: state, + traces: if tracing {Some(Vec::new())} else {None}, + } + } /// Get a structure containing individual references to all public fields. pub fn fields(&mut self) -> BlockRefMut { @@ -113,6 +121,7 @@ impl ExecutedBlock { uncles: &self.base.uncles, state: &mut self.state, receipts: &self.receipts, + traces: &self.traces, } } } @@ -134,6 +143,9 @@ pub trait IsBlock { /// Get all information on receipts in this block. fn receipts(&self) -> &Vec { &self.block().receipts } + /// Get all information concerning transaction tracing in this block. + fn traces(&self) -> &Option> { &self.block().traces } + /// Get all uncles in this block. fn uncles(&self) -> &Vec
{ &self.block().base.uncles } } @@ -171,9 +183,9 @@ pub struct SealedBlock { impl<'x> OpenBlock<'x> { /// Create a new OpenBlock ready for transaction pushing. - pub fn new(engine: &'x Engine, db: Box, parent: &Header, last_hashes: LastHashes, author: Address, gas_floor_target: U256, extra_data: Bytes) -> Self { + pub fn new(engine: &'x Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes, author: Address, gas_floor_target: U256, extra_data: Bytes) -> Self { let mut r = OpenBlock { - block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())), + block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()), tracing), engine: engine, last_hashes: last_hashes, }; @@ -249,11 +261,13 @@ impl<'x> OpenBlock<'x> { pub fn push_transaction(&mut self, t: SignedTransaction, h: Option) -> Result<&Receipt, Error> { let env_info = self.env_info(); // info!("env_info says gas_used={}", env_info.gas_used); - match self.block.state.apply(&env_info, self.engine, &t) { - Ok(receipt) => { + match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) { + Ok(outcome) => { self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.base.transactions.push(t); - self.block.receipts.push(receipt); + let t = outcome.trace; + self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed"))); + self.block.receipts.push(outcome.receipt); Ok(&self.block.receipts.last().unwrap()) } Err(x) => Err(From::from(x)) @@ -339,7 +353,7 @@ impl IsBlock for SealedBlock { } /// Enact the block given by block header, transactions and uncles -pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { { if ::log::max_log_level() >= ::log::LogLevel::Trace { let s = State::from_existing(db.spawn(), parent.state_root().clone(), engine.account_start_nonce()); @@ -347,7 +361,7 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head } } - let mut b = OpenBlock::new(engine, db, parent, last_hashes, header.author().clone(), x!(3141562), header.extra_data().clone()); + let mut b = OpenBlock::new(engine, tracing, db, parent, last_hashes, header.author().clone(), x!(3141562), header.extra_data().clone()); b.set_difficulty(*header.difficulty()); b.set_gas_limit(*header.gas_limit()); b.set_timestamp(header.timestamp()); @@ -357,22 +371,22 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header -pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { let block = BlockView::new(block_bytes); let header = block.header(); - enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes) + enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header -pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { let view = BlockView::new(&block.bytes); - enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes) + enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards -pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { +pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, tracing: bool, db: Box, parent: &Header, last_hashes: LastHashes) -> Result { let header = BlockView::new(block_bytes).header_view(); - Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(engine, header.seal()))) + Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes)).seal(engine, header.seal()))) } #[cfg(test)] @@ -391,7 +405,7 @@ mod tests { let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; - let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); let b = b.close(); let _ = b.seal(engine.deref(), vec![]); } @@ -405,14 +419,14 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); - let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close().seal(engine.deref(), vec![]).unwrap(); + let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close().seal(engine.deref(), vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); - let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, vec![genesis_header.hash()]).unwrap(); + let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap(); assert_eq!(e.rlp_bytes(), orig_bytes); @@ -430,7 +444,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); - let mut open_block = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]); + let mut open_block = OpenBlock::new(engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]); let mut uncle1_header = Header::new(); uncle1_header.extra_data = b"uncle1".to_vec(); let mut uncle2_header = Header::new(); @@ -438,14 +452,14 @@ mod tests { open_block.push_uncle(uncle1_header).unwrap(); open_block.push_uncle(uncle2_header).unwrap(); let b = open_block.close().seal(engine.deref(), vec![]).unwrap(); - + let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); - let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, vec![genesis_header.hash()]).unwrap(); + let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()]).unwrap(); let bytes = e.rlp_bytes(); assert_eq!(bytes, orig_bytes); diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 40b01c6f9..7e1a8e23f 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -54,6 +54,9 @@ impl Default for BlockChainConfig { /// Interface for querying blocks by hash and by number. pub trait BlockProvider { + /// True if we store full tracing information for transactions. + fn have_tracing(&self) -> bool; + /// Returns true if the given block is known /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool; @@ -177,6 +180,9 @@ impl BlockProvider for BlockChain { self.query_extras_exist(hash, &self.block_details) } + // We do not store tracing information. + fn have_tracing(&self) -> bool { false } + /// Get raw block data fn block(&self, hash: &H256) -> Option { { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c62364dce..614a74798 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -211,7 +211,7 @@ impl Client where V: Verifier { let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let db = self.state_db.lock().unwrap().spawn(); - let enact_result = enact_verified(&block, engine, db, &parent, last_hashes); + let enact_result = enact_verified(&block, engine, self.chain.have_tracing(), db, &parent, last_hashes); if let Err(e) = enact_result { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -398,6 +398,7 @@ impl BlockChainClient for Client where V: Verifier { let mut b = OpenBlock::new( engine, + false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. self.state_db.lock().unwrap().spawn(), match self.chain.block_header(&h) { Some(ref x) => x, None => {return None} }, self.build_last_hashes(h.clone()), diff --git a/ethcore/src/common.rs b/ethcore/src/common.rs index 5235e9f58..e91501217 100644 --- a/ethcore/src/common.rs +++ b/ethcore/src/common.rs @@ -25,4 +25,5 @@ pub use account::*; pub use transaction::*; pub use log_entry::*; pub use receipt::*; -pub use action_params::*; \ No newline at end of file +pub use action_params::*; +pub use trace::*; \ No newline at end of file diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 406777251..6f854921d 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -299,7 +299,7 @@ mod tests { let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; - let b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); } @@ -312,7 +312,7 @@ mod tests { let mut db = db_result.take(); engine.spec().ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; - let mut b = OpenBlock::new(engine.deref(), db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let mut b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); let mut uncle = Header::new(); let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106"); uncle.author = uncle_author.clone(); diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 28eb96f44..c1107f003 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -65,7 +65,7 @@ pub enum Error { /// Evm result. /// -/// Returns gas_left if execution is successfull, otherwise error. +/// Returns gas_left if execution is successful, otherwise error. pub type Result = result::Result; /// Evm interface. diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 782063cb2..d02f58815 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -41,26 +41,35 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address { pub struct Executed { /// Gas paid up front for execution of transaction. pub gas: U256, + /// Gas used during execution of transaction. pub gas_used: U256, + /// Gas refunded after the execution of transaction. /// To get gas that was required up front, add `refunded` and `gas_used`. pub refunded: U256, + /// Cumulative gas used in current block so far. /// /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` /// /// where `tn` is current transaction. pub cumulative_gas_used: U256, + /// Vector of logs generated by transaction. pub logs: Vec, + /// Addresses of contracts created during execution of transaction. /// Ordered from earliest creation. /// /// eg. sender creates contract A and A in constructor creates contract B /// /// B creation ends first, and it will be the first element of the vector. - pub contracts_created: Vec
+ pub contracts_created: Vec
, + /// Transaction output. + pub output: Bytes, + /// The trace of this transaction. + pub trace: Option, } /// Transaction execution result. @@ -71,38 +80,37 @@ pub struct Executive<'a> { state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, - depth: usize + depth: usize, } impl<'a> Executive<'a> { /// Basic constructor. pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine) -> Self { - Executive::new_with_depth(state, info, engine, 0) - } - - /// Populates executive from parent properties. Increments executive depth. - pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self { - Executive::new_with_depth(state, info, engine, depth + 1) - } - - /// Helper constructor. Should be used to create `Executive` with desired depth. - /// Private. - fn new_with_depth(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self { Executive { state: state, info: info, engine: engine, - depth: depth + depth: 0, + } + } + + /// Populates executive from parent properties. Increments executive depth. + pub fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, parent_depth: usize) -> Self { + Executive { + state: state, + info: info, + engine: engine, + depth: parent_depth + 1, } } /// Creates `Externalities` from `Executive`. - pub fn as_externalities<'_>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_>) -> Externalities { + pub fn as_externalities<'_>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>) -> Externalities { Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output) } /// This funtion should be used to execute transaction. - pub fn transact(&'a mut self, t: &SignedTransaction) -> Result { + pub fn transact(&'a mut self, t: &SignedTransaction, tracing: bool) -> Result { let sender = try!(t.sender()); let nonce = self.state.nonce(&sender); @@ -143,9 +151,9 @@ impl<'a> Executive<'a> { self.state.inc_nonce(&sender); self.state.sub_balance(&sender, &U256::from(gas_cost)); - let mut substate = Substate::new(); + let mut substate = Substate::new(tracing); - let res = match t.action { + let (gas_left, output) = match t.action { Action::Create => { let new_address = contract_address(&sender, &nonce); let params = ActionParams { @@ -159,7 +167,7 @@ impl<'a> Executive<'a> { code: Some(t.data.clone()), data: None, }; - self.create(params, &mut substate) + (self.create(params, &mut substate), vec![]) }, Action::Call(ref address) => { let params = ActionParams { @@ -175,12 +183,12 @@ impl<'a> Executive<'a> { }; // TODO: move output upstream let mut out = vec![]; - self.call(params, &mut substate, BytesRef::Flexible(&mut out)) + (self.call(params, &mut substate, BytesRef::Flexible(&mut out)), out) } }; // finalize here! - Ok(try!(self.finalize(t, substate, res))) + Ok(try!(self.finalize(t, substate, gas_left, output))) } fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy) -> evm::Result { @@ -241,15 +249,27 @@ impl<'a> Executive<'a> { // if destination is a contract, do normal message call // part of substate that may be reverted - let mut unconfirmed_substate = Substate::new(); + let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); + + // transaction tracing stuff. None if there's no tracing. + let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_call(¶ms), self.depth)); + let mut trace_output = trace_info.as_ref().map(|_| vec![]); let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output)) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut())) }; + // if there's tracing, make up trace_info's result with trace_output and some arithmetic. + if let Some((TraceAction::Call(ref mut c), _)) = trace_info { + if let Some(output) = trace_output { + c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, output)); + } + } + trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count); trace!("exec: substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); - self.enact_result(&res, substate, unconfirmed_substate); + + self.enact_result(&res, substate, unconfirmed_substate, trace_info); trace!("exec: new substate={:?}\n", substate); res } else { @@ -267,7 +287,7 @@ impl<'a> Executive<'a> { self.state.snapshot(); // part of substate that may be reverted - let mut unconfirmed_substate = Substate::new(); + let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); // create contract and transfer value to it if necessary let prev_bal = self.state.balance(¶ms.address); @@ -278,15 +298,26 @@ impl<'a> Executive<'a> { self.state.new_contract(¶ms.address, prev_bal); } + let mut trace_info = substate.subtraces.as_ref().map(|_| (TraceAction::from_create(¶ms), self.depth)); + let mut trace_output = trace_info.as_ref().map(|_| vec![]); + let created = params.address.clone(); + let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut())) }; - self.enact_result(&res, substate, unconfirmed_substate); + + if let Some((TraceAction::Create(ref mut c), _)) = trace_info { + if let Some(output) = trace_output { + c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, created, output)); + } + } + + self.enact_result(&res, substate, unconfirmed_substate, trace_info); res } /// Finalizes the transaction (does refunds and suicides). - fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result) -> ExecutionResult { + fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes) -> ExecutionResult { let schedule = self.engine.schedule(self.info); // refunds from SSTORE nonzero -> zero @@ -326,7 +357,9 @@ impl<'a> Executive<'a> { refunded: U256::zero(), cumulative_gas_used: self.info.gas_used + t.gas, logs: vec![], - contracts_created: vec![] + contracts_created: vec![], + output: output, + trace: None, }) }, _ => { @@ -337,12 +370,14 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + gas_used, logs: substate.logs, contracts_created: substate.contracts_created, + output: output, + trace: substate.subtraces.and_then(|mut v| v.pop()), }) }, } } - fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate) { + fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, maybe_info: Option<(TraceAction, usize)>) { match *result { Err(evm::Error::OutOfGas) | Err(evm::Error::BadJumpDestination {..}) @@ -353,7 +388,7 @@ impl<'a> Executive<'a> { }, Ok(_) | Err(evm::Error::Internal) => { self.state.clear_snapshot(); - substate.accrue(un_substate) + substate.accrue(un_substate, maybe_info) } } } @@ -391,7 +426,7 @@ mod tests { state.add_balance(&sender, &U256::from(0x100u64)); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -408,8 +443,8 @@ mod tests { // TODO: just test state root. } - evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} - fn test_create_contract(factory: Factory) { + evm_test!{test_create_contract_out_of_depth: test_create_contract_out_of_depth_jit, test_create_contract_out_of_depth_int} + fn test_create_contract_out_of_depth(factory: Factory) { // code: // // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? @@ -450,7 +485,7 @@ mod tests { state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -462,6 +497,135 @@ mod tests { assert_eq!(substate.contracts_created.len(), 0); } + evm_test!{test_call_to_create: test_call_to_create_jit, test_call_to_create_int} + fn test_call_to_create(factory: Factory) { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 17 - push 17 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address(&sender, &U256::zero()); + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(code.clone()); + params.value = ActionValue::Transfer(U256::from(100)); + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + state.add_balance(&sender, &U256::from(100)); + let info = EnvInfo::default(); + let engine = TestEngine::new(5, factory); + let mut substate = Substate::new(true); + + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + let output = BytesRef::Fixed(&mut[0u8;0]); + ex.call(params, &mut substate, output).unwrap() + }; + + println!("trace: {:?}", substate.subtraces); + let expected_trace = Some(vec![ Trace { + depth: 0, + action: TraceAction::Call(TraceCall { + from: x!("cd1722f3947def4cf144679da39c4c32bdc35681"), + to: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"), + value: x!(100), + gas: x!(100000), + input: vec![], + result: Some((x!(55248), vec![])) + }), + subs: vec![Trace { + depth: 1, + action: TraceAction::Create(TraceCreate { + from: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"), + value: x!(23), + gas: x!(67979), + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + result: Some((x!(3224), x!("c6d80f262ae5e0f164e5fde365044d7ada2bfa34"), vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53])), + }), + subs: vec![] + }] + } ]); + assert_eq!(substate.subtraces, expected_trace); + assert_eq!(gas_left, U256::from(44_752)); + } + + evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int} + fn test_create_contract(factory: Factory) { + // code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address(&sender, &U256::zero()); + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::default(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = Some(code.clone()); + params.value = ActionValue::Transfer(x!(100)); + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + state.add_balance(&sender, &U256::from(100)); + let info = EnvInfo::default(); + let engine = TestEngine::new(5, factory); + let mut substate = Substate::new(true); + + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.create(params.clone(), &mut substate).unwrap() + }; + + println!("trace: {:?}", substate.subtraces); + let expected_trace = Some(vec![Trace { + depth: 0, + action: TraceAction::Create(TraceCreate { + from: params.sender, + value: x!(100), + gas: params.gas, + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + result: Some((x!(3224), params.address, vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53])), + }), + subs: vec![] + } ]); + assert_eq!(substate.subtraces, expected_trace); + assert_eq!(gas_left, U256::from(96_776)); + } evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} fn test_create_contract_value_too_high(factory: Factory) { // code: @@ -504,7 +668,7 @@ mod tests { state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -556,7 +720,7 @@ mod tests { state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::default(); let engine = TestEngine::new(1024, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); { let mut ex = Executive::new(&mut state, &info, &engine); @@ -617,7 +781,7 @@ mod tests { let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -662,7 +826,7 @@ mod tests { state.init_code(&address, code.clone()); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let gas_left = { let mut ex = Executive::new(&mut state, &info, &engine); @@ -699,7 +863,7 @@ mod tests { let executed = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t).unwrap() + ex.transact(&t, false).unwrap() }; assert_eq!(executed.gas, U256::from(100_000)); @@ -732,7 +896,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -763,7 +927,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -796,7 +960,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -829,7 +993,7 @@ mod tests { let res = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.transact(&t) + ex.transact(&t, false) }; match res { @@ -859,7 +1023,7 @@ mod tests { state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap()); let info = EnvInfo::default(); let engine = TestEngine::new(0, factory); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let result = { let mut ex = Executive::new(&mut state, &info, &engine); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index d37bc20fb..a6f7e4f36 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -23,12 +23,12 @@ use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult}; use substate::*; /// Policy for handling output data on `RETURN` opcode. -pub enum OutputPolicy<'a> { +pub enum OutputPolicy<'a, 'b> { /// Return reference to fixed sized output. /// Used for message calls. - Return(BytesRef<'a>), + Return(BytesRef<'a>, Option<&'b mut Bytes>), /// Init new contract as soon as `RETURN` is called. - InitContract + InitContract(Option<&'b mut Bytes>), } /// Transaction properties that externalities need to know about. @@ -62,18 +62,19 @@ pub struct Externalities<'a> { origin_info: OriginInfo, substate: &'a mut Substate, schedule: Schedule, - output: OutputPolicy<'a> + output: OutputPolicy<'a, 'a> } impl<'a> Externalities<'a> { /// Basic `Externalities` constructor. pub fn new(state: &'a mut State, - env_info: &'a EnvInfo, - engine: &'a Engine, - depth: usize, - origin_info: OriginInfo, - substate: &'a mut Substate, - output: OutputPolicy<'a>) -> Self { + env_info: &'a EnvInfo, + engine: &'a Engine, + depth: usize, + origin_info: OriginInfo, + substate: &'a mut Substate, + output: OutputPolicy<'a, 'a> + ) -> Self { Externalities { state: state, env_info: env_info, @@ -190,20 +191,31 @@ impl<'a> Ext for Externalities<'a> { #[cfg_attr(feature="dev", allow(match_ref_pats))] fn ret(&mut self, gas: &U256, data: &[u8]) -> Result { - match &mut self.output { - &mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe { + let handle_copy = |to: &mut Option<&mut Bytes>| { + to.as_mut().map(|b| **b = data.to_owned()); + }; + match self.output { + OutputPolicy::Return(BytesRef::Fixed(ref mut slice), ref mut copy) => { + handle_copy(copy); + let len = cmp::min(slice.len(), data.len()); - ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); + unsafe { + ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len); + } Ok(*gas) }, - &mut OutputPolicy::Return(BytesRef::Flexible(ref mut vec)) => unsafe { + OutputPolicy::Return(BytesRef::Flexible(ref mut vec), ref mut copy) => { + handle_copy(copy); + vec.clear(); vec.reserve(data.len()); - ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); - vec.set_len(data.len()); + unsafe { + ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len()); + vec.set_len(data.len()); + } Ok(*gas) }, - &mut OutputPolicy::InitContract => { + OutputPolicy::InitContract(ref mut copy) => { let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas); if return_cost > *gas { return match self.schedule.exceptional_failed_code_deposit { @@ -211,14 +223,16 @@ impl<'a> Ext for Externalities<'a> { false => Ok(*gas) } } + + handle_copy(copy); + let mut code = vec![]; code.reserve(data.len()); unsafe { ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len()); code.set_len(data.len()); } - let address = &self.origin_info.address; - self.state.init_code(address, code); + self.state.init_code(&self.origin_info.address, code); Ok(*gas - return_cost) } } @@ -312,7 +326,7 @@ mod tests { TestSetup { state: get_temp_state(), engine: get_test_spec().to_engine().unwrap(), - sub_state: Substate::new(), + sub_state: Substate::new(false), env_info: get_test_env_info() } } @@ -323,7 +337,7 @@ mod tests { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None)); assert_eq!(ext.env_info().number, 100); } @@ -332,7 +346,7 @@ mod tests { fn can_return_block_hash_no_env() { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None)); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -351,7 +365,7 @@ mod tests { env_info.last_hashes.push(test_hash.clone()); } let state = setup.state.reference_mut(); - let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None)); let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap()); @@ -363,7 +377,7 @@ mod tests { fn can_call_fail_empty() { let mut setup = TestSetup::new(); let state = setup.state.reference_mut(); - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None)); let mut output = vec![]; @@ -387,7 +401,7 @@ mod tests { let state = setup.state.reference_mut(); { - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None)); ext.log(log_topics, &log_data); } @@ -402,7 +416,7 @@ mod tests { let state = setup.state.reference_mut(); { - let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract); + let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None)); ext.suicide(&refund_account); } diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 89bd5da2b..a2e6a5659 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -16,18 +16,19 @@ use super::test_common::*; use client::{BlockChainClient, Client, ClientConfig}; -use pod_state::*; use block::Block; use ethereum; use tests::helpers::*; use devtools::*; +use spec::Genesis; +use ethjson; pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { init_log(); - let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); + let tests = ethjson::blockchain::Test::load(json_data).unwrap(); let mut failed = Vec::new(); - for (name, test) in json.as_object().unwrap() { + for (name, blockchain) in tests.deref() { let mut fail = false; { let mut fail_unless = |cond: bool| if !cond && !fail { @@ -39,37 +40,36 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { flush!(" - {}...", name); - let blocks: Vec<(Bytes, bool)> = test["blocks"].as_array().unwrap().iter().map(|e| (xjson!(&e["rlp"]), e.find("blockHeader").is_some())).collect(); let mut spec = match era { ChainEra::Frontier => ethereum::new_frontier_test(), ChainEra::Homestead => ethereum::new_homestead_test(), }; - let s = PodState::from_json(test.find("pre").unwrap()); - spec.set_genesis_state(s); - spec.overwrite_genesis(test.find("genesisBlockHeader").unwrap()); + + let genesis = Genesis::from(blockchain.genesis()); + let state = From::from(blockchain.pre_state.clone()); + spec.set_genesis_state(state); + spec.overwrite_genesis_params(genesis); assert!(spec.is_state_root_valid()); - let genesis_hash = spec.genesis_header().hash(); - assert_eq!(genesis_hash, H256::from_json(&test.find("genesisBlockHeader").unwrap()["hash"])); let temp = RandomTempPath::new(); { let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap(); - assert_eq!(client.chain_info().best_block_hash, genesis_hash); - for (b, is_valid) in blocks.into_iter() { + for b in &blockchain.blocks_rlp() { if Block::is_good(&b) { let _ = client.import_block(b.clone()); + client.flush_queue(); + client.import_verified_blocks(&IoChannel::disconnected()); } - client.flush_queue(); - let imported_ok = client.import_verified_blocks(&IoChannel::disconnected()) > 0; - assert_eq!(imported_ok, is_valid); } - fail_unless(client.chain_info().best_block_hash == H256::from_json(&test["lastblockhash"])); + fail_unless(client.chain_info().best_block_hash == blockchain.best_block.clone().into()); } } + if !fail { flushln!("ok"); } } + println!("!!! {:?} tests from failed.", failed.len()); failed } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index b08257a92..dbb9bb6ab 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -75,7 +75,7 @@ impl<'a> TestExt<'a> { depth: usize, origin_info: OriginInfo, substate: &'a mut Substate, - output: OutputPolicy<'a>, + output: OutputPolicy<'a, 'a>, address: Address) -> Self { TestExt { contract_address: contract_address(&address, &state.nonce(&address)), @@ -227,19 +227,21 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec { let out_of_gas = test.find("callcreates").map(|_calls| { }).is_none(); - let mut substate = Substate::new(); + let mut substate = Substate::new(false); let mut output = vec![]; // execute let (res, callcreates) = { - let mut ex = TestExt::new(&mut state, - &info, - &engine, - 0, - OriginInfo::from(¶ms), - &mut substate, - OutputPolicy::Return(BytesRef::Flexible(&mut output)), - params.address.clone()); + let mut ex = TestExt::new( + &mut state, + &info, + &engine, + 0, + OriginInfo::from(¶ms), + &mut substate, + OutputPolicy::Return(BytesRef::Flexible(&mut output), None), + params.address.clone() + ); let evm = engine.vm_factory().create(); let res = evm.exec(params, &mut ex); (res, ex.callcreates) diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index f6b5751a7..4c001007a 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -69,7 +69,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { let mut state = state_result.reference_mut(); state.populate_from(pre); state.commit(); - let res = state.apply(&env, engine.deref(), &t); + let res = state.apply(&env, engine.deref(), &t, false); if fail_unless(state.root() == &post_state_root) { println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root); @@ -80,9 +80,9 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { } if let Ok(r) = res { - if fail_unless(logs == r.logs) { + if fail_unless(logs == r.receipt.logs) { println!("!!! {}: Logs mismatch:", name); - println!("Got:\n{:?}", r.logs); + println!("Got:\n{:?}", r.receipt.logs); println!("Expect:\n{:?}", logs); } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index dfa321639..f8fb223db 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -97,6 +97,7 @@ pub mod filter; pub mod header; pub mod service; pub mod log_entry; +pub mod trace; pub mod spec; pub mod transaction; pub mod views; diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 8f38326ca..387679da9 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -82,7 +82,8 @@ impl From for PodAccount { code: a.code.into(), storage: a.storage.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| { let key: U256 = key.into(); - acc.insert(H256::from(key), value.into()); + let value: U256 = value.into(); + acc.insert(H256::from(key), H256::from(value)); acc }) } diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index 5782803ef..7ebfed78b 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -21,7 +21,7 @@ use pod_account::*; use ethjson; /// State of all accounts in the system expressed in Plain Old Data. -#[derive(Debug,Clone,PartialEq,Eq,Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct PodState (BTreeMap); impl PodState { diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index bcfe7724f..95a891198 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -146,7 +146,7 @@ mod tests { fn it_can_be_started() { let spec = get_test_spec(); let temp_path = RandomTempPath::new(); - let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_with_port(40456), &temp_path.as_path()); + let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path()); assert!(service.is_ok()); } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index cb54654e6..cad29b678 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -26,8 +26,16 @@ use pod_account::*; use pod_state::PodState; //use state_diff::*; // TODO: uncomment once to_pod() works correctly. +/// Used to return information about an `State::apply` operation. +pub struct ApplyOutcome { + /// The receipt for the applied transaction. + pub receipt: Receipt, + /// The trace for the applied transaction, if None if tracing is disabled. + pub trace: Option, +} + /// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result; +pub type ApplyResult = Result; /// Representation of the entire state of all accounts in the system. pub struct State { @@ -209,17 +217,17 @@ impl State { /// Execute a given transaction. /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction) -> ApplyResult { + pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult { // let old = self.to_pod(); - let e = try!(Executive::new(self, env_info, engine).transact(t)); + let e = try!(Executive::new(self, env_info, engine).transact(t, tracing)); // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod())); self.commit(); let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); // trace!("Transaction receipt: {:?}", receipt); - Ok(receipt) + Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) } /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. diff --git a/ethcore/src/substate.rs b/ethcore/src/substate.rs index 57e35ad2e..0610bffb8 100644 --- a/ethcore/src/substate.rs +++ b/ethcore/src/substate.rs @@ -23,37 +23,45 @@ use common::*; pub struct Substate { /// Any accounts that have suicided. pub suicides: HashSet
, + /// Any logs. pub logs: Vec, + /// Refund counter of SSTORE nonzero -> zero. pub sstore_clears_count: U256, - /// Created contracts. - pub contracts_created: Vec
-} -impl Default for Substate { - fn default() -> Self { - Substate::new() - } + /// Created contracts. + pub contracts_created: Vec
, + + /// The trace during this execution or `None` if we're not tracing. + pub subtraces: Option>, } impl Substate { /// Creates new substate. - pub fn new() -> Self { + pub fn new(tracing: bool) -> Self { Substate { - suicides: HashSet::new(), - logs: vec![], - sstore_clears_count: U256::zero(), - contracts_created: vec![] + suicides: Default::default(), + logs: Default::default(), + sstore_clears_count: Default::default(), + contracts_created: Default::default(), + subtraces: if tracing {Some(vec![])} else {None}, } } /// Merge secondary substate `s` into self, accruing each element correspondingly. - pub fn accrue(&mut self, s: Substate) { + pub fn accrue(&mut self, s: Substate, maybe_info: Option<(TraceAction, usize)>) { self.suicides.extend(s.suicides.into_iter()); self.logs.extend(s.logs.into_iter()); self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.contracts_created.extend(s.contracts_created.into_iter()); + if let Some(info) = maybe_info { + self.subtraces.as_mut().expect("maybe_action is Some: so we must be tracing: qed").push(Trace { + action: info.0, + depth: info.1, + subs: s.subtraces.expect("maybe_action is Some: so we must be tracing: qed"), + }); + } } } @@ -64,13 +72,13 @@ mod tests { #[test] fn created() { - let sub_state = Substate::new(); + let sub_state = Substate::new(false); assert_eq!(sub_state.suicides.len(), 0); } #[test] fn accrue() { - let mut sub_state = Substate::new(); + let mut sub_state = Substate::new(false); sub_state.contracts_created.push(address_from_u64(1u64)); sub_state.logs.push(LogEntry { address: address_from_u64(1u64), @@ -80,7 +88,7 @@ mod tests { sub_state.sstore_clears_count = x!(5); sub_state.suicides.insert(address_from_u64(10u64)); - let mut sub_state_2 = Substate::new(); + let mut sub_state_2 = Substate::new(false); sub_state_2.contracts_created.push(address_from_u64(2u64)); sub_state_2.logs.push(LogEntry { address: address_from_u64(1u64), @@ -89,7 +97,7 @@ mod tests { }); sub_state_2.sstore_clears_count = x!(7); - sub_state.accrue(sub_state_2); + sub_state.accrue(sub_state_2, None); assert_eq!(sub_state.contracts_created.len(), 2); assert_eq!(sub_state.sstore_clears_count, x!(12)); assert_eq!(sub_state.suicides.len(), 1); diff --git a/ethcore/src/trace.rs b/ethcore/src/trace.rs new file mode 100644 index 000000000..858230bcd --- /dev/null +++ b/ethcore/src/trace.rs @@ -0,0 +1,111 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Tracing datatypes. +use common::*; + +/// Description of a _call_ action, either a `CALL` operation or a message transction. +#[derive(Debug, Clone, PartialEq)] +pub struct TraceCall { + /// The sending account. + pub from: Address, + /// The destination account. + pub to: Address, + /// The value transferred to the destination account. + pub value: U256, + /// The gas available for executing the call. + pub gas: U256, + /// The input data provided to the call. + pub input: Bytes, + /// The result of the operation; the gas used and the output data of the call. + pub result: Option<(U256, Bytes)>, +} + +/// Description of a _create_ action, either a `CREATE` operation or a create transction. +#[derive(Debug, Clone, PartialEq)] +pub struct TraceCreate { + /// The address of the creator. + pub from: Address, + /// The value with which the new account is endowed. + pub value: U256, + /// The gas available for the creation init code. + pub gas: U256, + /// The init code. + pub init: Bytes, + /// The result of the operation; tuple of the gas used, the address of the newly created account and its code. + /// NOTE: Presently failed operations are not reported so this will always be `Some`. + pub result: Option<(U256, Address, Bytes)>, +// pub output: Bytes, +} + +/// Description of an action that we trace; will be either a call or a create. +#[derive(Debug, Clone, PartialEq)] +pub enum TraceAction { + /// Action isn't yet known. + Unknown, + /// It's a call action. + Call(TraceCall), + /// It's a create action. + Create(TraceCreate), +} + +#[derive(Debug, Clone, PartialEq)] +/// A trace; includes a description of the action being traced and sub traces of each interior action. +pub struct Trace { + /// The number of EVM execution environments active when this action happened; 0 if it's + /// the outer action of the transaction. + pub depth: usize, + /// The action being performed. + pub action: TraceAction, + /// The sub traces for each interior action performed as part of this call. + pub subs: Vec, +} + +impl Default for Trace { + fn default() -> Trace { + Trace { + depth: 0, + action: TraceAction::Unknown, + subs: vec![], + } + } +} + +impl TraceAction { + /// Compose a `TraceAction` from an `ActionParams`, knowing that the action is a call. + pub fn from_call(p: &ActionParams) -> TraceAction { + TraceAction::Call(TraceCall { + from: p.sender.clone(), + to: p.address.clone(), + value: match p.value { ActionValue::Transfer(ref x) | ActionValue::Apparent(ref x) => x.clone() }, + gas: p.gas.clone(), + input: p.data.clone().unwrap_or(vec![]), + result: None, + }) + } + + /// Compose a `TraceAction` from an `ActionParams`, knowing that the action is a create. + pub fn from_create(p: &ActionParams) -> TraceAction { + TraceAction::Create(TraceCreate { + from: p.sender.clone(), + value: match p.value { ActionValue::Transfer(ref x) | ActionValue::Apparent(ref x) => x.clone() }, + gas: p.gas.clone(), + init: p.code.clone().unwrap_or(vec![]), + result: None, + }) + } +} + diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 60cbed56c..6e79d737e 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -278,6 +278,8 @@ mod tests { } impl BlockProvider for TestBlockChain { + fn have_tracing(&self) -> bool { false } + fn is_known(&self, hash: &H256) -> bool { self.blocks.contains_key(hash) } diff --git a/json/Cargo.toml b/json/Cargo.toml index 91f8b8431..7887f2cea 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -14,7 +14,7 @@ clippy = { version = "0.0.54", optional = true } [build-dependencies] serde_codegen = { version = "0.7.0", optional = true } -syntex = "0.29.0" +syntex = "0.30.0" [features] default = ["serde_codegen"] diff --git a/json/src/blockchain/account.rs b/json/src/blockchain/account.rs index 990c024c7..ca69409fc 100644 --- a/json/src/blockchain/account.rs +++ b/json/src/blockchain/account.rs @@ -19,7 +19,6 @@ use std::collections::BTreeMap; use uint::Uint; use bytes::Bytes; -use hash::H256; /// Blockchain test account deserializer. #[derive(Debug, PartialEq, Deserialize, Clone)] @@ -31,7 +30,7 @@ pub struct Account { /// Nonce. pub nonce: Uint, /// Storage. - pub storage: BTreeMap, + pub storage: BTreeMap, } #[cfg(test)] diff --git a/json/src/blockchain/block.rs b/json/src/blockchain/block.rs index 190102ae5..03522a2c9 100644 --- a/json/src/blockchain/block.rs +++ b/json/src/blockchain/block.rs @@ -24,11 +24,11 @@ use blockchain::transaction::Transaction; #[derive(Debug, PartialEq, Deserialize)] pub struct Block { #[serde(rename="blockHeader")] - header: Header, + header: Option
, rlp: Bytes, - transactions: Vec, + transactions: Option>, #[serde(rename="uncleHeaders")] - uncles: Vec
, + uncles: Option>, } impl Block { diff --git a/json/src/blockchain/blockchain.rs b/json/src/blockchain/blockchain.rs index 4783819e5..98392b983 100644 --- a/json/src/blockchain/blockchain.rs +++ b/json/src/blockchain/blockchain.rs @@ -17,6 +17,7 @@ //! Blockchain deserialization. use bytes::Bytes; +use hash::H256; use blockchain::state::State; use blockchain::header::Header; use blockchain::block::Block; @@ -30,7 +31,7 @@ pub struct BlockChain { pub genesis_block: Header, /// Genesis block rlp. #[serde(rename="genesisRLP")] - pub genesis_rlp: Bytes, + pub genesis_rlp: Option, /// Blocks. pub blocks: Vec, /// Post state. @@ -39,14 +40,12 @@ pub struct BlockChain { /// Pre state. #[serde(rename="pre")] pub pre_state: State, + /// Hash of best block. + #[serde(rename="lastblockhash")] + pub best_block: H256 } impl BlockChain { - /// Returns genesis block rlp. - pub fn genesis_rlp(&self) -> Vec { - self.genesis_rlp.clone().into() - } - /// Returns blocks rlp. pub fn blocks_rlp(&self) -> Vec> { self.blocks.iter().map(|block| block.rlp()).collect() diff --git a/json/src/blockchain/test.rs b/json/src/blockchain/test.rs index 6f48a8bc6..1a6a63a71 100644 --- a/json/src/blockchain/test.rs +++ b/json/src/blockchain/test.rs @@ -18,6 +18,9 @@ use std::collections::BTreeMap; use std::ops::Deref; +use std::io::Read; +use serde_json; +use serde_json::Error; use blockchain::blockchain::BlockChain; /// Blockchain test deserializer. @@ -31,3 +34,10 @@ impl Deref for Test { &self.0 } } + +impl Test { + /// Loads test from json. + pub fn load(reader: R) -> Result where R: Read { + serde_json::from_reader(reader) + } +} diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 061795b40..6ccae51d7 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -46,12 +46,8 @@ impl Visitor for BytesVisitor { let v = match value.len() { 0 => vec![], 2 if value.starts_with("0x") => vec![], - _ if value.starts_with("0x") => try!(FromHex::from_hex(&value[2..]).map_err(|_| { - Error::custom(format!("Invalid hex value {}.", value).as_ref()) - })), - _ => try!(FromHex::from_hex(value).map_err(|_| { - Error::custom(format!("Invalid hex value {}.", value).as_ref()) - })) + _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), + _ => FromHex::from_hex(value).unwrap_or(vec![]), }; Ok(Bytes(v)) } diff --git a/parity/main.rs b/parity/main.rs index c7e534993..56e70cf2a 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -46,6 +46,7 @@ use env_logger::LogBuilder; use ctrlc::CtrlC; use util::*; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; +use util::keys::store::*; use ethcore::spec::*; use ethcore::client::*; use ethcore::service::{ClientService, NetSyncMessage}; @@ -55,7 +56,6 @@ use ethminer::{Miner, MinerService}; use docopt::Docopt; use daemonize::Daemonize; use number_prefix::{binary_prefix, Standalone, Prefixed}; -use util::keys::store::*; fn die_with_message(msg: &str) -> ! { println!("ERROR: {}", msg); @@ -315,7 +315,7 @@ impl Configuration { fn author(&self) -> Address { let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author); - Address::from_str(d).unwrap_or_else(|_| { + Address::from_str(clean_0x(d)).unwrap_or_else(|_| { die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", d) }) } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index ca8004728..c28f598fd 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -26,7 +26,7 @@ clippy = { version = "0.0.54", optional = true } [build-dependencies] serde_codegen = { version = "0.7.0", optional = true } -syntex = "0.29.0" +syntex = "0.30.0" [features] default = ["serde_codegen"] diff --git a/rpc/rpctest/Cargo.toml b/rpc/rpctest/Cargo.toml new file mode 100644 index 000000000..5b8f7f845 --- /dev/null +++ b/rpc/rpctest/Cargo.toml @@ -0,0 +1,17 @@ +[package] +description = "Rpc test client." +name = "rpctest" +version = "1.1.0" +license = "GPL-3.0" +authors = ["Ethcore "] + +[dependencies] +ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" } +docopt = "0.6" +rustc-serialize = "0.3" +ethcore = { path = "../../ethcore" } +ethcore-devtools = { path = "../../devtools" } +ethcore-rpc = { path = ".." } +ethcore-util = { path = "../../util" } +ethjson = { path = "../../json" } +serde_json = "0.7.0" diff --git a/parity/rpctest.rs b/rpc/rpctest/src/main.rs similarity index 90% rename from parity/rpctest.rs rename to rpc/rpctest/src/main.rs index 4fd31b134..6cc747959 100644 --- a/parity/rpctest.rs +++ b/rpc/rpctest/src/main.rs @@ -37,9 +37,10 @@ use ethcore::ethereum; use ethcore::client::{BlockChainClient, Client, ClientConfig}; use devtools::RandomTempPath; use util::IoChannel; -use rpc::v1::tests::helpers::{TestSyncProvider, Config as SyncConfig, TestMinerService, TestAccountProvider}; -use rpc::v1::{Eth, EthClient}; +use rpc::v1::tests::helpers::{TestSyncProvider, Config as SyncConfig, TestMinerService, TestAccountProvider, TestAccount}; +use rpc::v1::{Eth, EthClient, EthFilter, EthFilterClient}; use util::panics::MayPanic; +use util::hash::Address; const USAGE: &'static str = r#" Parity rpctest client. @@ -86,8 +87,9 @@ impl Configuration { process::exit(1); }); - let tests: ethjson::blockchain::Test = serde_json::from_reader(file).unwrap_or_else(|_| { + let tests: ethjson::blockchain::Test = serde_json::from_reader(file).unwrap_or_else(|err| { println!("Invalid json file."); + println!("{:?}", err); process::exit(2); }); @@ -117,9 +119,12 @@ impl Configuration { })); let miner = Arc::new(TestMinerService::default()); - let accounts = Arc::new(TestAccountProvider::new(HashMap::new())); + let mut accs = HashMap::new(); + accs.insert(Address::from(1), TestAccount::new("test")); + let accounts = Arc::new(TestAccountProvider::new(accs)); let server = rpc::RpcServer::new(); server.add_delegate(EthClient::new(&client, &sync, &accounts, &miner).to_delegate()); + server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); let url = format!("{}:{}", self.args.flag_jsonrpc_addr, self.args.flag_jsonrpc_port); let panic_handler = server.start_http(url.as_ref(), "*", 1); diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 5cd1b2966..9b2588670 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -103,7 +103,8 @@ impl EthClient timestamp: U256::from(view.timestamp()), difficulty: view.difficulty(), total_difficulty: total_difficulty, - uncles: vec![], + nonce: view.seal().get(1).map_or_else(H64::zero, |r| H64::from_slice(r)), + uncles: block_view.uncle_hashes(), transactions: { if include_txs { BlockTransactions::Full(block_view.localized_transactions().into_iter().map(From::from).collect()) @@ -111,7 +112,7 @@ impl EthClient BlockTransactions::Hashes(block_view.transaction_hashes()) } }, - extra_data: Bytes::default() + extra_data: Bytes::new(view.extra_data()) }; to_value(&block) }, @@ -229,8 +230,8 @@ impl Eth for EthClient fn block_transaction_count_by_hash(&self, params: Params) -> Result { from_params::<(H256,)>(params) .and_then(|(hash,)| // match - to_value(&take_weak!(self.client).block(BlockId::Hash(hash)) - .map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count())))) + take_weak!(self.client).block(BlockId::Hash(hash)) + .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count())))) } fn block_transaction_count_by_number(&self, params: Params) -> Result { @@ -239,24 +240,24 @@ impl Eth for EthClient BlockNumber::Pending => to_value( &U256::from(take_weak!(self.miner).status().transactions_in_pending_block) ), - _ => to_value(&take_weak!(self.client).block(block_number.into()) - .map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count()))) + _ => take_weak!(self.client).block(block_number.into()) + .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count()))) }) } fn block_uncles_count_by_hash(&self, params: Params) -> Result { from_params::<(H256,)>(params) .and_then(|(hash,)| - to_value(&take_weak!(self.client).block(BlockId::Hash(hash)) - .map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).uncles_count())))) + take_weak!(self.client).block(BlockId::Hash(hash)) + .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count())))) } fn block_uncles_count_by_number(&self, params: Params) -> Result { from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { BlockNumber::Pending => to_value(&U256::from(0)), - _ => to_value(&take_weak!(self.client).block(block_number.into()) - .map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).uncles_count()))) + _ => take_weak!(self.client).block(block_number.into()) + .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count()))) }) } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 389de103b..a5f318350 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -224,7 +224,7 @@ fn rpc_eth_block_transaction_count_by_hash() { "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned())); } @@ -264,7 +264,7 @@ fn rpc_eth_uncle_count_by_block_hash() { "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 22e210392..7f07341bf 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -57,7 +57,7 @@ impl MinerService for TestMinerService { where T: Fn(&Address) -> AccountDetails { unimplemented!(); } /// Returns hashes of transactions currently in pending - fn pending_transactions_hashes(&self) -> Vec { unimplemented!(); } + fn pending_transactions_hashes(&self) -> Vec { vec![] } /// Removes all transactions from the queue and restart mining operation. fn clear_and_reset(&self, _chain: &BlockChainClient) { unimplemented!(); } diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 2457efcf8..ac334dd2b 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -63,7 +63,8 @@ pub struct Block { pub difficulty: U256, #[serde(rename="totalDifficulty")] pub total_difficulty: U256, - pub uncles: Vec, + pub nonce: H64, + pub uncles: Vec, pub transactions: BlockTransactions } @@ -78,7 +79,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x00"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}]"#); let t = BlockTransactions::Hashes(vec![H256::default()]); let serialized = serde_json::to_string(&t).unwrap(); @@ -104,11 +105,12 @@ mod tests { timestamp: U256::default(), difficulty: U256::default(), total_difficulty: U256::default(), + nonce: H64::default(), uncles: vec![], transactions: BlockTransactions::Hashes(vec![]) }; let serialized = serde_json::to_string(&block).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x00","gasUsed":"0x00","gasLimit":"0x00","extraData":"0x00","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x00","difficulty":"0x00","totalDifficulty":"0x00","uncles":[],"transactions":[]}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x00","gasUsed":"0x00","gasLimit":"0x00","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x00","difficulty":"0x00","totalDifficulty":"0x00","nonce":"0x0000000000000000","uncles":[],"transactions":[]}"#); } } diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 0b14c30e8..8c47806f8 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -20,7 +20,7 @@ use serde::de::Visitor; use util::common::FromHex; /// Wrapper structure around vector of bytes. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Default)] pub struct Bytes(Vec); impl Bytes { @@ -31,13 +31,6 @@ impl Bytes { pub fn to_vec(self) -> Vec { let Bytes(x) = self; x } } -impl Default for Bytes { - fn default() -> Self { - // default serialized value is 0x00 - Bytes(vec![0]) - } -} - impl Serialize for Bytes { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 232cf0bf3..d809d19b4 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -67,7 +67,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x00"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x"}"#); } } diff --git a/util/src/hash.rs b/util/src/hash.rs index fce0720d1..b7fddbe8b 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -63,7 +63,8 @@ pub trait FixedHash: Sized + BytesConvertable + Populatable + FromStr + Default fn low_u64(&self) -> u64; } -fn clean_0x(s: &str) -> &str { +/// Return `s` without the `0x` at the beginning of it, if any. +pub fn clean_0x(s: &str) -> &str { if s.len() >= 2 && &s[0..2] == "0x" { &s[2..] } else { diff --git a/util/src/network/host.rs b/util/src/network/host.rs index 02c576424..6baf0cf76 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -105,6 +105,14 @@ impl NetworkConfiguration { config.listen_address = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", port)).unwrap()); config } + + /// Create new default configuration for localhost-only connection with random port (usefull for testing) + pub fn new_local() -> NetworkConfiguration { + let mut config = NetworkConfiguration::new(); + config.listen_address = Some(SocketAddr::from_str("127.0.0.1:0").unwrap()); + config.nat_enabled = false; + config + } } // Tokens @@ -269,12 +277,12 @@ pub struct HostInfo { pub protocol_version: u32, /// Client identifier pub client_version: String, - /// TCP connection port. - pub listen_port: u16, /// Registered capabilities (handlers) pub capabilities: Vec, + /// Local address + discovery port + pub local_endpoint: NodeEndpoint, /// Public address + discovery port - public_endpoint: NodeEndpoint, + pub public_endpoint: Option, } impl HostInfo { @@ -307,7 +315,7 @@ struct ProtocolTimer { /// Root IO handler. Manages protocol handlers, IO timers and network connections. pub struct Host where Message: Send + Sync + Clone { pub info: RwLock, - tcp_listener: Mutex>, + tcp_listener: Mutex, handshakes: Arc>>, sessions: Arc>>, discovery: Mutex>, @@ -321,13 +329,12 @@ pub struct Host where Message: Send + Sync + Clone { impl Host where Message: Send + Sync + Clone { /// Create a new instance - pub fn new(config: NetworkConfiguration) -> Host { - let listen_address = match config.listen_address { + pub fn new(config: NetworkConfiguration) -> Result, UtilError> { + let mut listen_address = match config.listen_address { None => SocketAddr::from_str("0.0.0.0:30304").unwrap(), Some(addr) => addr, }; - let udp_port = config.udp_port.unwrap_or(listen_address.port()); let keys = if let Some(ref secret) = config.use_secret { KeyPair::from_secret(secret.clone()).unwrap() } else { @@ -342,7 +349,12 @@ impl Host where Message: Send + Sync + Clone { |s| KeyPair::from_secret(s).expect("Error creating node secret key")) }; let path = config.config_path.clone(); + // Setup the server socket + let tcp_listener = try!(TcpListener::bind(&listen_address)); + listen_address = SocketAddr::new(listen_address.ip(), try!(tcp_listener.local_addr()).port()); + let udp_port = config.udp_port.unwrap_or(listen_address.port()); let local_endpoint = NodeEndpoint { address: listen_address, udp_port: udp_port }; + let mut host = Host:: { info: RwLock::new(HostInfo { keys: keys, @@ -350,12 +362,12 @@ impl Host where Message: Send + Sync + Clone { nonce: H256::random(), protocol_version: PROTOCOL_VERSION, client_version: version(), - listen_port: 0, capabilities: Vec::new(), - public_endpoint: local_endpoint, // will be replaced by public once it is resolved + public_endpoint: None, + local_endpoint: local_endpoint, }), discovery: Mutex::new(None), - tcp_listener: Mutex::new(None), + tcp_listener: Mutex::new(tcp_listener), handshakes: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_HANDSHAKE, MAX_HANDSHAKES))), sessions: Arc::new(RwLock::new(Slab::new_starting_at(FIRST_SESSION, MAX_SESSIONS))), nodes: RwLock::new(NodeTable::new(path)), @@ -365,14 +377,12 @@ impl Host where Message: Send + Sync + Clone { stats: Arc::new(NetworkStats::default()), pinned_nodes: Vec::new(), }; - let port = listen_address.port(); - host.info.write().unwrap().deref_mut().listen_port = port; let boot_nodes = host.info.read().unwrap().config.boot_nodes.clone(); for n in boot_nodes { host.add_node(&n); } - host + Ok(host) } pub fn stats(&self) -> Arc { @@ -397,50 +407,50 @@ impl Host where Message: Send + Sync + Clone { self.info.read().unwrap().client_version.clone() } - pub fn client_url(&self) -> String { - format!("{}", Node::new(self.info.read().unwrap().id().clone(), self.info.read().unwrap().public_endpoint.clone())) + pub fn external_url(&self) -> Option { + self.info.read().unwrap().public_endpoint.as_ref().map(|e| format!("{}", Node::new(self.info.read().unwrap().id().clone(), e.clone()))) } - fn init_public_interface(&self, io: &IoContext>) { + pub fn local_url(&self) -> String { + let r = format!("{}", Node::new(self.info.read().unwrap().id().clone(), self.info.read().unwrap().local_endpoint.clone())); + println!("{}", r); + r + } + + fn init_public_interface(&self, io: &IoContext>) -> Result<(), UtilError> { io.clear_timer(INIT_PUBLIC).unwrap(); - let mut tcp_listener = self.tcp_listener.lock().unwrap(); - if tcp_listener.is_some() { - return; + if self.info.read().unwrap().public_endpoint.is_some() { + return Ok(()); } - // public_endpoint in host info contains local adderss at this point - let listen_address = self.info.read().unwrap().public_endpoint.address.clone(); - let udp_port = self.info.read().unwrap().config.udp_port.unwrap_or(listen_address.port()); + let local_endpoint = self.info.read().unwrap().local_endpoint.clone(); let public_address = self.info.read().unwrap().config.public_address.clone(); let public_endpoint = match public_address { None => { - let public_address = select_public_address(listen_address.port()); - let local_endpoint = NodeEndpoint { address: public_address, udp_port: udp_port }; + let public_address = select_public_address(local_endpoint.address.port()); + let public_endpoint = NodeEndpoint { address: public_address, udp_port: local_endpoint.udp_port }; if self.info.read().unwrap().config.nat_enabled { match map_external_address(&local_endpoint) { Some(endpoint) => { - info!("NAT mappped to external address {}", endpoint.address); + info!("NAT mapped to external address {}", endpoint.address); endpoint }, - None => local_endpoint + None => public_endpoint } } else { - local_endpoint + public_endpoint } } - Some(addr) => NodeEndpoint { address: addr, udp_port: udp_port } + Some(addr) => NodeEndpoint { address: addr, udp_port: local_endpoint.udp_port } }; - // Setup the server socket - *tcp_listener = Some(TcpListener::bind(&listen_address).unwrap()); - self.info.write().unwrap().public_endpoint = public_endpoint.clone(); - io.register_stream(TCP_ACCEPT).expect("Error registering TCP listener"); - info!("Public node URL: {}", self.client_url()); + self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone()); + info!("Public node URL: {}", self.external_url().unwrap()); // Initialize discovery. let discovery = { let info = self.info.read().unwrap(); if info.config.discovery_enabled && !info.config.pin { - Some(Discovery::new(&info.keys, listen_address.clone(), public_endpoint, DISCOVERY)) + Some(Discovery::new(&info.keys, public_endpoint.address.clone(), public_endpoint, DISCOVERY)) } else { None } }; @@ -454,6 +464,8 @@ impl Host where Message: Send + Sync + Clone { io.register_timer(DISCOVERY_ROUND, 300).expect("Error registering discovery timer"); *self.discovery.lock().unwrap().deref_mut() = Some(discovery); } + try!(io.register_stream(TCP_ACCEPT)); + Ok(()) } fn maintain_network(&self, io: &IoContext>) { @@ -567,7 +579,7 @@ impl Host where Message: Send + Sync + Clone { fn accept(&self, io: &IoContext>) { trace!(target: "network", "Accepting incoming connection"); loop { - let socket = match self.tcp_listener.lock().unwrap().as_ref().unwrap().accept() { + let socket = match self.tcp_listener.lock().unwrap().accept() { Ok(None) => break, Ok(Some((sock, _addr))) => sock, Err(e) => { @@ -861,7 +873,8 @@ impl IoHandler> for Host where Messa fn timeout(&self, io: &IoContext>, token: TimerToken) { match token { IDLE => self.maintain_network(io), - INIT_PUBLIC => self.init_public_interface(io), + INIT_PUBLIC => self.init_public_interface(io).unwrap_or_else(|e| + warn!("Error initializing public interface: {:?}", e)), FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io), FIRST_HANDSHAKE ... LAST_HANDSHAKE => self.connection_timeout(token, io), DISCOVERY_REFRESH => { @@ -945,7 +958,7 @@ impl IoHandler> for Host where Messa } } DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().register_socket(event_loop).expect("Error registering discovery socket"), - TCP_ACCEPT => event_loop.register(self.tcp_listener.lock().unwrap().as_ref().unwrap(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"), + TCP_ACCEPT => event_loop.register(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"), _ => warn!("Unexpected stream registration") } } @@ -986,7 +999,7 @@ impl IoHandler> for Host where Messa } } DISCOVERY => self.discovery.lock().unwrap().as_ref().unwrap().update_registration(event_loop).expect("Error reregistering discovery socket"), - TCP_ACCEPT => event_loop.reregister(self.tcp_listener.lock().unwrap().as_ref().unwrap(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"), + TCP_ACCEPT => event_loop.reregister(self.tcp_listener.lock().unwrap().deref(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"), _ => warn!("Unexpected stream update") } } @@ -1054,6 +1067,6 @@ fn host_client_url() { let mut config = NetworkConfiguration::new(); let key = h256_from_hex("6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2"); config.use_secret = Some(key); - let host: Host = Host::new(config); - assert!(host.client_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); + let host: Host = Host::new(config).unwrap(); + assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); } diff --git a/util/src/network/ip_utils.rs b/util/src/network/ip_utils.rs index b37a47064..27ff29737 100644 --- a/util/src/network/ip_utils.rs +++ b/util/src/network/ip_utils.rs @@ -208,6 +208,7 @@ fn can_select_public_address() { assert!(pub_address.port() == 40477); } +#[ignore] #[test] fn can_map_external_address_or_fail() { let pub_address = select_public_address(40478); diff --git a/util/src/network/mod.rs b/util/src/network/mod.rs index 50645f2be..29f3d166c 100644 --- a/util/src/network/mod.rs +++ b/util/src/network/mod.rs @@ -56,7 +56,7 @@ //! } //! //! fn main () { -//! let mut service = NetworkService::::start(NetworkConfiguration::new_with_port(40412)).expect("Error creating network service"); +//! let mut service = NetworkService::::start(NetworkConfiguration::new_local()).expect("Error creating network service"); //! service.register_protocol(Arc::new(MyHandler), "myproto", &[1u8]); //! //! // Wait for quit condition diff --git a/util/src/network/service.rs b/util/src/network/service.rs index 7b9388e85..49957f7e7 100644 --- a/util/src/network/service.rs +++ b/util/src/network/service.rs @@ -28,6 +28,7 @@ use io::*; pub struct NetworkService where Message: Send + Sync + Clone + 'static { io_service: IoService>, host_info: String, + host: Arc>, stats: Arc, panic_handler: Arc } @@ -39,15 +40,16 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat let mut io_service = try!(IoService::>::start()); panic_handler.forward_from(&io_service); - let host = Arc::new(Host::new(config)); + let host = Arc::new(try!(Host::new(config))); let stats = host.stats().clone(); let host_info = host.client_version(); - try!(io_service.register_handler(host)); + try!(io_service.register_handler(host.clone())); Ok(NetworkService { io_service: io_service, host_info: host_info, stats: stats, - panic_handler: panic_handler + panic_handler: panic_handler, + host: host, }) } @@ -71,12 +73,21 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat &mut self.io_service } - /// Returns underlying io service. + /// Returns network statistics. pub fn stats(&self) -> &NetworkStats { &self.stats } -} + /// Returns external url if available. + pub fn external_url(&self) -> Option { + self.host.external_url() + } + + /// Returns external url if available. + pub fn local_url(&self) -> String { + self.host.local_url() + } +} impl MayPanic for NetworkService where Message: Send + Sync + Clone + 'static { fn on_panic(&self, closure: F) where F: OnPanicListener { diff --git a/util/src/network/session.rs b/util/src/network/session.rs index 2f30d7376..7dbcc4229 100644 --- a/util/src/network/session.rs +++ b/util/src/network/session.rs @@ -315,7 +315,7 @@ impl Session { .append(&host.protocol_version) .append(&host.client_version) .append(&host.capabilities) - .append(&host.listen_port) + .append(&host.local_endpoint.address.port()) .append(host.id()); self.connection.send_packet(&rlp.out()) } diff --git a/util/src/network/tests.rs b/util/src/network/tests.rs index f8ef588f6..8df3b4028 100644 --- a/util/src/network/tests.rs +++ b/util/src/network/tests.rs @@ -97,21 +97,21 @@ impl NetworkProtocolHandler for TestProtocol { #[test] fn net_service() { - let mut service = NetworkService::::start(NetworkConfiguration::new_with_port(40414)).expect("Error creating network service"); + let mut service = NetworkService::::start(NetworkConfiguration::new_local()).expect("Error creating network service"); service.register_protocol(Arc::new(TestProtocol::new(false)), "myproto", &[1u8]).unwrap(); } #[test] fn net_connect() { + ::log::init_log(); let key1 = KeyPair::create().unwrap(); - let mut config1 = NetworkConfiguration::new_with_port(30354); + let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); - config1.nat_enabled = false; config1.boot_nodes = vec![ ]; - let mut config2 = NetworkConfiguration::new_with_port(30355); - config2.boot_nodes = vec![ format!("enode://{}@127.0.0.1:30354", key1.public().hex()) ]; - config2.nat_enabled = false; let mut service1 = NetworkService::::start(config1).unwrap(); + let mut config2 = NetworkConfiguration::new_local(); + info!("net_connect: local URL: {}", service1.local_url()); + config2.boot_nodes = vec![ service1.local_url() ]; let mut service2 = NetworkService::::start(config2).unwrap(); let handler1 = TestProtocol::register(&mut service1, false); let handler2 = TestProtocol::register(&mut service2, false); @@ -125,14 +125,12 @@ fn net_connect() { #[test] fn net_disconnect() { let key1 = KeyPair::create().unwrap(); - let mut config1 = NetworkConfiguration::new_with_port(30364); + let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); - config1.nat_enabled = false; config1.boot_nodes = vec![ ]; - let mut config2 = NetworkConfiguration::new_with_port(30365); - config2.boot_nodes = vec![ format!("enode://{}@127.0.0.1:30364", key1.public().hex()) ]; - config2.nat_enabled = false; let mut service1 = NetworkService::::start(config1).unwrap(); + let mut config2 = NetworkConfiguration::new_local(); + config2.boot_nodes = vec![ service1.local_url() ]; let mut service2 = NetworkService::::start(config2).unwrap(); let handler1 = TestProtocol::register(&mut service1, false); let handler2 = TestProtocol::register(&mut service2, true); @@ -145,7 +143,7 @@ fn net_disconnect() { #[test] fn net_timeout() { - let config = NetworkConfiguration::new_with_port(30346); + let config = NetworkConfiguration::new_local(); let mut service = NetworkService::::start(config).unwrap(); let handler = TestProtocol::register(&mut service, false); while !handler.got_timeout() {