diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9985358d8..2833a46c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,7 @@ linux-stable: - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5" -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu tags: - rust @@ -107,7 +107,7 @@ linux-centos: - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5 -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust @@ -146,7 +146,7 @@ linux-i686: - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5" -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust @@ -192,7 +192,7 @@ linux-armv7: - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5" -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust @@ -238,7 +238,7 @@ linux-arm: - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5" -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust @@ -277,7 +277,7 @@ linux-armv6: - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5 -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust @@ -322,7 +322,7 @@ linux-aarch64: - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5" -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust @@ -359,7 +359,7 @@ darwin: - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" -# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM + - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - osx @@ -421,7 +421,7 @@ windows: - aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5 - aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip - aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5 -# - curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM% + - curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM% - curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1338/push-build/%CI_BUILD_REF_NAME%/%PLATFORM% tags: - rust-windows @@ -535,7 +535,7 @@ push-release: - triggers image: ethcore/rust:stable script: -# - curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF + - curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF - curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF tags: - curl diff --git a/Cargo.lock b/Cargo.lock index 45aecc9de..a8a591964 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,7 @@ dependencies = [ "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -44,7 +45,7 @@ dependencies = [ "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -100,14 +101,6 @@ dependencies = [ "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "aster" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bigint" version = "1.0.0" @@ -329,8 +322,8 @@ dependencies = [ "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -413,8 +406,8 @@ dependencies = [ "fetch 0.1.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -426,8 +419,8 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -584,20 +577,19 @@ dependencies = [ "ethsync 1.6.0", "fetch 0.1.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", - "jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)", - "jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-ipc-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-macros 0.2.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", "parity-updater 1.6.0", - "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -612,7 +604,7 @@ dependencies = [ "ethcore-io 1.6.0", "ethcore-rpc 1.6.0", "ethcore-util 1.6.0", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-ui 1.6.0", @@ -631,8 +623,9 @@ dependencies = [ "ethcore-ipc-codegen 1.6.0", "ethcore-ipc-nano 1.6.0", "ethcore-util 1.6.0", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", - "jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-macros 0.2.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-tcp-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", @@ -693,8 +686,8 @@ dependencies = [ "ethcore-util 1.6.0", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -726,8 +719,8 @@ dependencies = [ "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -952,63 +945,69 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" -version = "4.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a" +version = "5.0.0" +source = "git+https://github.com/ethcore/jsonrpc.git#2187ebc32f67620b000afb03a43a3c2b1020abb3" dependencies = [ + "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-http-server" -version = "6.1.1" -source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a" +version = "7.0.0" +source = "git+https://github.com/ethcore/jsonrpc.git#2187ebc32f67620b000afb03a43a3c2b1020abb3" dependencies = [ + "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-ipc-server" -version = "0.2.4" -source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a" +version = "1.0.0" +source = "git+https://github.com/ethcore/jsonrpc.git#2187ebc32f67620b000afb03a43a3c2b1020abb3" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-macros" -version = "0.1.0" -source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a" +version = "0.2.0" +source = "git+https://github.com/ethcore/jsonrpc.git#2187ebc32f67620b000afb03a43a3c2b1020abb3" dependencies = [ - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-tcp-server" -version = "0.1.0" -source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a" +version = "1.0.0" +source = "git+https://github.com/ethcore/jsonrpc.git#2187ebc32f67620b000afb03a43a3c2b1020abb3" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1472,13 +1471,13 @@ dependencies = [ "ethcore-signer 1.6.0", "ethcore-util 1.6.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", @@ -1503,7 +1502,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#e21ae69190fa390f5550e5cf17f5ea362ba4db41" +source = "git+https://github.com/ethcore/js-precompiled.git#73497e202735f4a6d2bf55de6509fc010419871b" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1639,15 +1638,6 @@ dependencies = [ "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "quasi" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "quasi_codegen" version = "0.11.0" @@ -1658,17 +1648,6 @@ dependencies = [ "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "quasi_codegen" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.42.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "quick-error" version = "1.1.0" @@ -1679,6 +1658,11 @@ name = "quine-mc_cluskey" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "quote" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rand" version = "0.3.14" @@ -1723,7 +1707,7 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1912,29 +1896,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_codegen" -version = "0.8.4" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi_codegen 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.42.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen_internals 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_codegen_internals" -version = "0.7.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2032,6 +2014,15 @@ name = "strsim" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syntex" version = "0.33.0" @@ -2042,29 +2033,29 @@ dependencies = [ [[package]] name = "syntex" -version = "0.42.2" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_errors 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syntex_errors" -version = "0.42.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_pos 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syntex_pos" -version = "0.42.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2085,15 +2076,15 @@ dependencies = [ [[package]] name = "syntex_syntax" -version = "0.42.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_errors 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_pos 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2368,7 +2359,6 @@ dependencies = [ "checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4" "checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975" "checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a" -"checksum aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4df293303e8a52e1df7984ac1415e195f5fcbf51e4bb7bda54557861a3954a08" "checksum bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2311bcd71b281e142a095311c22509f0d6bcd87b3000d7dbaa810929b9d6f6ae" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" "checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d" @@ -2416,11 +2406,11 @@ dependencies = [ "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" "checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" -"checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" -"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)" = "" -"checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)" = "" -"checksum jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" -"checksum jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-core 5.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-http-server 7.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-ipc-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-macros 0.2.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-tcp-server 1.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" @@ -2482,11 +2472,10 @@ dependencies = [ "checksum primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5" "checksum primal-sieve 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa73fd87e5984a00bdb4c1b14d3d5d6d0bad01b2caaaf924c16ab7260ac946c" "checksum quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b26543b563704e7d87f3ec7cfafb713010a905c5f1b155a8ab66863af43ca578" -"checksum quasi 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb7eaef226a434a570fa336bc99502c4f5878208c1ebdd83b2d0bc37b1b1c34c" "checksum quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0881d9a45d5f9ebe4a7e77742f8c604f3658c212baf8dd711a692dd000bc648c" -"checksum quasi_codegen 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62e90381b315dfd00bfe6efbfeeec9f03a6d66159c3a5c8411b6c550d24b08fd" "checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c" "checksum quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6683b0e23d80813b1a535841f0048c1537d3f86d63c999e8373b39a9b0eb74a" +"checksum quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6732e32663c9c271bfc7c1823486b471f18c47a2dbf87c066897b7b51afc83be" "checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5" "checksum rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "655df67c314c30fa3055a365eae276eb88aa4f3413a352a1ab32c1320eda41ea" "checksum regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)" = "b4329b8928a284580a1c63ec9d846b12f6d3472317243ff7077aff11f23f2b29" @@ -2510,9 +2499,9 @@ dependencies = [ "checksum semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae2ff60ecdb19c255841c066cbfa5f8c2a4ada1eb3ae47c77ab6667128da71f5" "checksum semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e88e43a5a74dd2a11707f9c21dfd4a423c66bd871df813227bb0a3e78f3a1ae9" "checksum serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "58a19c0871c298847e6b68318484685cd51fa5478c0c905095647540031356e5" -"checksum serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e422ae53d7933f59c6ff57e7b5870b5c9094b1f473f78ec33d89f8a692c3ec02" -"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21" -"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc" +"checksum serde_codegen 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ce29a6ae259579707650ec292199b5fed2c0b8e2a4bdc994452d24d1bcf2242a" +"checksum serde_codegen_internals 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "59933a62554548c690d2673c5164f0c4a46be7c5731edfd94b0ecb1048940732" +"checksum serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7d3c184d35801fb8b32b46a7d58d57dbcc150b0eb2b46a1eb79645e8ecfd5b" "checksum serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53d4ebaa8d1d4f90d1b63dfca81ccd98ac20e1e479dbae393cbaf60f6fecd8d8" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" "checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" @@ -2527,12 +2516,13 @@ dependencies = [ "checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf" "checksum stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)" = "" "checksum strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825" +"checksum syn 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94e7d81ecd16d39f16193af05b8d5a0111b9d8d2f3f78f31760f327a247da777" "checksum syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393b6dd0889df2b064beeea954cfda6bc2571604ac460deeae0fed55a53988af" -"checksum syntex 0.42.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0a30b08a6b383a22e5f6edc127d169670d48f905bb00ca79a00ea3e442ebe317" -"checksum syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)" = "04c48f32867b6114449155b2a82114b86d4b09e1bddb21c47ff104ab9172b646" -"checksum syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd49988e52451813c61fecbe9abb5cfd4e1b7bb6cdbb980a6fbcbab859171a6" +"checksum syntex 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bd253b0d7d787723a33384d426f0ebec7f8edccfaeb2022d0177162bb134da0" +"checksum syntex_errors 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "84822a1178204a191239ad844599f8c85c128cf9f4173397def4eb46b55b0aa1" +"checksum syntex_pos 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a43abded5057c75bac8555e46ec913ce502efb418267b1ab8e9783897470c7db" "checksum syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44bded3cabafc65c90b663b1071bd2d198a9ab7515e6ce729e4570aaf53c407e" -"checksum syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7628a0506e8f9666fdabb5f265d0059b059edac9a3f810bda077abb5d826bd8d" +"checksum syntex_syntax 0.50.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ef781e4b60f03431f1b5b59843546ce60ae029a787770cf8e0969ac1fd063a5" "checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281" diff --git a/Cargo.toml b/Cargo.toml index 8026662d1..359ec77b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,14 +26,13 @@ lazy_static = "0.2" regex = "0.1" isatty = "0.1" toml = "0.2" -serde = "0.8.0" -serde_json = "0.8.0" +serde = "0.8" +serde_json = "0.8" app_dirs = "1.1.1" +fdlimit = "0.1" hyper = { version = "0.9", default-features = false } ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } -fdlimit = "0.1" -clippy = { version = "0.0.103", optional = true} -rlp = { path = "util/rlp" } +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } ethsync = { path = "sync" } ethcore = { path = "ethcore" } ethcore-util = { path = "util" } @@ -44,15 +43,17 @@ ethcore-signer = { path = "signer" } ethcore-ipc = { path = "ipc/rpc" } ethcore-ipc-nano = { path = "ipc/nano" } ethcore-ipc-hypervisor = { path = "ipc/hypervisor" } +ethcore-light = { path = "ethcore/light" } ethcore-logger = { path = "logger" } ethcore-stratum = { path = "stratum" } -ethcore-dapps = { path = "dapps", optional = true } +rlp = { path = "util/rlp" } rpc-cli = { path = "rpc_cli" } parity-rpc-client = { path = "rpc_client" } -ethcore-light = { path = "ethcore/light" } parity-hash-fetch = { path = "hash-fetch" } parity-updater = { path = "updater" } parity-reactor = { path = "util/reactor" } +ethcore-dapps = { path = "dapps", optional = true } +clippy = { version = "0.0.103", optional = true} [target.'cfg(windows)'.dependencies] winapi = "0.2" diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 64ee0c341..55bc67ff3 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -15,30 +15,6 @@ // along with Parity. If not, see . //! Ethcore Webapplications for Parity -//! ``` -//! extern crate jsonrpc_core; -//! extern crate ethcore_dapps; -//! -//! use std::sync::Arc; -//! use jsonrpc_core::IoHandler; -//! use ethcore_dapps::*; -//! -//! struct SayHello; -//! impl MethodCommand for SayHello { -//! fn execute(&self, _params: Params) -> Result { -//! Ok(Value::String("hello".to_string())) -//! } -//! } -//! -//! fn main() { -//! let io = IoHandler::new(); -//! io.add_method("say_hello", SayHello); -//! let _server = Server::start_unsecure_http( -//! &"127.0.0.1:3030".parse().unwrap(), -//! Arc::new(io) -//! ); -//! } -//! ``` //! #![warn(missing_docs)] #![cfg_attr(feature="nightly", plugin(clippy))] @@ -93,11 +69,11 @@ use std::sync::{Arc, Mutex}; use std::net::SocketAddr; use std::collections::HashMap; -use hash_fetch::urlhint::ContractClient; +use ethcore_rpc::Metadata; use fetch::{Fetch, Client as FetchClient}; -use jsonrpc_core::{IoHandler, IoDelegate}; +use hash_fetch::urlhint::ContractClient; +use jsonrpc_core::reactor::RpcHandler; use router::auth::{Authorization, NoAuth, HttpBasicAuth}; -use ethcore_rpc::Extendable; use parity_reactor::Remote; use self::apps::{HOME_PAGE, DAPPS_DOMAIN}; @@ -126,32 +102,26 @@ impl WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync { pub struct ServerBuilder { dapps_path: PathBuf, extra_dapps: Vec, - handler: Arc, registrar: Arc, sync_status: Arc, web_proxy_tokens: Arc, signer_address: Option<(String, u16)>, + allowed_hosts: Option>, remote: Remote, fetch: Option, } -impl Extendable for ServerBuilder { - fn add_delegate(&self, delegate: IoDelegate) { - self.handler.add_delegate(delegate); - } -} - impl ServerBuilder { /// Construct new dapps server pub fn new>(dapps_path: P, registrar: Arc, remote: Remote) -> Self { ServerBuilder { dapps_path: dapps_path.as_ref().to_owned(), extra_dapps: vec![], - handler: Arc::new(IoHandler::new()), registrar: registrar, sync_status: Arc::new(|| false), web_proxy_tokens: Arc::new(|_| false), signer_address: None, + allowed_hosts: Some(vec![]), remote: remote, fetch: None, } @@ -164,11 +134,11 @@ impl ServerBuilder { ServerBuilder { dapps_path: self.dapps_path, extra_dapps: vec![], - handler: self.handler, registrar: self.registrar, sync_status: self.sync_status, web_proxy_tokens: self.web_proxy_tokens, signer_address: self.signer_address, + allowed_hosts: self.allowed_hosts, remote: self.remote, fetch: Some(fetch), } @@ -192,6 +162,14 @@ impl ServerBuilder { self } + /// Change allowed hosts. + /// `None` - All hosts are allowed + /// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address) + pub fn allowed_hosts(mut self, allowed_hosts: Option>) -> Self { + self.allowed_hosts = allowed_hosts; + self + } + /// Change extra dapps paths (apart from `dapps_path`) pub fn extra_dapps>(mut self, extra_dapps: &[P]) -> Self { self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect(); @@ -200,39 +178,41 @@ impl ServerBuilder { /// Asynchronously start server with no authentication, /// returns result with `Server` handle on success or an error. - pub fn start_unsecured_http(self, addr: &SocketAddr, hosts: Option>) -> Result { + pub fn start_unsecured_http(self, addr: &SocketAddr, handler: RpcHandler) -> Result { + let fetch = self.fetch_client()?; Server::start_http( addr, - hosts, + self.allowed_hosts, NoAuth, - self.handler.clone(), - self.dapps_path.clone(), - self.extra_dapps.clone(), - self.signer_address.clone(), - self.registrar.clone(), - self.sync_status.clone(), - self.web_proxy_tokens.clone(), - self.remote.clone(), - self.fetch_client()?, + handler, + self.dapps_path, + self.extra_dapps, + self.signer_address, + self.registrar, + self.sync_status, + self.web_proxy_tokens, + self.remote, + fetch, ) } /// Asynchronously start server with `HTTP Basic Authentication`, /// return result with `Server` handle on success or an error. - pub fn start_basic_auth_http(self, addr: &SocketAddr, hosts: Option>, username: &str, password: &str) -> Result { + pub fn start_basic_auth_http(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler) -> Result { + let fetch = self.fetch_client()?; Server::start_http( addr, - hosts, + self.allowed_hosts, HttpBasicAuth::single_user(username, password), - self.handler.clone(), - self.dapps_path.clone(), - self.extra_dapps.clone(), - self.signer_address.clone(), - self.registrar.clone(), - self.sync_status.clone(), - self.web_proxy_tokens.clone(), - self.remote.clone(), - self.fetch_client()?, + handler, + self.dapps_path, + self.extra_dapps, + self.signer_address, + self.registrar, + self.sync_status, + self.web_proxy_tokens, + self.remote, + fetch, ) } @@ -281,7 +261,7 @@ impl Server { addr: &SocketAddr, hosts: Option>, authorization: A, - handler: Arc, + handler: RpcHandler, dapps_path: PathBuf, extra_dapps: Vec, signer_address: Option<(String, u16)>, diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index 1abae3b49..03bd3b34e 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -17,13 +17,15 @@ use std::sync::{Arc, Mutex}; use hyper; -use jsonrpc_core::{IoHandler, ResponseHandler, Request, Response}; -use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin, RpcHandler}; +use ethcore_rpc::{Metadata, Origin}; +use jsonrpc_core::reactor::RpcHandler; +use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor}; use endpoint::{Endpoint, EndpointPath, Handler}; -pub fn rpc(handler: Arc, panic_handler: Arc () + Send>>>>) -> Box { +pub fn rpc(handler: RpcHandler, panic_handler: Arc () + Send>>>>) -> Box { Box::new(RpcEndpoint { - handler: Arc::new(RpcMiddleware::new(handler)), + handler: handler, + meta_extractor: Arc::new(MetadataExtractor), panic_handler: panic_handler, cors_domain: None, // NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router. @@ -32,7 +34,8 @@ pub fn rpc(handler: Arc, panic_handler: Arc } struct RpcEndpoint { - handler: Arc, + handler: RpcHandler, + meta_extractor: Arc>, panic_handler: Arc () + Send>>>>, cors_domain: Option>, allowed_hosts: Option>, @@ -42,7 +45,7 @@ impl Endpoint for RpcEndpoint { fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box { let panic_handler = PanicHandler { handler: self.panic_handler.clone() }; Box::new(ServerHandler::new( - self.handler.clone(), + Rpc::new(self.handler.clone(), self.meta_extractor.clone()), self.cors_domain.clone(), self.allowed_hosts.clone(), panic_handler, @@ -51,85 +54,19 @@ impl Endpoint for RpcEndpoint { } } -struct RpcMiddleware { - handler: Arc, - methods: Vec, -} - -impl RpcMiddleware { - fn new(handler: Arc) -> Self { - RpcMiddleware { - handler: handler, - methods: vec!["eth_accounts".into(), "parity_accountsInfo".into()], - } - } - - /// Appends additional parameter for specific calls. - fn augment_request(&self, request: &mut Request, meta: Option) { - use jsonrpc_core::{Call, Params, to_value}; - - fn augment_call(call: &mut Call, meta: Option<&Meta>, methods: &Vec) { - match (call, meta) { - (&mut Call::MethodCall(ref mut method_call), Some(meta)) if methods.contains(&method_call.method) => { - let session = to_value(&meta.app_id); - - let params = match method_call.params { - Some(Params::Array(ref vec)) if vec.len() == 0 => Some(Params::Array(vec![session])), - // invalid params otherwise - _ => None, - }; - - method_call.params = params; - }, - _ => {} - } - } - - match *request { - Request::Single(ref mut call) => augment_call(call, meta.as_ref(), &self.methods), - Request::Batch(ref mut vec) => { - for mut call in vec { - augment_call(call, meta.as_ref(), &self.methods) - } - }, - } - } -} - -#[derive(Debug)] -struct Meta { - app_id: String, -} - -impl RpcHandler for RpcMiddleware { - type Metadata = Meta; - - fn read_metadata(&self, request: &hyper::server::Request) -> Option { - request.headers().get::() +struct MetadataExtractor; +impl HttpMetaExtractor for MetadataExtractor { + fn read_metadata(&self, request: &hyper::server::Request) -> Metadata { + let dapp_id = request.headers().get::() .and_then(|referer| hyper::Url::parse(referer).ok()) .and_then(|url| { url.path_segments() .and_then(|mut split| split.next()) - .map(|app_id| Meta { - app_id: app_id.to_owned(), - }) - }) - } - - fn handle_request(&self, request_str: &str, response_handler: H, meta: Option) where - H: ResponseHandler, Option> + 'static - { - let handler = IoHandler::convert_handler(response_handler); - let request = IoHandler::read_request(request_str); - trace!(target: "rpc", "Request metadata: {:?}", meta); - - match request { - Ok(mut request) => { - self.augment_request(&mut request, meta); - self.handler.request_handler().handle_request(request, handler, None) - }, - Err(error) => handler.send(Some(Response::from(error))), + .map(|app_id| app_id.to_owned()) + }); + Metadata { + dapp_id: dapp_id, + origin: Origin::Dapps, } } } - diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs index 5cc367fcc..02a459830 100644 --- a/dapps/src/tests/helpers/mod.rs +++ b/dapps/src/tests/helpers/mod.rs @@ -16,8 +16,11 @@ use std::env; use std::str; +use std::ops::Deref; use std::sync::Arc; use env_logger::LogBuilder; +use jsonrpc_core::MetaIoHandler; +use jsonrpc_core::reactor::RpcEventLoop; use ServerBuilder; use Server; @@ -42,7 +45,20 @@ fn init_logger() { } } -pub fn init_server(hosts: Option>, process: F, remote: Remote) -> (Server, Arc) where +pub struct ServerLoop { + pub server: Server, + pub event_loop: RpcEventLoop, +} + +impl Deref for ServerLoop { + type Target = Server; + + fn deref(&self) -> &Self::Target { + &self.server + } +} + +pub fn init_server(process: F, remote: Remote) -> (ServerLoop, Arc) where F: FnOnce(ServerBuilder) -> ServerBuilder, B: Fetch, { @@ -50,60 +66,76 @@ pub fn init_server(hosts: Option>, process: F, remote: Remote) let registrar = Arc::new(FakeRegistrar::new()); let mut dapps_path = env::temp_dir(); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); + + // TODO [ToDr] When https://github.com/ethcore/jsonrpc/issues/26 is resolved + // this additional EventLoop wouldn't be needed, we should be able to re-use remote. + let event_loop = RpcEventLoop::spawn(); + let handler = event_loop.handler(Arc::new(MetaIoHandler::default())); let server = process(ServerBuilder::new( &dapps_path, registrar.clone(), remote, )) .signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))) - .start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(); + .start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), handler).unwrap(); ( - server, + ServerLoop { server: server, event_loop: event_loop }, registrar, ) } -pub fn serve_with_auth(user: &str, pass: &str) -> Server { +pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop { init_logger(); let registrar = Arc::new(FakeRegistrar::new()); let mut dapps_path = env::temp_dir(); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); - ServerBuilder::new(&dapps_path, registrar.clone(), Remote::new_sync()) + + let event_loop = RpcEventLoop::spawn(); + let handler = event_loop.handler(Arc::new(MetaIoHandler::default())); + let server = ServerBuilder::new(&dapps_path, registrar, Remote::new(event_loop.remote())) .signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))) - .start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap() + .allowed_hosts(None) + .start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, handler).unwrap(); + ServerLoop { + server: server, + event_loop: event_loop, + } } -pub fn serve_hosts(hosts: Option>) -> Server { - init_server(hosts, |builder| builder, Remote::new_sync()).0 +pub fn serve_hosts(hosts: Option>) -> ServerLoop { + init_server(|builder| builder.allowed_hosts(hosts), Remote::new_sync()).0 } -pub fn serve_with_registrar() -> (Server, Arc) { - init_server(None, |builder| builder, Remote::new_sync()) +pub fn serve_with_registrar() -> (ServerLoop, Arc) { + init_server(|builder| builder.allowed_hosts(None), Remote::new_sync()) } -pub fn serve_with_registrar_and_sync() -> (Server, Arc) { - init_server(None, |builder| { - builder.sync_status(Arc::new(|| true)) +pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc) { + init_server(|builder| { + builder + .sync_status(Arc::new(|| true)) + .allowed_hosts(None) }, Remote::new_sync()) } -pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc) { +pub fn serve_with_registrar_and_fetch() -> (ServerLoop, FakeFetch, Arc) { serve_with_registrar_and_fetch_and_threads(false) } -pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc) { +pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (ServerLoop, FakeFetch, Arc) { let fetch = FakeFetch::default(); let f = fetch.clone(); - let (server, reg) = init_server(None, move |builder| { - builder.fetch(f.clone()) + let (server, reg) = init_server(move |builder| { + builder.allowed_hosts(None).fetch(f.clone()) }, if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() }); (server, fetch, reg) } -pub fn serve_with_fetch(web_token: &'static str) -> (Server, FakeFetch) { +pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) { let fetch = FakeFetch::default(); let f = fetch.clone(); - let (server, _) = init_server(None, move |builder| { + let (server, _) = init_server(move |builder| { builder + .allowed_hosts(None) .fetch(f.clone()) .web_proxy_tokens(Arc::new(move |token| &token == web_token)) }, Remote::new_sync()); @@ -111,11 +143,11 @@ pub fn serve_with_fetch(web_token: &'static str) -> (Server, FakeFetch) { (server, fetch) } -pub fn serve() -> Server { - init_server(None, |builder| builder, Remote::new_sync()).0 +pub fn serve() -> ServerLoop { + init_server(|builder| builder.allowed_hosts(None), Remote::new_sync()).0 } -pub fn request(server: Server, request: &str) -> http_client::Response { +pub fn request(server: ServerLoop, request: &str) -> http_client::Response { http_client::request(server.addr(), request) } diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 1d32361a7..7d4cb0fe8 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -66,6 +66,9 @@ pub trait LightChainClient: Send + Sync { /// Clear the queue. fn clear_queue(&self); + /// Flush the queue. + fn flush_queue(&self); + /// Get queue info. fn queue_info(&self) -> queue::QueueInfo; @@ -130,7 +133,7 @@ impl Client { BlockChainInfo { total_difficulty: best_block.total_difficulty, - pending_total_difficulty: best_block.total_difficulty, + pending_total_difficulty: best_block.total_difficulty + self.queue.total_difficulty(), genesis_hash: genesis_hash, best_block_hash: best_block.hash, best_block_number: best_block.number, @@ -151,6 +154,11 @@ impl Client { self.chain.get_header(id) } + /// Flush the header queue. + pub fn flush_queue(&self) { + self.queue.flush() + } + /// Get the `i`th CHT root. pub fn cht_root(&self, i: usize) -> Option { self.chain.cht_root(i) @@ -211,6 +219,10 @@ impl LightChainClient for Client { self.queue.clear() } + fn flush_queue(&self) { + Client::flush_queue(self); + } + fn queue_info(&self) -> queue::QueueInfo { self.queue.queue_info() } diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index 89ba2e6e8..04cca9969 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -23,7 +23,7 @@ //! This module provides an interface for configuration of buffer //! flow costs and recharge rates. //! -//! Current default costs are picked completely arbitrarily, not based +//! Current default costs are picked completely arbitrarily, not based //! on any empirical timings or mathematical models. use request; @@ -184,6 +184,23 @@ impl FlowParams { } } + /// Create effectively infinite flow params. + pub fn free() -> Self { + let free_cost = Cost(0.into(), 0.into()); + FlowParams { + limit: (!0u64).into(), + recharge: 1.into(), + costs: CostTable { + headers: free_cost.clone(), + bodies: free_cost.clone(), + receipts: free_cost.clone(), + state_proofs: free_cost.clone(), + contract_codes: free_cost.clone(), + header_proofs: free_cost.clone(), + } + } + } + /// Get a reference to the buffer limit. pub fn limit(&self) -> &U256 { &self.limit } @@ -209,7 +226,7 @@ impl FlowParams { cost.0 + (amount * cost.1) } - /// Compute the maximum number of costs of a specific kind which can be made + /// Compute the maximum number of costs of a specific kind which can be made /// with the given buffer. /// Saturates at `usize::max()`. This is not a problem in practice because /// this amount of requests is already prohibitively large. @@ -317,4 +334,4 @@ mod tests { assert_eq!(buffer.estimate, 100.into()); } -} \ No newline at end of file +} diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 863382ecc..c31f49062 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -41,7 +41,6 @@ use self::buffer_flow::{Buffer, FlowParams}; use self::context::{Ctx, TickCtx}; use self::error::Punishment; -mod buffer_flow; mod context; mod error; mod status; @@ -49,6 +48,8 @@ mod status; #[cfg(test)] mod tests; +pub mod buffer_flow; + pub use self::error::Error; pub use self::context::{BasicContext, EventContext, IoContext}; pub use self::status::{Status, Capabilities, Announcement}; @@ -244,7 +245,7 @@ pub struct LightProtocol { pending_requests: RwLock>, capabilities: RwLock, flow_params: FlowParams, // assumed static and same for every peer. - handlers: Vec>, + handlers: Vec>, req_id: AtomicUsize, } @@ -383,11 +384,11 @@ impl LightProtocol { } /// Add an event handler. - /// Ownership will be transferred to the protocol structure, - /// and the handler will be kept alive as long as it is. + /// /// These are intended to be added when the protocol structure - /// is initialized as a means of customizing its behavior. - pub fn add_handler(&mut self, handler: Box) { + /// is initialized as a means of customizing its behavior, + /// and dispatching requests immediately upon events. + pub fn add_handler(&mut self, handler: Arc) { self.handlers.push(handler); } @@ -447,8 +448,10 @@ impl LightProtocol { } } - // handle a packet using the given io context. - fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) { + /// Handle an LES packet using the given io context. + /// Packet data is _untrusted_, which means that invalid data won't lead to + /// issues. + pub fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); trace!(target: "les", "Incoming packet {} from peer {}", packet_id, peer); @@ -488,6 +491,71 @@ impl LightProtocol { } } + /// called when a peer connects. + pub fn on_connect(&self, peer: &PeerId, io: &IoContext) { + let proto_version = match io.protocol_version(*peer).ok_or(Error::WrongNetwork) { + Ok(pv) => pv, + Err(e) => { punish(*peer, io, e); return } + }; + + if PROTOCOL_VERSIONS.iter().find(|x| **x == proto_version).is_none() { + punish(*peer, io, Error::UnsupportedProtocolVersion(proto_version)); + return; + } + + let chain_info = self.provider.chain_info(); + + let status = Status { + head_td: chain_info.total_difficulty, + head_hash: chain_info.best_block_hash, + head_num: chain_info.best_block_number, + genesis_hash: chain_info.genesis_hash, + protocol_version: proto_version as u32, // match peer proto version + network_id: self.network_id, + last_head: None, + }; + + let capabilities = self.capabilities.read().clone(); + let status_packet = status::write_handshake(&status, &capabilities, Some(&self.flow_params)); + + self.pending_peers.write().insert(*peer, PendingPeer { + sent_head: chain_info.best_block_hash, + last_update: SteadyTime::now(), + }); + + io.send(*peer, packet::STATUS, status_packet); + } + + /// called when a peer disconnects. + pub fn on_disconnect(&self, peer: PeerId, io: &IoContext) { + trace!(target: "les", "Peer {} disconnecting", peer); + + + self.pending_peers.write().remove(&peer); + if self.peers.write().remove(&peer).is_some() { + let unfulfilled: Vec<_> = self.pending_requests.read() + .iter() + .filter(|&(_, r)| r.peer_id == peer) + .map(|(&id, _)| ReqId(id)) + .collect(); + + { + let mut pending = self.pending_requests.write(); + for &ReqId(ref inner) in &unfulfilled { + pending.remove(inner); + } + } + + for handler in &self.handlers { + handler.on_disconnect(&Ctx { + peer: peer, + io: io, + proto: self, + }, &unfulfilled) + } + } + } + // check timeouts and punish peers. fn timeout_check(&self, io: &IoContext) { let now = SteadyTime::now(); @@ -536,6 +604,16 @@ impl LightProtocol { } } + /// Execute the given closure with a basic context derived from the I/O context. + pub fn with_context(&self, io: &IoContext, f: F) -> T + where F: FnOnce(&BasicContext) -> T + { + f(&TickCtx { + io: io, + proto: self, + }) + } + fn tick_handlers(&self, io: &IoContext) { for handler in &self.handlers { handler.tick(&TickCtx { @@ -547,71 +625,6 @@ impl LightProtocol { } impl LightProtocol { - // called when a peer connects. - fn on_connect(&self, peer: &PeerId, io: &IoContext) { - let proto_version = match io.protocol_version(*peer).ok_or(Error::WrongNetwork) { - Ok(pv) => pv, - Err(e) => { punish(*peer, io, e); return } - }; - - if PROTOCOL_VERSIONS.iter().find(|x| **x == proto_version).is_none() { - punish(*peer, io, Error::UnsupportedProtocolVersion(proto_version)); - return; - } - - let chain_info = self.provider.chain_info(); - - let status = Status { - head_td: chain_info.total_difficulty, - head_hash: chain_info.best_block_hash, - head_num: chain_info.best_block_number, - genesis_hash: chain_info.genesis_hash, - protocol_version: proto_version as u32, // match peer proto version - network_id: self.network_id, - last_head: None, - }; - - let capabilities = self.capabilities.read().clone(); - let status_packet = status::write_handshake(&status, &capabilities, Some(&self.flow_params)); - - self.pending_peers.write().insert(*peer, PendingPeer { - sent_head: chain_info.best_block_hash, - last_update: SteadyTime::now(), - }); - - io.send(*peer, packet::STATUS, status_packet); - } - - // called when a peer disconnects. - fn on_disconnect(&self, peer: PeerId, io: &IoContext) { - trace!(target: "les", "Peer {} disconnecting", peer); - - - self.pending_peers.write().remove(&peer); - if self.peers.write().remove(&peer).is_some() { - let unfulfilled: Vec<_> = self.pending_requests.read() - .iter() - .filter(|&(_, r)| r.peer_id == peer) - .map(|(&id, _)| ReqId(id)) - .collect(); - - { - let mut pending = self.pending_requests.write(); - for &ReqId(ref inner) in &unfulfilled { - pending.remove(inner); - } - } - - for handler in &self.handlers { - handler.on_disconnect(&Ctx { - peer: peer, - io: io, - proto: self, - }, &unfulfilled) - } - } - } - // Handle status message from peer. fn status(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(peer) { diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 56ff32b54..a0a9feee4 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -18,7 +18,7 @@ //! These don't test of the higher level logic on top of use ethcore::blockchain_info::BlockChainInfo; -use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; +use ethcore::client::{EachBlockWith, TestBlockChainClient}; use ethcore::ids::BlockId; use ethcore::transaction::PendingTransaction; use ethcore::encoded; @@ -88,7 +88,7 @@ impl Provider for TestProvider { } fn reorg_depth(&self, a: &H256, b: &H256) -> Option { - self.0.client.tree_route(a, b).map(|route| route.index as u64) + self.0.client.reorg_depth(a, b) } fn earliest_state(&self) -> Option { @@ -305,7 +305,9 @@ fn get_block_bodies() { } let request = request::Bodies { - block_hashes: (0..10).map(|i| provider.client.block_hash(BlockId::Number(i)).unwrap()).collect(), + block_hashes: (0..10).map(|i| + provider.client.block_header(BlockId::Number(i)).unwrap().hash() + ).collect() }; let req_id = 111; @@ -353,8 +355,9 @@ fn get_block_receipts() { // find the first 10 block hashes starting with `f` because receipts are only provided // by the test client in that case. - let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockId::Number(i)).unwrap()) - .filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); + let block_hashes: Vec<_> = (0..1000).map(|i| + provider.client.block_header(BlockId::Number(i)).unwrap().hash() + ).filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); let request = request::Receipts { block_hashes: block_hashes.clone(), diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 0b94077ab..9a05d3499 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -83,7 +83,7 @@ pub trait Provider: Send + Sync { (0u64..req.max as u64) .map(|x: u64| x.saturating_mul(req.skip + 1)) - .take_while(|x| if req.reverse { x < &start_num } else { best_num - start_num >= *x }) + .take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x }) .map(|x| if req.reverse { start_num - x } else { start_num + x }) .map(|x| self.block_header(BlockId::Number(x))) .take_while(|x| x.is_some()) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 62e9af250..40df0e11a 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -1329,6 +1329,7 @@ mod tests { use transaction::{Transaction, Action}; use log_entry::{LogEntry, LocalizedLogEntry}; use spec::Spec; + use ethkey::Secret; fn new_db(path: &str) -> Arc { Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), path).unwrap()) @@ -1467,6 +1468,10 @@ mod tests { // TODO: insert block that already includes one of them as an uncle to check it's not allowed. } + fn secret() -> Secret { + Secret::from_slice(&"".sha3()).unwrap() + } + #[test] fn test_fork_transaction_addresses() { let mut canon_chain = ChainGenerator::default(); @@ -1482,7 +1487,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let b1a = canon_chain @@ -1546,7 +1551,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let t2 = Transaction { nonce: 1.into(), @@ -1555,7 +1560,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let t3 = Transaction { nonce: 2.into(), @@ -1564,7 +1569,7 @@ mod tests { action: Action::Create, value: 100.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let b1a = canon_chain .with_transaction(t1.clone()) @@ -1870,7 +1875,7 @@ mod tests { action: Action::Create, value: 101.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let t2 = Transaction { nonce: 0.into(), gas_price: 0.into(), @@ -1878,7 +1883,7 @@ mod tests { action: Action::Create, value: 102.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let t3 = Transaction { nonce: 0.into(), gas_price: 0.into(), @@ -1886,7 +1891,7 @@ mod tests { action: Action::Create, value: 103.into(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let tx_hash1 = t1.hash(); let tx_hash2 = t2.hash(); let tx_hash3 = t3.hash(); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index cd074a88c..befe73ca8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -848,7 +848,7 @@ impl BlockChainClient for Client { difficulty: header.difficulty(), last_hashes: last_hashes, gas_used: U256::zero(), - gas_limit: header.gas_limit(), + gas_limit: U256::max_value(), }; // that's just a copy of the state. let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; @@ -874,6 +874,7 @@ impl BlockChainClient for Client { } fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result { + const UPPER_CEILING: u64 = 1_000_000_000_000u64; let header = self.block_header(block).ok_or(CallError::StatePruned)?; let last_hashes = self.build_last_hashes(header.parent_hash()); let env_info = EnvInfo { @@ -883,37 +884,38 @@ impl BlockChainClient for Client { difficulty: header.difficulty(), last_hashes: last_hashes, gas_used: U256::zero(), - gas_limit: header.gas_limit(), + gas_limit: UPPER_CEILING.into(), }; // that's just a copy of the state. - let mut original_state = self.state_at(block).ok_or(CallError::StatePruned)?; + let original_state = self.state_at(block).ok_or(CallError::StatePruned)?; let sender = t.sender().map_err(|e| { let message = format!("Transaction malformed: {:?}", e); ExecutionError::TransactionMalformed(message) })?; let balance = original_state.balance(&sender); - let needed_balance = t.value + t.gas * t.gas_price; - if balance < needed_balance { - // give the sender a sufficient balance - original_state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty); - } let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false }; let mut tx = t.clone(); let mut cond = |gas| { - let mut state = original_state.clone(); tx.gas = gas; + + let mut state = original_state.clone(); + let needed_balance = tx.value + tx.gas * tx.gas_price; + if balance < needed_balance { + // give the sender a sufficient balance + state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty); + } + Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm) .transact(&tx, options.clone()) - .map(|r| r.trace[0].result.succeeded()) + .map(|r| r.exception.is_some()) .unwrap_or(false) }; - let mut upper = env_info.gas_limit; + let mut upper = header.gas_limit(); if !cond(upper) { // impossible at block gas limit - try `UPPER_CEILING` instead. // TODO: consider raising limit by powers of two. - const UPPER_CEILING: u64 = 1_000_000_000_000u64; upper = UPPER_CEILING.into(); if !cond(upper) { trace!(target: "estimate_gas", "estimate_gas failed with {}", upper); @@ -1669,7 +1671,7 @@ mod tests { use util::Hashable; // given - let key = KeyPair::from_secret("test".sha3()).unwrap(); + let key = KeyPair::from_secret_slice(&"test".sha3()).unwrap(); let secret = key.secret(); let block_number = 1; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 22e61ab09..7568f86be 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -26,6 +26,7 @@ use blockchain::TreeRoute; use client::{ BlockChainClient, MiningBlockChainClient, EngineClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, + ProvingBlockChainClient, }; use db::{NUM_COLUMNS, COL_STATE}; use header::{Header as BlockHeader, BlockNumber}; @@ -134,6 +135,9 @@ impl TestBlockChainClient { /// Create test client with custom spec and extra data. pub fn new_with_spec_and_extra(spec: Spec, extra_data: Bytes) -> Self { + let genesis_block = spec.genesis_block(); + let genesis_hash = spec.genesis_header().hash(); + let mut client = TestBlockChainClient { blocks: RwLock::new(HashMap::new()), numbers: RwLock::new(HashMap::new()), @@ -158,8 +162,12 @@ impl TestBlockChainClient { traces: RwLock::new(None), history: RwLock::new(None), }; - client.add_blocks(1, EachBlockWith::Nothing); // add genesis block - client.genesis_hash = client.last_hash.read().clone(); + + // insert genesis hash. + client.blocks.get_mut().insert(genesis_hash, genesis_block); + client.numbers.get_mut().insert(0, genesis_hash); + *client.last_hash.get_mut() = genesis_hash; + client.genesis_hash = genesis_hash; client } @@ -720,6 +728,20 @@ impl BlockChainClient for TestBlockChainClient { fn registry_address(&self, _name: String) -> Option
{ None } } +impl ProvingBlockChainClient for TestBlockChainClient { + fn prove_storage(&self, _: H256, _: H256, _: u32, _: BlockId) -> Vec { + Vec::new() + } + + fn prove_account(&self, _: H256, _: u32, _: BlockId) -> Vec { + Vec::new() + } + + fn code_by_hash(&self, _: H256, _: BlockId) -> Bytes { + Vec::new() + } +} + impl EngineClient for TestBlockChainClient { fn update_sealing(&self) { self.miner.update_sealing(self) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 0986a5aa7..adbc6ba99 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -354,6 +354,7 @@ mod tests { use env_info::EnvInfo; use header::Header; use error::{Error, BlockError}; + use ethkey::Secret; use rlp::encode; use block::*; use tests::helpers::*; @@ -411,8 +412,8 @@ mod tests { #[test] fn generates_seal_and_does_not_double_propose() { let tap = AccountProvider::transient_provider(); - let addr1 = tap.insert_account("1".sha3(), "1").unwrap(); - let addr2 = tap.insert_account("2".sha3(), "2").unwrap(); + let addr1 = tap.insert_account(Secret::from_slice(&"1".sha3()).unwrap(), "1").unwrap(); + let addr2 = tap.insert_account(Secret::from_slice(&"2".sha3()).unwrap(), "2").unwrap(); let spec = Spec::new_test_round(); let engine = &*spec.engine; @@ -445,7 +446,7 @@ mod tests { fn proposer_switching() { let mut header: Header = Header::default(); let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account("0".sha3(), "0").unwrap(); + let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap(); header.set_author(addr); @@ -464,7 +465,7 @@ mod tests { fn rejects_future_block() { let mut header: Header = Header::default(); let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account("0".sha3(), "0").unwrap(); + let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap(); header.set_author(addr); diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 61e25e58f..b3ccfeb43 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -201,6 +201,7 @@ mod tests { use error::{BlockError, Error}; use tests::helpers::*; use account_provider::AccountProvider; + use ethkey::Secret; use header::Header; use spec::Spec; use engines::Seal; @@ -261,7 +262,7 @@ mod tests { #[test] fn can_generate_seal() { let tap = AccountProvider::transient_provider(); - let addr = tap.insert_account("".sha3(), "").unwrap(); + let addr = tap.insert_account(Secret::from_slice(&"".sha3()).unwrap(), "").unwrap(); let spec = new_test_authority(); let engine = &*spec.engine; @@ -281,7 +282,7 @@ mod tests { #[test] fn seals_internally() { let tap = AccountProvider::transient_provider(); - let authority = tap.insert_account("".sha3(), "").unwrap(); + let authority = tap.insert_account(Secret::from_slice(&"".sha3()).unwrap(), "").unwrap(); let engine = new_test_authority().engine; assert!(!engine.is_sealer(&Address::default()).unwrap()); diff --git a/ethcore/src/engines/tendermint/message.rs b/ethcore/src/engines/tendermint/message.rs index 02e1276cf..1f17ed902 100644 --- a/ethcore/src/engines/tendermint/message.rs +++ b/ethcore/src/engines/tendermint/message.rs @@ -23,15 +23,35 @@ use header::Header; use rlp::*; use ethkey::{recover, public_to_address}; -#[derive(Debug, PartialEq, Eq, Clone)] +/// Message transmitted between consensus participants. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct ConsensusMessage { + pub vote_step: VoteStep, + pub block_hash: Option, pub signature: H520, +} + +/// Complete step of the consensus process. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub struct VoteStep { pub height: Height, pub round: Round, pub step: Step, - pub block_hash: Option, } +impl VoteStep { + pub fn new(height: Height, round: Round, step: Step) -> Self { + VoteStep { height: height, round: round, step: step } + } + + pub fn is_height(&self, height: Height) -> bool { + self.height == height + } + + pub fn is_round(&self, height: Height, round: Round) -> bool { + self.height == height && self.round == round + } +} fn consensus_round(header: &Header) -> Result { let round_rlp = header.seal().get(0).expect("seal passed basic verification; seal has 3 fields; qed"); @@ -42,53 +62,29 @@ impl ConsensusMessage { pub fn new(signature: H520, height: Height, round: Round, step: Step, block_hash: Option) -> Self { ConsensusMessage { signature: signature, - height: height, - round: round, - step: step, block_hash: block_hash, + vote_step: VoteStep::new(height, round, step), } } pub fn new_proposal(header: &Header) -> Result { Ok(ConsensusMessage { + vote_step: VoteStep::new(header.number() as Height, consensus_round(header)?, Step::Propose), signature: UntrustedRlp::new(header.seal().get(1).expect("seal passed basic verification; seal has 3 fields; qed").as_slice()).as_val()?, - height: header.number() as Height, - round: consensus_round(header)?, - step: Step::Propose, block_hash: Some(header.bare_hash()), }) } pub fn new_commit(proposal: &ConsensusMessage, signature: H520) -> Self { + let mut vote_step = proposal.vote_step.clone(); + vote_step.step = Step::Precommit; ConsensusMessage { - signature: signature, - height: proposal.height, - round: proposal.round, - step: Step::Precommit, + vote_step: vote_step, block_hash: proposal.block_hash, + signature: signature, } } - pub fn is_height(&self, height: Height) -> bool { - self.height == height - } - - pub fn is_round(&self, height: Height, round: Round) -> bool { - self.height == height && self.round == round - } - - pub fn is_step(&self, height: Height, round: Round, step: Step) -> bool { - self.height == height && self.round == round && self.step == step - } - - pub fn is_block_hash(&self, h: Height, r: Round, s: Step, block_hash: Option) -> bool { - self.height == h && self.round == r && self.step == s && self.block_hash == block_hash - } - - pub fn is_aligned(&self, m: &ConsensusMessage) -> bool { - self.is_block_hash(m.height, m.round, m.step, m.block_hash) - } - pub fn verify(&self) -> Result { let full_rlp = ::rlp::encode(self); let block_info = Rlp::new(&full_rlp).at(1); @@ -97,16 +93,30 @@ impl ConsensusMessage { } pub fn precommit_hash(&self) -> H256 { - message_info_rlp(self.height, self.round, Step::Precommit, self.block_hash).sha3() + let mut vote_step = self.vote_step.clone(); + vote_step.step = Step::Precommit; + message_info_rlp(&vote_step, self.block_hash).sha3() } } -impl PartialOrd for ConsensusMessage { - fn partial_cmp(&self, m: &ConsensusMessage) -> Option { +impl PartialOrd for VoteStep { + fn partial_cmp(&self, m: &VoteStep) -> Option { Some(self.cmp(m)) } } +impl Ord for VoteStep { + fn cmp(&self, m: &VoteStep) -> Ordering { + if self.height != m.height { + self.height.cmp(&m.height) + } else if self.round != m.round { + self.round.cmp(&m.round) + } else { + self.step.number().cmp(&m.step.number()) + } + } +} + impl Step { fn number(&self) -> u8 { match *self { @@ -118,20 +128,6 @@ impl Step { } } -impl Ord for ConsensusMessage { - fn cmp(&self, m: &ConsensusMessage) -> Ordering { - if self.height != m.height { - self.height.cmp(&m.height) - } else if self.round != m.round { - self.round.cmp(&m.round) - } else if self.step != m.step { - self.step.number().cmp(&m.step.number()) - } else { - self.signature.cmp(&m.signature) - } - } -} - impl Decodable for Step { fn decode(decoder: &D) -> Result where D: Decoder { match decoder.as_rlp().as_val()? { @@ -149,42 +145,39 @@ impl Encodable for Step { } } -/// (signature, height, round, step, block_hash) +/// (signature, (height, round, step, block_hash)) impl Decodable for ConsensusMessage { fn decode(decoder: &D) -> Result where D: Decoder { let rlp = decoder.as_rlp(); let m = rlp.at(1)?; let block_message: H256 = m.val_at(3)?; Ok(ConsensusMessage { - signature: rlp.val_at(0)?, - height: m.val_at(0)?, - round: m.val_at(1)?, - step: m.val_at(2)?, + vote_step: VoteStep::new(m.val_at(0)?, m.val_at(1)?, m.val_at(2)?), block_hash: match block_message.is_zero() { true => None, false => Some(block_message), - } + }, + signature: rlp.val_at(0)?, }) } -} +} impl Encodable for ConsensusMessage { fn rlp_append(&self, s: &mut RlpStream) { - let info = message_info_rlp(self.height, self.round, self.step, self.block_hash); + let info = message_info_rlp(&self.vote_step, self.block_hash); s.begin_list(2) .append(&self.signature) .append_raw(&info, 1); } } -pub fn message_info_rlp(height: Height, round: Round, step: Step, block_hash: Option) -> Bytes { +pub fn message_info_rlp(vote_step: &VoteStep, block_hash: Option) -> Bytes { // TODO: figure out whats wrong with nested list encoding let mut s = RlpStream::new_list(5); - s.append(&height).append(&round).append(&step).append(&block_hash.unwrap_or_else(H256::zero)); + s.append(&vote_step.height).append(&vote_step.round).append(&vote_step.step).append(&block_hash.unwrap_or_else(H256::zero)); s.out() } - pub fn message_full_rlp(signature: &H520, vote_info: &Bytes) -> Bytes { let mut s = RlpStream::new_list(2); s.append(signature).append_raw(vote_info, 1); @@ -199,14 +192,17 @@ mod tests { use super::*; use account_provider::AccountProvider; use header::Header; + use ethkey::Secret; #[test] fn encode_decode() { let message = ConsensusMessage { signature: H520::default(), - height: 10, - round: 123, - step: Step::Precommit, + vote_step: VoteStep { + height: 10, + round: 123, + step: Step::Precommit, + }, block_hash: Some("1".sha3()) }; let raw_rlp = ::rlp::encode(&message).to_vec(); @@ -215,9 +211,11 @@ mod tests { let message = ConsensusMessage { signature: H520::default(), - height: 1314, - round: 0, - step: Step::Prevote, + vote_step: VoteStep { + height: 1314, + round: 0, + step: Step::Prevote, + }, block_hash: None }; let raw_rlp = ::rlp::encode(&message); @@ -228,10 +226,10 @@ mod tests { #[test] fn generate_and_verify() { let tap = Arc::new(AccountProvider::transient_provider()); - let addr = tap.insert_account("0".sha3(), "0").unwrap(); + let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap(); tap.unlock_account_permanently(addr, "0".into()).unwrap(); - let mi = message_info_rlp(123, 2, Step::Precommit, Some(H256::default())); + let mi = message_info_rlp(&VoteStep::new(123, 2, Step::Precommit), Some(H256::default())); let raw_rlp = message_full_rlp(&tap.sign(addr, None, mi.sha3()).unwrap().into(), &mi); @@ -254,9 +252,11 @@ mod tests { message, ConsensusMessage { signature: Default::default(), - height: 0, - round: 0, - step: Step::Propose, + vote_step: VoteStep { + height: 0, + round: 0, + step: Step::Propose, + }, block_hash: Some(header.bare_hash()) } ); @@ -267,12 +267,10 @@ mod tests { let header = Header::default(); let pro = ConsensusMessage { signature: Default::default(), - height: 0, - round: 0, - step: Step::Propose, + vote_step: VoteStep::new(0, 0, Step::Propose), block_hash: Some(header.bare_hash()) }; - let pre = message_info_rlp(0, 0, Step::Precommit, Some(header.bare_hash())); + let pre = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash())); assert_eq!(pro.precommit_hash(), pre.sha3()); } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 5fe9a0248..9b7fa8cf6 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -53,7 +53,7 @@ use self::transition::TransitionHandler; use self::params::TendermintParams; use self::vote_collector::VoteCollector; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum Step { Propose, Prevote, @@ -163,13 +163,13 @@ impl Tendermint { let h = self.height.load(AtomicOrdering::SeqCst); let r = self.round.load(AtomicOrdering::SeqCst); let s = self.step.read(); - let vote_info = message_info_rlp(h, r, *s, block_hash); + let vote_info = message_info_rlp(&VoteStep::new(h, r, *s), block_hash); let authority = self.authority.read(); match ap.sign(*authority, self.password.read().clone(), vote_info.sha3()).map(Into::into) { Ok(signature) => { let message_rlp = message_full_rlp(&signature, &vote_info); let message = ConsensusMessage::new(signature, h, r, *s, block_hash); - self.votes.vote(message.clone(), *authority); + self.votes.vote(message.clone(), &*authority); debug!(target: "poa", "Generated {:?} as {}.", message, *authority); self.handle_valid_message(&message); @@ -221,7 +221,7 @@ impl Tendermint { }, Step::Prevote => { let block_hash = match *self.lock_change.read() { - Some(ref m) if !self.should_unlock(m.round) => m.block_hash, + Some(ref m) if !self.should_unlock(m.vote_step.round) => m.block_hash, _ => self.proposal.read().clone(), }; self.generate_and_broadcast_message(block_hash); @@ -230,8 +230,8 @@ impl Tendermint { trace!(target: "poa", "to_step: Precommit."); let block_hash = match *self.lock_change.read() { Some(ref m) if self.is_round(m) && m.block_hash.is_some() => { - trace!(target: "poa", "Setting last lock: {}", m.round); - self.last_lock.store(m.round, AtomicOrdering::SeqCst); + trace!(target: "poa", "Setting last lock: {}", m.vote_step.round); + self.last_lock.store(m.vote_step.round, AtomicOrdering::SeqCst); m.block_hash }, _ => None, @@ -246,7 +246,7 @@ impl Tendermint { if let Some(block_hash) = *self.proposal.read() { // Generate seal and remove old votes. if self.is_proposer(&*self.authority.read()).is_ok() { - if let Some(seal) = self.votes.seal_signatures(height, round, block_hash) { + if let Some(seal) = self.votes.seal_signatures(height, round, &block_hash) { trace!(target: "poa", "Collected seal: {:?}", seal); let seal = vec![ ::rlp::encode(&round).to_vec(), @@ -290,11 +290,11 @@ impl Tendermint { } fn is_height(&self, message: &ConsensusMessage) -> bool { - message.is_height(self.height.load(AtomicOrdering::SeqCst)) + message.vote_step.is_height(self.height.load(AtomicOrdering::SeqCst)) } fn is_round(&self, message: &ConsensusMessage) -> bool { - message.is_round(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst)) + message.vote_step.is_round(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst)) } fn increment_round(&self, n: Round) { @@ -302,20 +302,20 @@ impl Tendermint { self.round.fetch_add(n, AtomicOrdering::SeqCst); } - fn should_unlock(&self, lock_change_round: Round) -> bool { + fn should_unlock(&self, lock_change_round: Round) -> bool { self.last_lock.load(AtomicOrdering::SeqCst) < lock_change_round && lock_change_round < self.round.load(AtomicOrdering::SeqCst) } fn has_enough_any_votes(&self) -> bool { - let step_votes = self.votes.count_step_votes(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), *self.step.read()); + let step_votes = self.votes.count_step_votes(&VoteStep::new(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), *self.step.read())); self.is_above_threshold(step_votes) } - fn has_enough_future_step_votes(&self, message: &ConsensusMessage) -> bool { - if message.round > self.round.load(AtomicOrdering::SeqCst) { - let step_votes = self.votes.count_step_votes(message.height, message.round, message.step); + fn has_enough_future_step_votes(&self, vote_step: &VoteStep) -> bool { + if vote_step.round > self.round.load(AtomicOrdering::SeqCst) { + let step_votes = self.votes.count_step_votes(vote_step); self.is_above_threshold(step_votes) } else { false @@ -328,12 +328,13 @@ impl Tendermint { } fn handle_valid_message(&self, message: &ConsensusMessage) { + let ref vote_step = message.vote_step; let is_newer_than_lock = match *self.lock_change.read() { - Some(ref lock) => message > lock, + Some(ref lock) => vote_step > &lock.vote_step, None => true, }; let lock_change = is_newer_than_lock - && message.step == Step::Prevote + && vote_step.step == Step::Prevote && message.block_hash.is_some() && self.has_enough_aligned_votes(message); if lock_change { @@ -351,15 +352,15 @@ impl Tendermint { Some(Step::Commit) } }, - Step::Precommit if self.has_enough_future_step_votes(message) => { - self.increment_round(message.round - self.round.load(AtomicOrdering::SeqCst)); + Step::Precommit if self.has_enough_future_step_votes(&vote_step) => { + self.increment_round(vote_step.round - self.round.load(AtomicOrdering::SeqCst)); Some(Step::Precommit) }, // Avoid counting twice. Step::Prevote if lock_change => Some(Step::Precommit), Step::Prevote if self.has_enough_aligned_votes(message) => Some(Step::Precommit), - Step::Prevote if self.has_enough_future_step_votes(message) => { - self.increment_round(message.round - self.round.load(AtomicOrdering::SeqCst)); + Step::Prevote if self.has_enough_future_step_votes(&vote_step) => { + self.increment_round(vote_step.round - self.round.load(AtomicOrdering::SeqCst)); Some(Step::Prevote) }, _ => None, @@ -390,8 +391,8 @@ impl Engine for Tendermint { let message = ConsensusMessage::new_proposal(header).expect("Invalid header."); map![ "signature".into() => message.signature.to_string(), - "height".into() => message.height.to_string(), - "round".into() => message.round.to_string(), + "height".into() => message.vote_step.height.to_string(), + "round".into() => message.vote_step.round.to_string(), "block_hash".into() => message.block_hash.as_ref().map(ToString::to_string).unwrap_or("".into()) ] } @@ -431,11 +432,11 @@ impl Engine for Tendermint { let height = header.number() as Height; let round = self.round.load(AtomicOrdering::SeqCst); let bh = Some(header.bare_hash()); - let vote_info = message_info_rlp(height, round, Step::Propose, bh.clone()); + let vote_info = message_info_rlp(&VoteStep::new(height, round, Step::Propose), bh.clone()); if let Ok(signature) = ap.sign(*author, self.password.read().clone(), vote_info.sha3()).map(H520::from) { // Insert Propose vote. debug!(target: "poa", "Submitting proposal {} at height {} round {}.", header.bare_hash(), height, round); - self.votes.vote(ConsensusMessage::new(signature, height, round, Step::Propose, bh), *author); + self.votes.vote(ConsensusMessage::new(signature, height, round, Step::Propose, bh), author); // Remember proposal for later seal submission. *self.proposal.write() = bh; Seal::Proposal(vec![ @@ -461,9 +462,11 @@ impl Engine for Tendermint { if !self.is_authority(&sender) { Err(EngineError::NotAuthorized(sender))?; } - self.broadcast_message(rlp.as_raw().to_vec()); + if self.votes.vote(message.clone(), &sender).is_some() { + Err(EngineError::DoubleVote(sender))? + } trace!(target: "poa", "Handling a valid {:?} from {}.", message, sender); - self.votes.vote(message.clone(), sender); + self.broadcast_message(rlp.as_raw().to_vec()); self.handle_valid_message(&message); } Ok(()) @@ -502,7 +505,7 @@ impl Engine for Tendermint { } fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - let proposal = ConsensusMessage::new_proposal(header)?; + let proposal = ConsensusMessage::new_proposal(header)?; let proposer = proposal.verify()?; if !self.is_authority(&proposer) { Err(EngineError::NotAuthorized(proposer))? @@ -541,7 +544,7 @@ impl Engine for Tendermint { found: signatures_len }))?; } - self.is_round_proposer(proposal.height, proposal.round, &proposer)?; + self.is_round_proposer(proposal.vote_step.height, proposal.vote_step.round, &proposer)?; } Ok(()) } @@ -607,16 +610,16 @@ impl Engine for Tendermint { let proposal = ConsensusMessage::new_proposal(header).expect("block went through full verification; this Engine verifies new_proposal creation; qed"); if signatures_len != 1 { // New Commit received, skip to next height. - trace!(target: "poa", "Received a commit for height {}, round {}.", proposal.height, proposal.round); - self.to_next_height(proposal.height); + trace!(target: "poa", "Received a commit: {:?}.", proposal.vote_step); + self.to_next_height(proposal.vote_step.height); return false; } let proposer = proposal.verify().expect("block went through full verification; this Engine tries verify; qed"); - debug!(target: "poa", "Received a new proposal for height {}, round {} from {}.", proposal.height, proposal.round, proposer); + debug!(target: "poa", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer); if self.is_round(&proposal) { *self.proposal.write() = proposal.block_hash.clone(); } - self.votes.vote(proposal, proposer); + self.votes.vote(proposal, &proposer); true } @@ -671,6 +674,7 @@ mod tests { use error::{Error, BlockError}; use header::Header; use env_info::EnvInfo; + use ethkey::Secret; use client::chain_notify::ChainNotify; use miner::MinerService; use tests::helpers::*; @@ -703,7 +707,7 @@ mod tests { } fn vote(engine: &Engine, signer: F, height: usize, round: usize, step: Step, block_hash: Option) -> Bytes where F: FnOnce(H256) -> Result { - let mi = message_info_rlp(height, round, step, block_hash); + let mi = message_info_rlp(&VoteStep::new(height, round, step), block_hash); let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi); engine.handle_message(&m).unwrap(); m @@ -711,7 +715,7 @@ mod tests { fn proposal_seal(tap: &Arc, header: &Header, round: Round) -> Vec { let author = header.author(); - let vote_info = message_info_rlp(header.number() as Height, round, Step::Propose, Some(header.bare_hash())); + let vote_info = message_info_rlp(&VoteStep::new(header.number() as Height, round, Step::Propose), Some(header.bare_hash())); let signature = tap.sign(*author, None, vote_info.sha3()).unwrap(); vec![ ::rlp::encode(&round).to_vec(), @@ -721,7 +725,7 @@ mod tests { } fn insert_and_unlock(tap: &Arc, acc: &str) -> Address { - let addr = tap.insert_account(acc.sha3(), acc).unwrap(); + let addr = tap.insert_account(Secret::from_slice(&acc.sha3()).unwrap(), acc).unwrap(); tap.unlock_account_permanently(addr, acc.into()).unwrap(); addr } @@ -825,7 +829,7 @@ mod tests { header.set_author(proposer); let mut seal = proposal_seal(&tap, &header, 0); - let vote_info = message_info_rlp(0, 0, Step::Precommit, Some(header.bare_hash())); + let vote_info = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash())); let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap(); seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone())]).to_vec(); @@ -886,7 +890,7 @@ mod tests { fn relays_messages() { let (spec, tap) = setup(); let engine = spec.engine.clone(); - + let v0 = insert_and_register(&tap, engine.as_ref(), "0"); let v1 = insert_and_register(&tap, engine.as_ref(), "1"); diff --git a/ethcore/src/engines/tendermint/vote_collector.rs b/ethcore/src/engines/tendermint/vote_collector.rs index be592bc8f..758dfb5a3 100644 --- a/ethcore/src/engines/tendermint/vote_collector.rs +++ b/ethcore/src/engines/tendermint/vote_collector.rs @@ -17,13 +17,50 @@ //! Collects votes on hashes at each height and round. use util::*; -use super::message::ConsensusMessage; -use super::{Height, Round, Step}; +use super::message::*; +use super::{Height, Round, Step, BlockHash}; #[derive(Debug)] pub struct VoteCollector { /// Storing all Proposals, Prevotes and Precommits. - votes: RwLock>, + votes: RwLock>, +} + +#[derive(Debug, Default)] +struct StepCollector { + voted: HashSet
, + pub block_votes: HashMap, HashMap>, + messages: HashSet, +} + +impl StepCollector { + /// Returns Some(&Address) when validator is double voting. + fn insert<'a>(&mut self, message: ConsensusMessage, address: &'a Address) -> Option<&'a Address> { + // Do nothing when message was seen. + if self.messages.insert(message.clone()) { + if self.voted.insert(address.clone()) { + self + .block_votes + .entry(message.block_hash) + .or_insert_with(HashMap::new) + .insert(message.signature, address.clone()); + } else { + // Bad validator sent a different message. + return Some(address); + } + } + None + } + + /// Count all votes for the given block hash at this step. + fn count_block(&self, block_hash: &Option) -> usize { + self.block_votes.get(block_hash).map_or(0, HashMap::len) + } + + /// Count all votes collected for the given step. + fn count(&self) -> usize { + self.block_votes.values().map(HashMap::len).sum() + } } #[derive(Debug)] @@ -42,109 +79,105 @@ impl PartialEq for SealSignatures { impl Eq for SealSignatures {} impl VoteCollector { - pub fn new() -> VoteCollector { + pub fn new() -> Self { let mut collector = BTreeMap::new(); - // Insert dummy message to fulfill invariant: "only messages newer than the oldest are inserted". - collector.insert(ConsensusMessage { - signature: H520::default(), - height: 0, - round: 0, - step: Step::Propose, - block_hash: None - }, - Address::default()); + // Insert dummy entry to fulfill invariant: "only messages newer than the oldest are inserted". + collector.insert(VoteStep::new(0, 0, Step::Propose), Default::default()); VoteCollector { votes: RwLock::new(collector) } } /// Insert vote if it is newer than the oldest one. - pub fn vote(&self, message: ConsensusMessage, voter: Address) -> Option
{ - self.votes.write().insert(message, voter) + pub fn vote<'a>(&self, message: ConsensusMessage, voter: &'a Address) -> Option<&'a Address> { + self + .votes + .write() + .entry(message.vote_step.clone()) + .or_insert_with(Default::default) + .insert(message, voter) } + /// Checks if the message should be ignored. pub fn is_old_or_known(&self, message: &ConsensusMessage) -> bool { - self.votes.read().get(message).map_or(false, |a| { - trace!(target: "poa", "Known message from {}: {:?}.", a, message); - true - }) || { + self + .votes + .read() + .get(&message.vote_step) + .map_or(false, |c| { + let is_known = c.messages.contains(message); + if is_known { trace!(target: "poa", "Known message: {:?}.", message); } + is_known + }) + || { let guard = self.votes.read(); - let is_old = guard.keys().next().map_or(true, |oldest| message <= oldest); + let is_old = guard.keys().next().map_or(true, |oldest| message.vote_step <= *oldest); if is_old { trace!(target: "poa", "Old message {:?}.", message); } is_old } } /// Throws out messages older than message, leaves message as marker for the oldest. - pub fn throw_out_old(&self, message: &ConsensusMessage) { + pub fn throw_out_old(&self, vote_step: &VoteStep) { let mut guard = self.votes.write(); - let new_collector = guard.split_off(message); + let new_collector = guard.split_off(vote_step); *guard = new_collector; } - pub fn seal_signatures(&self, height: Height, round: Round, block_hash: H256) -> Option { - let bh = Some(block_hash); - let (proposal, votes) = { + /// Collects the signatures used to seal a block. + pub fn seal_signatures(&self, height: Height, round: Round, block_hash: &H256) -> Option { + let ref bh = Some(*block_hash); + let precommit_step = VoteStep::new(height, round, Step::Precommit); + let maybe_seal = { let guard = self.votes.read(); - let mut current_signatures = guard.keys().skip_while(|m| !m.is_block_hash(height, round, Step::Propose, bh)); - let proposal = current_signatures.next().cloned(); - let votes = current_signatures - .skip_while(|m| !m.is_block_hash(height, round, Step::Precommit, bh)) - .filter(|m| m.is_block_hash(height, round, Step::Precommit, bh)) - .cloned() - .collect::>(); - (proposal, votes) + guard + .get(&VoteStep::new(height, round, Step::Propose)) + .and_then(|c| c.block_votes.get(bh)) + .and_then(|proposals| proposals.keys().next()) + .map(|proposal| SealSignatures { + proposal: proposal.clone(), + votes: guard + .get(&precommit_step) + .and_then(|c| c.block_votes.get(bh)) + .map(|precommits| precommits.keys().cloned().collect()) + .unwrap_or_else(Vec::new), + }) + .and_then(|seal| if seal.votes.is_empty() { None } else { Some(seal) }) }; - if votes.is_empty() { - return None; + if maybe_seal.is_some() { + // Remove messages that are no longer relevant. + self.throw_out_old(&precommit_step); } - // Remove messages that are no longer relevant. - votes.last().map(|m| self.throw_out_old(m)); - let mut votes_vec: Vec<_> = votes.into_iter().map(|m| m.signature).collect(); - votes_vec.sort(); - proposal.map(|p| SealSignatures { - proposal: p.signature, - votes: votes_vec, - }) + maybe_seal } + /// Count votes which agree with the given message. pub fn count_aligned_votes(&self, message: &ConsensusMessage) -> usize { - let guard = self.votes.read(); - guard.keys() - .skip_while(|m| !m.is_aligned(message)) - // sorted by signature so might not be continuous - .filter(|m| m.is_aligned(message)) - .count() + self + .votes + .read() + .get(&message.vote_step) + .map_or(0, |m| m.count_block(&message.block_hash)) } - pub fn count_step_votes(&self, height: Height, round: Round, step: Step) -> usize { - let guard = self.votes.read(); - let current = guard.iter().skip_while(|&(m, _)| !m.is_step(height, round, step)); - let mut origins = HashSet::new(); - let mut n = 0; - for (message, origin) in current { - if message.is_step(height, round, step) { - if origins.insert(origin) { - n += 1; - } else { - warn!("count_step_votes: Authority {} has cast multiple step votes, this indicates malicious behaviour.", origin) - } - } - } - n + /// Count all votes collected for a given step. + pub fn count_step_votes(&self, vote_step: &VoteStep) -> usize { + self.votes.read().get(vote_step).map_or(0, StepCollector::count) } + /// Get all messages older than the height. pub fn get_up_to(&self, height: Height) -> Vec { let guard = self.votes.read(); guard - .keys() - .filter(|m| m.step.is_pre()) - .take_while(|m| m.height <= height) - .map(|m| ::rlp::encode(m).to_vec()) - .collect() + .iter() + .filter(|&(s, _)| s.step.is_pre()) + .take_while(|&(s, _)| s.height <= height) + .map(|(_, c)| c.messages.iter().map(|m| ::rlp::encode(m).to_vec()).collect::>()) + .fold(Vec::new(), |mut acc, mut messages| { acc.append(&mut messages); acc }) } + /// Retrieve address from which the message was sent from cache. pub fn get(&self, message: &ConsensusMessage) -> Option
{ let guard = self.votes.read(); - guard.get(message).cloned() + guard.get(&message.vote_step).and_then(|c| c.block_votes.get(&message.block_hash)).and_then(|origins| origins.get(&message.signature).cloned()) } } @@ -152,15 +185,15 @@ impl VoteCollector { mod tests { use util::*; use super::*; - use super::super::{Height, Round, BlockHash, Step}; - use super::super::message::ConsensusMessage; + use super::super::{BlockHash, Step}; + use super::super::message::*; - fn random_vote(collector: &VoteCollector, signature: H520, h: Height, r: Round, step: Step, block_hash: Option) -> Option { - full_vote(collector, signature, h, r, step, block_hash, H160::random()) + fn random_vote(collector: &VoteCollector, signature: H520, vote_step: VoteStep, block_hash: Option) -> bool { + full_vote(collector, signature, vote_step, block_hash, &H160::random()).is_none() } - fn full_vote(collector: &VoteCollector, signature: H520, h: Height, r: Round, step: Step, block_hash: Option, address: Address) -> Option { - collector.vote(ConsensusMessage { signature: signature, height: h, round: r, step: step, block_hash: block_hash }, address) + fn full_vote<'a>(collector: &VoteCollector, signature: H520, vote_step: VoteStep, block_hash: Option, address: &'a Address) -> Option<&'a Address> { + collector.vote(ConsensusMessage { signature: signature, vote_step: vote_step, block_hash: block_hash }, address) } #[test] @@ -173,68 +206,71 @@ mod tests { for _ in 0..5 { signatures.push(H520::random()); } + let propose_step = VoteStep::new(h, r, Step::Propose); + let prevote_step = VoteStep::new(h, r, Step::Prevote); + let precommit_step = VoteStep::new(h, r, Step::Precommit); // Wrong height proposal. - random_vote(&collector, signatures[4].clone(), h - 1, r, Step::Propose, bh.clone()); + random_vote(&collector, signatures[4].clone(), VoteStep::new(h - 1, r, Step::Propose), bh.clone()); // Good proposal - random_vote(&collector, signatures[0].clone(), h, r, Step::Propose, bh.clone()); + random_vote(&collector, signatures[0].clone(), propose_step.clone(), bh.clone()); // Wrong block proposal. - random_vote(&collector, signatures[0].clone(), h, r, Step::Propose, Some("0".sha3())); + random_vote(&collector, signatures[0].clone(), propose_step.clone(), Some("0".sha3())); // Wrong block precommit. - random_vote(&collector, signatures[3].clone(), h, r, Step::Precommit, Some("0".sha3())); + random_vote(&collector, signatures[3].clone(), precommit_step.clone(), Some("0".sha3())); // Wrong round proposal. - random_vote(&collector, signatures[0].clone(), h, r - 1, Step::Propose, bh.clone()); + random_vote(&collector, signatures[0].clone(), VoteStep::new(h, r - 1, Step::Propose), bh.clone()); // Prevote. - random_vote(&collector, signatures[0].clone(), h, r, Step::Prevote, bh.clone()); + random_vote(&collector, signatures[0].clone(), prevote_step.clone(), bh.clone()); // Relevant precommit. - random_vote(&collector, signatures[2].clone(), h, r, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[2].clone(), precommit_step.clone(), bh.clone()); // Replcated vote. - random_vote(&collector, signatures[2].clone(), h, r, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[2].clone(), precommit_step.clone(), bh.clone()); // Wrong round precommit. - random_vote(&collector, signatures[4].clone(), h, r + 1, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[4].clone(), VoteStep::new(h, r + 1, Step::Precommit), bh.clone()); // Wrong height precommit. - random_vote(&collector, signatures[3].clone(), h + 1, r, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[3].clone(), VoteStep::new(h + 1, r, Step::Precommit), bh.clone()); // Relevant precommit. - random_vote(&collector, signatures[1].clone(), h, r, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[1].clone(), precommit_step.clone(), bh.clone()); // Wrong round precommit, same signature. - random_vote(&collector, signatures[1].clone(), h, r + 1, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[1].clone(), VoteStep::new(h, r + 1, Step::Precommit), bh.clone()); // Wrong round precommit. - random_vote(&collector, signatures[4].clone(), h, r - 1, Step::Precommit, bh.clone()); + random_vote(&collector, signatures[4].clone(), VoteStep::new(h, r - 1, Step::Precommit), bh.clone()); let seal = SealSignatures { proposal: signatures[0], votes: signatures[1..3].to_vec() }; - assert_eq!(seal, collector.seal_signatures(h, r, bh.unwrap()).unwrap()); + assert_eq!(seal, collector.seal_signatures(h, r, &bh.unwrap()).unwrap()); } #[test] fn count_votes() { let collector = VoteCollector::new(); + let prevote_step = VoteStep::new(3, 2, Step::Prevote); + let precommit_step = VoteStep::new(3, 2, Step::Precommit); // good prevote - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3())); - random_vote(&collector, H520::random(), 3, 1, Step::Prevote, Some("0".sha3())); + random_vote(&collector, H520::random(), prevote_step.clone(), Some("0".sha3())); + random_vote(&collector, H520::random(), VoteStep::new(3, 1, Step::Prevote), Some("0".sha3())); // good precommit - random_vote(&collector, H520::random(), 3, 2, Step::Precommit, Some("0".sha3())); - random_vote(&collector, H520::random(), 3, 3, Step::Precommit, Some("0".sha3())); + random_vote(&collector, H520::random(), precommit_step.clone(), Some("0".sha3())); + random_vote(&collector, H520::random(), VoteStep::new(3, 3, Step::Precommit), Some("0".sha3())); // good prevote - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3())); + random_vote(&collector, H520::random(), prevote_step.clone(), Some("1".sha3())); // good prevote let same_sig = H520::random(); - random_vote(&collector, same_sig.clone(), 3, 2, Step::Prevote, Some("1".sha3())); - random_vote(&collector, same_sig, 3, 2, Step::Prevote, Some("1".sha3())); + random_vote(&collector, same_sig.clone(), prevote_step.clone(), Some("1".sha3())); + random_vote(&collector, same_sig, prevote_step.clone(), Some("1".sha3())); // good precommit - random_vote(&collector, H520::random(), 3, 2, Step::Precommit, Some("1".sha3())); + random_vote(&collector, H520::random(), precommit_step.clone(), Some("1".sha3())); // good prevote - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3())); - random_vote(&collector, H520::random(), 2, 2, Step::Precommit, Some("2".sha3())); + random_vote(&collector, H520::random(), prevote_step.clone(), Some("0".sha3())); + random_vote(&collector, H520::random(), VoteStep::new(2, 2, Step::Precommit), Some("2".sha3())); - assert_eq!(collector.count_step_votes(3, 2, Step::Prevote), 4); - assert_eq!(collector.count_step_votes(3, 2, Step::Precommit), 2); + assert_eq!(collector.count_step_votes(&prevote_step), 4); + assert_eq!(collector.count_step_votes(&precommit_step), 2); let message = ConsensusMessage { signature: H520::default(), - height: 3, - round: 2, - step: Step::Prevote, + vote_step: prevote_step, block_hash: Some("1".sha3()) }; assert_eq!(collector.count_aligned_votes(&message), 2); @@ -243,30 +279,29 @@ mod tests { #[test] fn remove_old() { let collector = VoteCollector::new(); - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3())); - random_vote(&collector, H520::random(), 3, 1, Step::Prevote, Some("0".sha3())); - random_vote(&collector, H520::random(), 3, 3, Step::Precommit, Some("0".sha3())); - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3())); - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3())); - random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3())); - random_vote(&collector, H520::random(), 2, 2, Step::Precommit, Some("2".sha3())); - - let message = ConsensusMessage { - signature: H520::default(), - height: 3, - round: 2, - step: Step::Precommit, - block_hash: Some("1".sha3()) + let vote = |height, round, step, hash| { + random_vote(&collector, H520::random(), VoteStep::new(height, round, step), hash); }; - collector.throw_out_old(&message); + vote(3, 2, Step::Prevote, Some("0".sha3())); + vote(3, 1, Step::Prevote, Some("0".sha3())); + vote(3, 3, Step::Precommit, Some("0".sha3())); + vote(3, 2, Step::Prevote, Some("1".sha3())); + vote(3, 2, Step::Prevote, Some("1".sha3())); + vote(3, 2, Step::Prevote, Some("0".sha3())); + vote(2, 2, Step::Precommit, Some("2".sha3())); + + collector.throw_out_old(&VoteStep::new(3, 2, Step::Precommit)); assert_eq!(collector.votes.read().len(), 1); } #[test] fn malicious_authority() { let collector = VoteCollector::new(); - full_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3()), Address::default()); - full_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3()), Address::default()); - assert_eq!(collector.count_step_votes(3, 2, Step::Prevote), 1); + let vote_step = VoteStep::new(3, 2, Step::Prevote); + // Vote is inserted fine. + assert!(full_vote(&collector, H520::random(), vote_step.clone(), Some("0".sha3()), &Address::default()).is_none()); + // Returns the double voting address. + full_vote(&collector, H520::random(), vote_step.clone(), Some("1".sha3()), &Address::default()).unwrap(); + assert_eq!(collector.count_step_votes(&vote_step), 1); } } diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index b8b63112c..7efe668e6 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -118,17 +118,17 @@ mod provider { } } fn as_string(e: T) -> String { format!("{:?}", e) } - + /// Auto-generated from: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}` #[allow(dead_code)] - pub fn get_validators(&self) -> Result, String> { + pub fn get_validators(&self) -> Result, String> { let call = self.contract.function("getValidators".into()).map_err(Self::as_string)?; let data = call.encode_call( vec![] ).map_err(Self::as_string)?; let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; let mut result = output.into_iter().rev().collect::>(); - Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::>() })) + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::>() })) } } } @@ -140,6 +140,7 @@ mod tests { use account_provider::AccountProvider; use transaction::{Transaction, Action}; use client::{BlockChainClient, EngineClient}; + use ethkey::Secret; use miner::MinerService; use tests::helpers::generate_dummy_client_with_spec_and_data; use super::super::ValidatorSet; @@ -158,8 +159,9 @@ mod tests { #[test] fn changes_validators() { let tap = Arc::new(AccountProvider::transient_provider()); - let v0 = tap.insert_account("1".sha3(), "").unwrap(); - let v1 = tap.insert_account("0".sha3(), "").unwrap(); + let s0 = Secret::from_slice(&"1".sha3()).unwrap(); + let v0 = tap.insert_account(s0.clone(), "").unwrap(); + let v1 = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "").unwrap(); let spec_factory = || { let spec = Spec::new_validator_contract(); spec.engine.register_account_provider(tap.clone()); @@ -178,7 +180,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "f94e18670000000000000000000000000000000000000000000000000000000000000001".from_hex().unwrap(), - }.sign(&"1".sha3(), None); + }.sign(&s0, None); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 1); @@ -190,7 +192,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&"1".sha3(), None); + }.sign(&s0, None); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // The transaction is not yet included so still unable to seal. @@ -209,7 +211,7 @@ mod tests { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(&"1".sha3(), None); + }.sign(&s0, None); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // Able to seal again. diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 3270b06e4..78dc8b73a 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -22,7 +22,7 @@ use action_params::ActionParams; use evm::Ext; /// Evm errors. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Error { /// `OutOfGas` is returned when transaction execution runs out of gas. /// The state should be reverted to the state from before the diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 72427c668..a8c9596ab 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -463,8 +463,9 @@ impl<'a> Executive<'a> { match result { Err(evm::Error::Internal) => Err(ExecutionError::Internal), - Err(_) => { + Err(exception) => { Ok(Executed { + exception: Some(exception), gas: t.gas, gas_used: t.gas, refunded: U256::zero(), @@ -479,6 +480,7 @@ impl<'a> Executive<'a> { }, _ => { Ok(Executed { + exception: None, gas: t.gas, gas_used: gas_used, refunded: refunded, diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index cfd53053e..ae52ee3b1 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -844,6 +844,7 @@ mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use super::*; + use ethkey::Secret; use util::{U256, H256, FixedHash, Address, Hashable}; use tests::helpers::*; use devtools::*; @@ -854,6 +855,10 @@ mod tests { use trace::{FlatTrace, TraceError, trace}; use types::executed::CallType; + fn secret() -> Secret { + Secret::from_slice(&"".sha3()).unwrap() + } + #[test] fn should_apply_create_transaction() { init_log(); @@ -872,7 +877,7 @@ mod tests { action: Action::Create, value: 100.into(), data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); @@ -932,7 +937,7 @@ mod tests { action: Action::Create, value: 100.into(), data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); @@ -969,7 +974,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); @@ -1012,7 +1017,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); let result = state.apply(&info, &engine, &t, true).unwrap(); @@ -1054,7 +1059,7 @@ mod tests { action: Action::Call(0x1.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); let result = state.apply(&info, engine, &t, true).unwrap(); @@ -1096,7 +1101,7 @@ mod tests { action: Action::Call(0xa.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); let result = state.apply(&info, engine, &t, true).unwrap(); @@ -1139,7 +1144,7 @@ mod tests { action: Action::Call(0xa.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); @@ -1201,7 +1206,7 @@ mod tests { action: Action::Call(0xa.into()), value: 0.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); @@ -1260,7 +1265,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); @@ -1300,7 +1305,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); @@ -1360,7 +1365,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); @@ -1415,7 +1420,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); @@ -1458,7 +1463,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); @@ -1514,7 +1519,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); @@ -1589,7 +1594,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); @@ -1662,7 +1667,7 @@ mod tests { action: Action::Call(0xa.into()), value: 100.into(), data: vec![], - }.sign(&"".sha3(), None); + }.sign(&secret(), None); state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty); diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 0dfd8434d..347d1adc3 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -28,7 +28,7 @@ use rlp::View; use spec::Spec; use views::BlockView; use util::stats::Histogram; -use ethkey::KeyPair; +use ethkey::{KeyPair, Secret}; use transaction::{PendingTransaction, Transaction, Action}; use miner::MinerService; @@ -290,7 +290,7 @@ fn change_history_size() { #[test] fn does_not_propagate_delayed_transactions() { - let key = KeyPair::from_secret("test".sha3()).unwrap(); + let key = KeyPair::from_secret(Secret::from_slice(&"test".sha3()).unwrap()).unwrap(); let secret = key.secret(); let tx0 = PendingTransaction::new(Transaction { nonce: 0.into(), diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index d08261306..55306dc51 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -163,7 +163,7 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe let mut last_hashes = vec![]; let mut last_header = genesis_header.clone(); - let kp = KeyPair::from_secret("".sha3()).unwrap(); + let kp = KeyPair::from_secret_slice(&"".sha3()).unwrap(); let author = kp.address(); let mut n = 0; diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 1f0ef33c7..ab7be891e 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -18,6 +18,7 @@ use util::{Bytes, U256, Address, U512}; use rlp::*; +use evm; use trace::{VMTrace, FlatTrace}; use types::log_entry::LogEntry; use types::state_diff::StateDiff; @@ -65,6 +66,9 @@ impl Decodable for CallType { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "ipc", binary)] pub struct Executed { + /// True if the outer call/create resulted in an exceptional exit. + pub exception: Option, + /// Gas paid up front for execution of transaction. pub gas: U256, @@ -178,6 +182,8 @@ pub enum CallError { TransactionNotFound, /// Couldn't find requested block's state in the chain. StatePruned, + /// Couldn't find an amount of gas that didn't result in an exception. + Exceptional, /// Error executing. Execution(ExecutionError), } @@ -195,6 +201,7 @@ impl fmt::Display for CallError { let msg = match *self { TransactionNotFound => "Transaction couldn't be found in the chain".into(), StatePruned => "Couldn't find the transaction block's state in the chain".into(), + Exceptional => "An exception happened in the execution".into(), Execution(ref e) => format!("{}", e), }; diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 8dd7391bb..1f26b156d 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -102,6 +102,7 @@ impl HeapSizeOf for Transaction { impl From for SignedTransaction { fn from(t: ethjson::state::Transaction) -> Self { let to: Option = t.to.into(); + let secret = Secret::from_slice(&t.secret.0).expect("Valid secret expected."); Transaction { nonce: t.nonce.into(), gas_price: t.gas_price.into(), @@ -112,7 +113,7 @@ impl From for SignedTransaction { }, value: t.value.into(), data: t.data.into(), - }.sign(&t.secret.into(), None) + }.sign(&secret, None) } } diff --git a/ethcrypto/src/lib.rs b/ethcrypto/src/lib.rs index 4053baa9f..ea933ea60 100644 --- a/ethcrypto/src/lib.rs +++ b/ethcrypto/src/lib.rs @@ -166,7 +166,7 @@ pub mod aes { /// ECDH functions #[cfg_attr(feature="dev", allow(similar_names))] pub mod ecdh { - use secp256k1::{ecdh, key}; + use secp256k1::{ecdh, key, Error as SecpError}; use ethkey::{Secret, Public, SECP256K1}; use Error; @@ -180,13 +180,11 @@ pub mod ecdh { }; let publ = key::PublicKey::from_slice(context, &pdata)?; - // no way to create SecretKey from raw byte array. - let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) }; - let shared = ecdh::SharedSecret::new_raw(context, &publ, sec); + let sec = key::SecretKey::from_slice(context, &secret)?; + let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); - let mut s = Secret::default(); - s.copy_from_slice(&shared[0..32]); - Ok(s) + Secret::from_slice(&shared[0..32]) + .map_err(|_| Error::Secp(SecpError::InvalidSecretKey)) } } diff --git a/ethkey/src/brain.rs b/ethkey/src/brain.rs index 2db460812..ad194388c 100644 --- a/ethkey/src/brain.rs +++ b/ethkey/src/brain.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use keccak::Keccak256; -use super::{KeyPair, Error, Generator}; +use super::{KeyPair, Error, Generator, Secret}; /// Simple brainwallet. pub struct Brain(String); @@ -34,13 +34,15 @@ impl Generator for Brain { let mut i = 0; loop { secret = secret.keccak256(); - + match i > 16384 { false => i += 1, true => { - let result = KeyPair::from_secret(secret.clone().into()); - if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) { - return result; + if let Ok(secret) = Secret::from_slice(&secret) { + let result = KeyPair::from_secret(secret); + if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) { + return result; + } } }, } diff --git a/ethkey/src/keypair.rs b/ethkey/src/keypair.rs index 8d6eceb9c..7fd181523 100644 --- a/ethkey/src/keypair.rs +++ b/ethkey/src/keypair.rs @@ -60,11 +60,14 @@ impl KeyPair { Ok(keypair) } + pub fn from_secret_slice(slice: &[u8]) -> Result { + Self::from_secret(Secret::from_slice(slice)?) + } + pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self { let context = &SECP256K1; let serialized = publ.serialize_vec(context, false); - let mut secret = Secret::default(); - secret.copy_from_slice(&sec[0..32]); + let secret = Secret::from(sec); let mut public = Public::default(); public.copy_from_slice(&serialized[1..65]); diff --git a/ethkey/src/lib.rs b/ethkey/src/lib.rs index 79faf0ef9..79921fd8c 100644 --- a/ethkey/src/lib.rs +++ b/ethkey/src/lib.rs @@ -29,6 +29,7 @@ mod keccak; mod prefix; mod random; mod signature; +mod secret; lazy_static! { pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); @@ -46,10 +47,10 @@ pub use self::keypair::{KeyPair, public_to_address}; pub use self::prefix::Prefix; pub use self::random::Random; pub use self::signature::{sign, verify_public, verify_address, recover, Signature}; +pub use self::secret::Secret; use bigint::hash::{H160, H256, H512}; pub type Address = H160; -pub type Secret = H256; pub type Message = H256; pub type Public = H512; diff --git a/ethkey/src/secret.rs b/ethkey/src/secret.rs new file mode 100644 index 000000000..f109abaa4 --- /dev/null +++ b/ethkey/src/secret.rs @@ -0,0 +1,69 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +use std::fmt; +use std::ops::Deref; +use std::str::FromStr; +use secp256k1::key; +use bigint::hash::H256; +use {Error}; + +#[derive(Clone, PartialEq, Eq)] +pub struct Secret { + inner: H256, +} + +impl fmt::Debug for Secret { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31]) + } +} + +impl Secret { + pub fn from_slice(key: &[u8]) -> Result { + if key.len() != 32 { + return Err(Error::InvalidSecret); + } + + let mut h = H256::default(); + h.copy_from_slice(&key[0..32]); + Ok(Secret { inner: h }) + } +} + +impl FromStr for Secret { + type Err = Error; + fn from_str(s: &str) -> Result { + let hash = H256::from_str(s).map_err(|e| Error::Custom(format!("{:?}", e)))?; + Self::from_slice(&hash) + } +} + +impl From for Secret { + fn from(key: key::SecretKey) -> Self { + Self::from_slice(&key[0..32]) + .expect("`key::SecretKey` is valid (no way to construct invalid one); qed") + } +} + +impl Deref for Secret { + type Target = H256; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + diff --git a/ethkey/src/signature.rs b/ethkey/src/signature.rs index ef28fb408..724fd125f 100644 --- a/ethkey/src/signature.rs +++ b/ethkey/src/signature.rs @@ -16,7 +16,7 @@ use std::ops::{Deref, DerefMut}; use std::cmp::PartialEq; -use std::{mem, fmt}; +use std::fmt; use std::str::FromStr; use std::hash::{Hash, Hasher}; use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; @@ -169,9 +169,8 @@ impl DerefMut for Signature { pub fn sign(secret: &Secret, message: &Message) -> Result { let context = &SECP256K1; - // no way to create from raw byte array. - let sec: &SecretKey = unsafe { mem::transmute(secret) }; - let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, sec)?; + let sec = SecretKey::from_slice(context, &secret)?; + let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?; let (rec_id, data) = s.serialize_compact(context); let mut data_arr = [0; 65]; diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index 76da94021..72ea08ed5 100644 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -122,16 +122,14 @@ impl Crypto { return Err(Error::InvalidPassword); } - let mut secret = Secret::default(); - match self.cipher { Cipher::Aes128Ctr(ref params) => { let from = 32 - self.ciphertext.len(); - crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut (&mut *secret)[from..]) + let mut secret = [0; 32]; + crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut secret[from..]); + Ok(Secret::from_slice(&secret)?) }, } - - Ok(secret) } } diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index b9a15aed5..45d127664 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -47,7 +47,7 @@ impl PresaleWallet { let len = crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)?; let unpadded = &key[..len]; - let secret = Secret::from(unpadded.keccak256()); + let secret = Secret::from_slice(&unpadded.keccak256())?; if let Ok(kp) = KeyPair::from_secret(secret) { if kp.address() == self.address { return Ok(kp) diff --git a/ethstore/tests/api.rs b/ethstore/tests/api.rs index 6485c3347..e1a98c90a 100644 --- a/ethstore/tests/api.rs +++ b/ethstore/tests/api.rs @@ -133,9 +133,9 @@ fn secret_store_load_pat_files() { #[test] fn test_decrypting_files_with_short_ciphertext() { // 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30 - let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".into()).unwrap(); + let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap(); // d1e64e5480bfaf733ba7d48712decb8227797a4e , 31 - let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".into()).unwrap(); + let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap(); let dir = DiskDirectory::at(ciphertext_path()); let store = EthStore::open(Box::new(dir)).unwrap(); let accounts = store.accounts().unwrap(); diff --git a/js/package.json b/js/package.json index 413d1515a..f70e9b9be 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.182", + "version": "0.2.184", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", diff --git a/js/src/abi/spec/event/event.js b/js/src/abi/spec/event/event.js index a17de1e64..025f2c316 100644 --- a/js/src/abi/spec/event/event.js +++ b/js/src/abi/spec/event/event.js @@ -23,12 +23,12 @@ import { eventSignature } from '../../util/signature'; export default class Event { constructor (abi) { - this._name = abi.name; this._inputs = EventParam.toEventParams(abi.inputs || []); this._anonymous = !!abi.anonymous; - const { id, signature } = eventSignature(this._name, this.inputParamTypes()); + const { id, name, signature } = eventSignature(abi.name, this.inputParamTypes()); this._id = id; + this._name = name; this._signature = signature; } diff --git a/js/src/abi/spec/function.js b/js/src/abi/spec/function.js index 68a6ca342..04e059c47 100644 --- a/js/src/abi/spec/function.js +++ b/js/src/abi/spec/function.js @@ -22,14 +22,14 @@ import { methodSignature } from '../util/signature'; export default class Func { constructor (abi) { this._abi = abi; - this._name = abi.name; this._constant = !!abi.constant; this._payable = abi.payable; this._inputs = Param.toParams(abi.inputs || []); this._outputs = Param.toParams(abi.outputs || []); - const { id, signature } = methodSignature(this._name, this.inputParamTypes()); + const { id, name, signature } = methodSignature(abi.name, this.inputParamTypes()); this._id = id; + this._name = name; this._signature = signature; } diff --git a/js/src/abi/spec/function.spec.js b/js/src/abi/spec/function.spec.js index 6ad755d70..af43958c9 100644 --- a/js/src/abi/spec/function.spec.js +++ b/js/src/abi/spec/function.spec.js @@ -35,6 +35,18 @@ describe('abi/spec/Function', () => { }); describe('constructor', () => { + it('returns signature correctly if name already contains it', () => { + const func = new Func({ + name: 'test(bool,string)', + inputs: inputsArr, + outputs: outputsArr + }); + + expect(func.name).to.equal('test'); + expect(func.id).to.equal('test(bool,string)'); + expect(func.signature).to.equal('02356205'); + }); + it('stores the parameters as received', () => { expect(func.name).to.equal('test'); expect(func.constant).to.be.false; diff --git a/js/src/abi/util/signature.js b/js/src/abi/util/signature.js index f192e576b..ad2b7ca5b 100644 --- a/js/src/abi/util/signature.js +++ b/js/src/abi/util/signature.js @@ -17,15 +17,31 @@ import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase import { fromParamType } from '../spec/paramType/format'; -export function eventSignature (name, params) { +export function eventSignature (eventName, params) { + const { strName, name } = parseName(eventName); const types = (params || []).map(fromParamType).join(','); - const id = `${name || ''}(${types})`; + const id = `${strName}(${types})`; - return { id, signature: keccak_256(id) }; + return { id, name, signature: keccak_256(id) }; } -export function methodSignature (name, params) { - const { id, signature } = eventSignature(name, params); +export function methodSignature (methodName, params) { + const { id, name, signature } = eventSignature(methodName, params); - return { id, signature: signature.substr(0, 8) }; + return { id, name, signature: signature.substr(0, 8) }; +} + +function parseName (name) { + const strName = `${name || ''}`; + const idx = strName.indexOf('('); + + if (idx === -1) { + return { strName, name }; + } + + const trimmedName = strName.slice(0, idx); + return { + strName: trimmedName, + name: trimmedName + }; } diff --git a/js/src/abi/util/signature.spec.js b/js/src/abi/util/signature.spec.js index 144c1c7aa..dfa81333f 100644 --- a/js/src/abi/util/signature.spec.js +++ b/js/src/abi/util/signature.spec.js @@ -19,50 +19,93 @@ import { eventSignature, methodSignature } from './signature'; describe('abi/util/signature', () => { describe('eventSignature', () => { it('encodes signature baz() correctly', () => { - expect(eventSignature('baz', [])) - .to.deep.equal({ id: 'baz()', signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf' }); + expect(eventSignature('baz', [])).to.deep.equal({ + id: 'baz()', + name: 'baz', + signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf' + }); }); it('encodes signature baz(uint32) correctly', () => { - expect(eventSignature('baz', [{ type: 'uint', length: 32 }])) - .to.deep.equal({ id: 'baz(uint32)', signature: '7d68785e8fc871be024b75964bd86d093511d4bc2dc7cf7bea32c48a0efaecb1' }); + expect(eventSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({ + id: 'baz(uint32)', + name: 'baz', + signature: '7d68785e8fc871be024b75964bd86d093511d4bc2dc7cf7bea32c48a0efaecb1' + }); }); it('encodes signature baz(uint32, bool) correctly', () => { - expect(eventSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])) - .to.deep.equal({ id: 'baz(uint32,bool)', signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2' }); + expect(eventSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({ + id: 'baz(uint32,bool)', + name: 'baz', + signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2' + }); }); it('encodes no-name signature correctly as ()', () => { - expect(eventSignature(undefined, [])) - .to.deep.equal({ id: '()', signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' }); + expect(eventSignature(undefined, [])).to.deep.equal({ + id: '()', + name: undefined, + signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' + }); }); it('encodes no-params signature correctly as ()', () => { - expect(eventSignature(undefined, undefined)) - .to.deep.equal({ id: '()', signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' }); + expect(eventSignature(undefined, undefined)).to.deep.equal({ + id: '()', + name: undefined, + signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' + }); }); }); describe('methodSignature', () => { it('encodes signature baz() correctly', () => { - expect(methodSignature('baz', [])).to.deep.equal({ id: 'baz()', signature: 'a7916fac' }); + expect(methodSignature('baz', [])).to.deep.equal({ + id: 'baz()', + name: 'baz', + signature: 'a7916fac' + }); }); it('encodes signature baz(uint32) correctly', () => { - expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({ id: 'baz(uint32)', signature: '7d68785e' }); + expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({ + id: 'baz(uint32)', + name: 'baz', + signature: '7d68785e' + }); }); it('encodes signature baz(uint32, bool) correctly', () => { - expect(methodSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({ id: 'baz(uint32,bool)', signature: 'cdcd77c0' }); + expect(methodSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({ + id: 'baz(uint32,bool)', + name: 'baz', + signature: 'cdcd77c0' + }); + }); + + it('encodes signature in name correctly', () => { + expect(methodSignature('baz(uint32,bool)', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({ + id: 'baz(uint32,bool)', + name: 'baz', + signature: 'cdcd77c0' + }); }); it('encodes no-name signature correctly as ()', () => { - expect(methodSignature(undefined, [])).to.deep.equal({ id: '()', signature: '861731d5' }); + expect(methodSignature(undefined, [])).to.deep.equal({ + id: '()', + name: undefined, + signature: '861731d5' + }); }); it('encodes no-params signature correctly as ()', () => { - expect(methodSignature(undefined, undefined)).to.deep.equal({ id: '()', signature: '861731d5' }); + expect(methodSignature(undefined, undefined)).to.deep.equal({ + id: '()', + name: undefined, + signature: '861731d5' + }); }); }); }); diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 9c3b02b72..df134fbfd 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -248,18 +248,32 @@ export default class Contract { .call(callParams) .then((encoded) => func.decodeOutput(encoded)) .then((tokens) => tokens.map((token) => token.value)) - .then((returns) => returns.length === 1 ? returns[0] : returns); + .then((returns) => returns.length === 1 ? returns[0] : returns) + .catch((error) => { + console.warn(`${func.name}.call`, values, error); + throw error; + }); }; if (!func.constant) { func.postTransaction = (options, values = []) => { const _options = this._encodeOptions(func, this._addOptionsTo(options), values); - return this._api.parity.postTransaction(_options); + return this._api.parity + .postTransaction(_options) + .catch((error) => { + console.warn(`${func.name}.postTransaction`, values, error); + throw error; + }); }; func.estimateGas = (options, values = []) => { const _options = this._encodeOptions(func, this._addOptionsTo(options), values); - return this._api.eth.estimateGas(_options); + return this._api.eth + .estimateGas(_options) + .catch((error) => { + console.warn(`${func.name}.estimateGas`, values, error); + throw error; + }); }; } @@ -385,6 +399,10 @@ export default class Contract { this._subscribeToChanges(); return subscriptionId; }); + }) + .catch((error) => { + console.warn('subscribe', event, _options, error); + throw error; }); } diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 594ad3922..569356cf9 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -20,7 +20,6 @@ import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { toWei } from '~/api/util/wei'; import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Modal, TxHash, Warning } from '~/ui'; @@ -470,11 +469,7 @@ function mapStateToProps (initState, initProps) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(ExecuteContract); diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js index 338e1f255..3a01b94ae 100644 --- a/js/src/modals/Transfer/store.js +++ b/js/src/modals/Transfer/store.js @@ -23,7 +23,7 @@ import { bytesToHex } from '~/api/util/format'; import Contract from '~/api/contract'; import ERRORS from './errors'; import { ERROR_CODES } from '~/api/transport/error'; -import { DEFAULT_GAS, MAX_GAS_ESTIMATION } from '~/util/constants'; +import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants'; import GasPriceStore from '~/ui/GasPriceEditor/store'; import { getLogger, LOG_KEYS } from '~/config'; @@ -441,6 +441,8 @@ export default class TransferStore { const gasTotal = new BigNumber(_gasTotal || 0); const { valueAll, isEth, isWallet } = this; + log.debug('@getValues', 'gas', gasTotal.toFormat()); + if (!valueAll) { const value = this.getTokenValue(); @@ -568,6 +570,7 @@ export default class TransferStore { send () { const { options, values } = this._getTransferParams(); options.minBlock = new BigNumber(this.minBlock || 0).gt(0) ? this.minBlock : null; + log.debug('@send', 'transfer value', options.value && options.value.toFormat()); return this._getTransferMethod().postTransaction(options, values); } @@ -626,7 +629,8 @@ export default class TransferStore { options.gas = MAX_GAS_ESTIMATION; } - const { token } = this.getValues(options.gas); + const gasTotal = new BigNumber(options.gas || DEFAULT_GAS).mul(options.gasPrice || DEFAULT_GASPRICE); + const { token } = this.getValues(gasTotal); if (isEth && !isWallet && !forceToken) { options.value = token; diff --git a/js/src/redux/providers/personalActions.js b/js/src/redux/providers/personalActions.js index 5d91aeef8..d16e722bf 100644 --- a/js/src/redux/providers/personalActions.js +++ b/js/src/redux/providers/personalActions.js @@ -75,6 +75,9 @@ export function personalAccountsInfo (accountsInfo) { return wallet; }); }) + .catch(() => { + return []; + }) .then((_wallets) => { _wallets.forEach((wallet) => { const owners = wallet.owners.map((o) => o.address); @@ -96,6 +99,10 @@ export function personalAccountsInfo (accountsInfo) { dispatch(_personalAccountsInfo(data)); dispatch(attachWallets(wallets)); dispatch(fetchBalances()); + }) + .catch((error) => { + console.warn('personalAccountsInfo', error); + throw error; }); }; } diff --git a/js/src/ui/Balance/balance.js b/js/src/ui/Balance/balance.js index e4dced882..b8f98c2ac 100644 --- a/js/src/ui/Balance/balance.js +++ b/js/src/ui/Balance/balance.js @@ -17,7 +17,6 @@ import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import unknownImage from '../../../assets/images/contracts/unknown-64x64.png'; import styles from './balance.css'; @@ -107,11 +106,7 @@ function mapStateToProps (state) { return { images }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Balance); diff --git a/js/src/ui/BlockStatus/blockStatus.js b/js/src/ui/BlockStatus/blockStatus.js index 47ee1a1c8..22c0fab2e 100644 --- a/js/src/ui/BlockStatus/blockStatus.js +++ b/js/src/ui/BlockStatus/blockStatus.js @@ -17,7 +17,6 @@ import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import styles from './blockStatus.css'; @@ -113,11 +112,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(BlockStatus); diff --git a/js/src/ui/Form/InputAddress/inputAddress.js b/js/src/ui/Form/InputAddress/inputAddress.js index c10ee126e..79bb24c8d 100644 --- a/js/src/ui/Form/InputAddress/inputAddress.js +++ b/js/src/ui/Form/InputAddress/inputAddress.js @@ -16,7 +16,6 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import util from '~/api/util'; import { nodeOrStringProptype } from '~/util/proptypes'; @@ -162,11 +161,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(InputAddress); diff --git a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js index f5b218694..46e4ac3b1 100644 --- a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js +++ b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js @@ -16,7 +16,6 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import AddressSelect from '../AddressSelect'; @@ -68,11 +67,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(InputAddressSelect); diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js index 693ae60b5..2c4c28e47 100644 --- a/js/src/ui/MethodDecoding/methodDecoding.js +++ b/js/src/ui/MethodDecoding/methodDecoding.js @@ -377,11 +377,15 @@ class MethodDecoding extends Component { } const inputs = methodInputs.map((input, index) => { + const label = input.name + ? `${input.name}: ${input.type}` + : input.type; + return ( { - const type = abi.inputs[index].type; - return { type, value }; + const { name, type } = abi.inputs[index]; + return { name, type, value }; }); } diff --git a/js/src/ui/Modal/modal.js b/js/src/ui/Modal/modal.js index d9d6ddbaf..72a9d74ae 100644 --- a/js/src/ui/Modal/modal.js +++ b/js/src/ui/Modal/modal.js @@ -16,9 +16,8 @@ import { Dialog } from 'material-ui'; import React, { Component, PropTypes } from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import ReactDOM from 'react-dom'; +import { connect } from 'react-redux'; import { nodeOrStringProptype } from '~/util/proptypes'; @@ -113,11 +112,7 @@ function mapStateToProps (state) { return { settings }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Modal); diff --git a/js/src/ui/SignerIcon/signerIcon.js b/js/src/ui/SignerIcon/signerIcon.js index ca1ca43fa..3f6e6dd0e 100644 --- a/js/src/ui/SignerIcon/signerIcon.js +++ b/js/src/ui/SignerIcon/signerIcon.js @@ -16,7 +16,6 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase import ActionFingerprint from 'material-ui/svg-icons/action/fingerprint'; @@ -54,11 +53,7 @@ function mapStateToProps (state) { return { secureToken }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(SignerIcon); diff --git a/js/src/ui/TxHash/txHash.js b/js/src/ui/TxHash/txHash.js index df5e9342e..c054dc01f 100644 --- a/js/src/ui/TxHash/txHash.js +++ b/js/src/ui/TxHash/txHash.js @@ -15,11 +15,10 @@ // along with Parity. If not, see . import BigNumber from 'bignumber.js'; +import { LinearProgress } from 'material-ui'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { LinearProgress } from 'material-ui'; import { txLink } from '~/3rdparty/etherscan/links'; import ShortenedHash from '../ShortenedHash'; @@ -169,11 +168,7 @@ function mapStateToProps (state) { return { isTest }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(TxHash); diff --git a/js/src/ui/TxList/txList.js b/js/src/ui/TxList/txList.js index b99335a4f..5cc5618d6 100644 --- a/js/src/ui/TxList/txList.js +++ b/js/src/ui/TxList/txList.js @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { observer } from 'mobx-react'; import Store from './store'; import TxRow from './TxRow'; @@ -92,11 +91,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(TxList); diff --git a/js/src/util/subscribe-to-events.spec.js b/js/src/util/subscribe-to-events.spec.js new file mode 100644 index 000000000..bbb0d280d --- /dev/null +++ b/js/src/util/subscribe-to-events.spec.js @@ -0,0 +1,115 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +import { spy, stub } from 'sinon'; + +import subscribeToEvents from './subscribe-to-events'; +import { + pastLogs, liveLogs, createApi, createContract +} from './subscribe-to-events.test.js'; + +const delay = (t) => new Promise((resolve) => { + setTimeout(resolve, t); +}); + +describe('util/subscribe-to-events', () => { + beforeEach(function () { + this.api = createApi(); + this.contract = createContract(this.api); + }); + + it('installs a filter', async function () { + const { api, contract } = this; + + subscribeToEvents(contract, [ 'Foo', 'Bar' ]); + await delay(0); + + expect(api.eth.newFilter.calledOnce).to.equal(true); + expect(api.eth.newFilter.firstCall.args).to.eql([ { + fromBlock: 0, toBlock: 'latest', + address: contract.address, + topics: [ [ + contract.instance.Foo.signature, + contract.instance.Bar.signature + ] ] + } ]); + }); + + it('queries & parses logs in the beginning', async function () { + const { api, contract } = this; + + subscribeToEvents(contract, [ 'Foo', 'Bar' ]); + + await delay(0); + expect(api.eth.getFilterLogs.callCount).to.equal(1); + expect(api.eth.getFilterLogs.firstCall.args).to.eql([ 123 ]); + + await delay(0); + expect(contract.parseEventLogs.callCount).to.equal(1); + }); + + it('emits logs in the beginning', async function () { + const { contract } = this; + + const onLog = spy(); + const onFoo = spy(); + const onBar = spy(); + subscribeToEvents(contract, [ 'Foo', 'Bar' ]) + .on('log', onLog) + .on('Foo', onFoo) + .on('Bar', onBar); + + await delay(0); + + expect(onLog.callCount).to.equal(2); + expect(onLog.firstCall.args).to.eql([ pastLogs[0] ]); + expect(onLog.secondCall.args).to.eql([ pastLogs[1] ]); + expect(onFoo.callCount).to.equal(1); + expect(onFoo.firstCall.args).to.eql([ pastLogs[0] ]); + expect(onBar.callCount).to.equal(1); + expect(onBar.firstCall.args).to.eql([ pastLogs[1] ]); + }); + + it('uninstalls the filter on sunsubscribe', async function () { + const { api, contract } = this; + + const s = subscribeToEvents(contract, [ 'Foo', 'Bar' ]); + await delay(0); + s.unsubscribe(); + await delay(0); + + expect(api.eth.uninstallFilter.calledOnce).to.equal(true); + expect(api.eth.uninstallFilter.firstCall.args).to.eql([ 123 ]); + }); + + it.skip('checks for new events regularly', async function () { + const { api, contract } = this; + api.eth.getFilterLogs = stub().resolves([]); + + const onLog = spy(); + const onBar = spy(); + const s = subscribeToEvents(contract, [ 'Bar' ], { interval: 5 }) + .on('log', onLog) + .on('Bar', onBar); + await delay(9); + s.unsubscribe(); + + expect(onLog.callCount).to.equal(1); + expect(onLog.firstCall.args).to.eql([ liveLogs[0] ]); + expect(onBar.callCount).to.equal(1); + expect(onBar.firstCall.args).to.eql([ liveLogs[0] ]); + }); +}); diff --git a/js/src/util/subscribe-to-events.test.js b/js/src/util/subscribe-to-events.test.js new file mode 100644 index 000000000..642f8e592 --- /dev/null +++ b/js/src/util/subscribe-to-events.test.js @@ -0,0 +1,53 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +import { stub } from 'sinon'; + +export const ADDRESS = '0x1111111111111111111111111111111111111111'; + +export const pastLogs = [ + { event: 'Foo', type: 'mined', address: ADDRESS, params: {} }, + { event: 'Bar', type: 'mined', address: ADDRESS, params: {} } +]; + +export const liveLogs = [ + { event: 'Bar', type: 'mined', address: ADDRESS, params: { foo: 'bar' } } +]; + +export const createApi = () => ({ + eth: { + newFilter: stub().resolves(123), + uninstallFilter: stub() + .rejects(new Error('unknown filter id')) + .withArgs(123).resolves(null), + getFilterLogs: stub() + .rejects(new Error('unknown filter id')) + .withArgs(123).resolves(pastLogs), + getFilterChanges: stub() + .rejects(new Error('unknown filter id')) + .withArgs(123).resolves(liveLogs) + } +}); + +export const createContract = (api) => ({ + api, + address: ADDRESS, + instance: { + Foo: { signature: 'Foo signature' }, + Bar: { signature: 'Bar signature' } + }, + parseEventLogs: stub().returnsArg(0) +}); diff --git a/js/src/views/Account/Transactions/transactions.js b/js/src/views/Account/Transactions/transactions.js index 5e48d5c5c..547c918d8 100644 --- a/js/src/views/Account/Transactions/transactions.js +++ b/js/src/views/Account/Transactions/transactions.js @@ -18,7 +18,6 @@ import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { Container, TxList, Loading } from '~/ui'; @@ -118,11 +117,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Transactions); diff --git a/js/src/views/Application/Status/status.js b/js/src/views/Application/Status/status.js index 1aded4b88..1e2cd4d41 100644 --- a/js/src/views/Application/Status/status.js +++ b/js/src/views/Application/Status/status.js @@ -17,7 +17,6 @@ import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { BlockStatus } from '~/ui'; @@ -133,11 +132,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Status); diff --git a/js/src/views/Application/application.js b/js/src/views/Application/application.js index 03b85ca38..256ff0212 100644 --- a/js/src/views/Application/application.js +++ b/js/src/views/Application/application.js @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { observer } from 'mobx-react'; import UpgradeStore from '~/modals/UpgradeParity/store'; @@ -123,11 +122,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Application); diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index 8ebbb602d..27e5c8b52 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -14,13 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { omitBy } from 'lodash'; import { Checkbox } from 'material-ui'; import { observer } from 'mobx-react'; import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { omitBy } from 'lodash'; import { AddDapps, DappPermissions } from '~/modals'; import PermissionStore from '~/modals/DappPermissions/store'; @@ -169,11 +168,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(Dapps); diff --git a/js/src/views/Dapps/dappsStore.js b/js/src/views/Dapps/dappsStore.js index 8cca4d3f7..efbde9ef4 100644 --- a/js/src/views/Dapps/dappsStore.js +++ b/js/src/views/Dapps/dappsStore.js @@ -256,7 +256,7 @@ export default class DappsStore { store.set(LS_KEY_DISPLAY, this.displayApps); } - @action addApps = (_apps) => { + @action addApps = (_apps = []) => { transaction(() => { const apps = _apps.filter((app) => app); diff --git a/js/src/views/ParityBar/parityBar.js b/js/src/views/ParityBar/parityBar.js index 42c52f47d..8893ac566 100644 --- a/js/src/views/ParityBar/parityBar.js +++ b/js/src/views/ParityBar/parityBar.js @@ -17,7 +17,6 @@ import React, { Component, PropTypes } from 'react'; import { Link } from 'react-router'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import ActionFingerprint from 'material-ui/svg-icons/action/fingerprint'; import ContentClear from 'material-ui/svg-icons/content/clear'; @@ -162,11 +161,7 @@ function mapStateToProps (state) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(ParityBar); diff --git a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js index 5a93cef0d..f9d93cbe1 100644 --- a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js +++ b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js @@ -14,13 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import keycode from 'keycode'; +import RaisedButton from 'material-ui/RaisedButton'; import React, { Component, PropTypes } from 'react'; import ReactDOM from 'react-dom'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import RaisedButton from 'material-ui/RaisedButton'; import ReactTooltip from 'react-tooltip'; -import keycode from 'keycode'; import { Form, Input, IdentityIcon } from '~/ui'; @@ -258,11 +257,7 @@ function mapStateToProps (_, initProps) { }; } -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); -} - export default connect( mapStateToProps, - mapDispatchToProps + null )(TransactionPendingFormConfirm); diff --git a/parity/configuration.rs b/parity/configuration.rs index 671c78206..42816a823 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -20,9 +20,10 @@ use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::cmp::max; use cli::{Args, ArgsError}; -use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address}; +use util::{Hashable, U256, Uint, Bytes, version_data, Address}; use util::log::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP}; +use ethcore::ethstore::ethkey::Secret; use ethcore::client::{VMType}; use ethcore::miner::{MinerOptions, Banning}; use ethcore::verification::queue::VerifierSettings; @@ -603,7 +604,13 @@ impl Configuration { let (listen, public) = self.net_addresses()?; ret.listen_address = listen.map(|l| format!("{}", l)); ret.public_address = public.map(|p| format!("{}", p)); - ret.use_secret = self.args.flag_node_key.as_ref().map(|s| s.parse::().unwrap_or_else(|_| s.sha3())); + ret.use_secret = match self.args.flag_node_key.as_ref() + .map(|s| s.parse::().or_else(|_| Secret::from_slice(&s.sha3())).map_err(|e| format!("Invalid key: {:?}", e)) + ) { + None => None, + Some(Ok(key)) => Some(key), + Some(Err(err)) => return Err(err), + }; ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover; ret.max_peers = self.max_peers(); ret.min_peers = self.min_peers(); diff --git a/parity/dapps.rs b/parity/dapps.rs index 591c17593..d1edcbd65 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -22,9 +22,9 @@ use ethcore::client::Client; use ethsync::SyncProvider; use helpers::replace_home; use dir::default_data_path; +use jsonrpc_core::reactor::Remote; use rpc_apis::SignerService; use hash_fetch::fetch::Client as FetchClient; -use parity_reactor::Remote; #[derive(Debug, PartialEq, Clone)] pub struct Configuration { @@ -130,6 +130,8 @@ mod server { use rpc_apis; use ethcore_rpc::is_major_importing; use hash_fetch::urlhint::ContractClient; + use jsonrpc_core::reactor::RpcHandler; + use parity_reactor; pub use ethcore_dapps::Server as WebappServer; @@ -146,8 +148,9 @@ mod server { let server = dapps::ServerBuilder::new( &dapps_path, Arc::new(Registrar { client: deps.client.clone() }), - deps.remote.clone(), + parity_reactor::Remote::new(deps.remote.clone()), ); + let sync = deps.sync.clone(); let client = deps.client.clone(); let signer = deps.signer.clone(); @@ -156,15 +159,17 @@ mod server { .sync_status(Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info()))) .web_proxy_tokens(Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token))) .extra_dapps(&extra_dapps) - .signer_address(deps.signer.address()); + .signer_address(deps.signer.address()) + .allowed_hosts(allowed_hosts); - let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext); + let apis = rpc_apis::setup_rpc(Default::default(), deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext); + let handler = RpcHandler::new(Arc::new(apis), deps.remote); let start_result = match auth { None => { - server.start_unsecured_http(url, allowed_hosts) + server.start_unsecured_http(url, handler) }, Some((username, password)) => { - server.start_basic_auth_http(url, allowed_hosts, &username, &password) + server.start_basic_auth_http(url, &username, &password, handler) }, }; @@ -175,8 +180,9 @@ mod server { }, Err(e) => Err(format!("WebApps error: {:?}", e)), Ok(server) => { + let ph = deps.panic_handler; server.set_panic_handler(move || { - deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned()); + ph.notify_all("Panic in WebApp thread.".to_owned()); }); Ok(server) }, diff --git a/parity/main.rs b/parity/main.rs index 7d2be8fb0..082879091 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -41,6 +41,7 @@ extern crate ethcore_ipc as ipc; extern crate ethcore_ipc_nano as nanoipc; extern crate serde; extern crate serde_json; +extern crate jsonrpc_core; extern crate rlp; extern crate ethcore_light as light; extern crate parity_hash_fetch as hash_fetch; diff --git a/parity/presale.rs b/parity/presale.rs index 02ae8dfdd..d5d028367 100644 --- a/parity/presale.rs +++ b/parity/presale.rs @@ -40,6 +40,6 @@ pub fn execute(cmd: ImportWallet) -> Result { let acc_provider = AccountProvider::new(secret_store); let wallet = PresaleWallet::open(cmd.wallet_path).map_err(|_| "Unable to open presale wallet.")?; let kp = wallet.decrypt(&password).map_err(|_| "Invalid password.")?; - let address = acc_provider.insert_account(*kp.secret(), &password).unwrap(); + let address = acc_provider.insert_account(kp.secret().clone(), &password).unwrap(); Ok(format!("{:?}", address)) } diff --git a/parity/rpc.rs b/parity/rpc.rs index 4fe126e89..178a7f14b 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -19,11 +19,14 @@ use std::sync::Arc; use std::net::SocketAddr; use std::io; use io::PanicHandler; -use ethcore_rpc::{RpcServerError, RpcServer as Server, IpcServerError}; + +use dir::default_data_path; +use ethcore_rpc::{self as rpc, RpcServerError, IpcServerError, Metadata}; +use helpers::parity_ipc_path; +use jsonrpc_core::MetaIoHandler; +use jsonrpc_core::reactor::{RpcHandler, Remote}; use rpc_apis; use rpc_apis::ApiSet; -use helpers::parity_ipc_path; -use dir::default_data_path; pub use ethcore_rpc::{IpcServer, Server as HttpServer}; @@ -81,6 +84,7 @@ impl fmt::Display for IpcConfiguration { pub struct Dependencies { pub panic_handler: Arc, pub apis: Arc, + pub remote: Remote, } pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result, String> { @@ -93,9 +97,8 @@ pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result Result { - let server = Server::new(); - Ok(rpc_apis::setup_rpc(server, deps.apis.clone(), apis)) +fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler { + rpc_apis::setup_rpc(MetaIoHandler::default(), deps.apis.clone(), apis) } pub fn setup_http_rpc_server( @@ -105,9 +108,10 @@ pub fn setup_http_rpc_server( allowed_hosts: Option>, apis: ApiSet ) -> Result { - let server = setup_rpc_server(apis, dependencies)?; + let apis = setup_apis(apis, dependencies); + let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone()); let ph = dependencies.panic_handler.clone(); - let start_result = server.start_http(url, cors_domains, allowed_hosts, ph); + let start_result = rpc::start_http(url, cors_domains, allowed_hosts, ph, handler); match start_result { Err(RpcServerError::IoError(err)) => match err.kind() { io::ErrorKind::AddrInUse => Err(format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)), @@ -118,14 +122,15 @@ pub fn setup_http_rpc_server( } } -pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result, String> { +pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result>, String> { if !conf.enabled { return Ok(None); } Ok(Some(setup_ipc_rpc_server(deps, &conf.socket_addr, conf.apis)?)) } -pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: ApiSet) -> Result { - let server = setup_rpc_server(apis, dependencies)?; - match server.start_ipc(addr) { +pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: ApiSet) -> Result, String> { + let apis = setup_apis(apis, dependencies); + let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone()); + match rpc::start_ipc(addr, handler) { Err(IpcServerError::Io(io_error)) => Err(format!("RPC io error: {}", io_error)), Err(any_error) => Err(format!("Rpc error: {:?}", any_error)), Ok(server) => Ok(server) diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 5260b9df5..062ef02db 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -20,16 +20,16 @@ use std::cmp::PartialEq; use std::str::FromStr; use std::sync::Arc; use util::RotatingLogger; +use jsonrpc_core::{MetaIoHandler}; use ethcore::miner::{Miner, ExternalMiner}; use ethcore::client::Client; use ethcore::account_provider::AccountProvider; use ethcore::snapshot::SnapshotService; use ethsync::{ManageNetwork, SyncProvider}; -use ethcore_rpc::{Extendable, NetworkSettings}; +use ethcore_rpc::{Metadata, NetworkSettings}; pub use ethcore_rpc::SignerService; use updater::Updater; use hash_fetch::fetch::Client as FetchClient; -use parity_reactor::Remote; #[derive(Debug, PartialEq, Clone, Eq, Hash)] pub enum Api { @@ -125,7 +125,6 @@ pub struct Dependencies { pub dapps_interface: Option, pub dapps_port: Option, pub fetch: FetchClient, - pub remote: Remote, } fn to_modules(apis: &[Api]) -> BTreeMap { @@ -170,18 +169,20 @@ impl ApiSet { } macro_rules! add_signing_methods { - ($namespace:ident, $server:expr, $deps:expr) => { - let server = &$server; - let deps = &$deps; - if deps.signer_service.is_enabled() { - server.add_delegate($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, &deps.client, &deps.miner, &deps.secret_store))) - } else { - server.add_delegate($namespace::to_delegate(SigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner))) + ($namespace:ident, $handler:expr, $deps:expr) => { + { + let handler = &mut $handler; + let deps = &$deps; + if deps.signer_service.is_enabled() { + handler.extend_with($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, &deps.client, &deps.miner, &deps.secret_store))) + } else { + handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner))) + } } } } -pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet) -> T { +pub fn setup_rpc(mut handler: MetaIoHandler, deps: Arc, apis: ApiSet) -> MetaIoHandler { use ethcore_rpc::v1::*; // it's turned into vector, cause ont of the cases requires &[] @@ -189,10 +190,10 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet for api in &apis { match *api { Api::Web3 => { - server.add_delegate(Web3Client::new().to_delegate()); + handler.extend_with(Web3Client::new().to_delegate()); }, Api::Net => { - server.add_delegate(NetClient::new(&deps.sync).to_delegate()); + handler.extend_with(NetClient::new(&deps.sync).to_delegate()); }, Api::Eth => { let client = EthClient::new( @@ -207,25 +208,25 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet send_block_number_in_get_work: !deps.geth_compatibility, } ); - server.add_delegate(client.to_delegate()); + handler.extend_with(client.to_delegate()); let filter_client = EthFilterClient::new(&deps.client, &deps.miner); - server.add_delegate(filter_client.to_delegate()); + handler.extend_with(filter_client.to_delegate()); - add_signing_methods!(EthSigning, server, deps); + add_signing_methods!(EthSigning, handler, deps); }, Api::Personal => { - server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner, deps.geth_compatibility).to_delegate()); + handler.extend_with(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner, deps.geth_compatibility).to_delegate()); }, Api::Signer => { - server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_service).to_delegate()); + handler.extend_with(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_service).to_delegate()); }, Api::Parity => { let signer = match deps.signer_service.is_enabled() { true => Some(deps.signer_service.clone()), false => None, }; - server.add_delegate(ParityClient::new( + handler.extend_with(ParityClient::new( &deps.client, &deps.miner, &deps.sync, @@ -239,32 +240,31 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet deps.dapps_port, ).to_delegate()); - add_signing_methods!(EthSigning, server, deps); - add_signing_methods!(ParitySigning, server, deps); + add_signing_methods!(EthSigning, handler, deps); + add_signing_methods!(ParitySigning, handler, deps); }, Api::ParityAccounts => { - server.add_delegate(ParityAccountsClient::new(&deps.secret_store, &deps.client).to_delegate()); + handler.extend_with(ParityAccountsClient::new(&deps.secret_store, &deps.client).to_delegate()); }, Api::ParitySet => { - server.add_delegate(ParitySetClient::new( + handler.extend_with(ParitySetClient::new( &deps.client, &deps.miner, &deps.updater, &deps.net_service, deps.fetch.clone(), - deps.remote.clone(), ).to_delegate()) }, Api::Traces => { - server.add_delegate(TracesClient::new(&deps.client, &deps.miner).to_delegate()) + handler.extend_with(TracesClient::new(&deps.client, &deps.miner).to_delegate()) }, Api::Rpc => { let modules = to_modules(&apis); - server.add_delegate(RpcClient::new(modules).to_delegate()); + handler.extend_with(RpcClient::new(modules).to_delegate()); } } } - server + handler } #[cfg(test)] diff --git a/parity/run.rs b/parity/run.rs index e4c5fca6f..55a8bb8e6 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -347,7 +347,6 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R ); service.add_notify(updater.clone()); - // set up dependencies for rpc servers let signer_path = cmd.signer_conf.signer_path.clone(); let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies { @@ -375,12 +374,12 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R false => None, }, fetch: fetch.clone(), - remote: event_loop.remote(), }); let dependencies = rpc::Dependencies { panic_handler: panic_handler.clone(), apis: deps_for_rpc_apis.clone(), + remote: event_loop.raw_remote(), }; // start rpc servers @@ -393,7 +392,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R apis: deps_for_rpc_apis.clone(), client: client.clone(), sync: sync_provider.clone(), - remote: event_loop.remote(), + remote: event_loop.raw_remote(), fetch: fetch.clone(), signer: deps_for_rpc_apis.signer_service.clone(), }; @@ -403,6 +402,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R let signer_deps = signer::Dependencies { panic_handler: panic_handler.clone(), apis: deps_for_rpc_apis.clone(), + remote: event_loop.raw_remote(), }; let signer_server = signer::start(cmd.signer_conf.clone(), signer_deps)?; diff --git a/parity/signer.rs b/parity/signer.rs index 43bb080da..69597e0a0 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -22,8 +22,9 @@ use io::{ForwardPanic, PanicHandler}; use util::path::restrict_permissions_owner; use rpc_apis; use ethcore_signer as signer; -use helpers::replace_home; use dir::default_data_path; +use helpers::replace_home; +use jsonrpc_core::reactor::{RpcHandler, Remote}; pub use ethcore_signer::Server as SignerServer; const CODES_FILENAME: &'static str = "authcodes"; @@ -53,6 +54,7 @@ impl Default for Configuration { pub struct Dependencies { pub panic_handler: Arc, pub apis: Arc, + pub remote: Remote, } pub struct NewToken { @@ -124,8 +126,9 @@ fn do_start(conf: Configuration, deps: Dependencies) -> Result(&self, delegate: IoDelegate); -} +/// Start http server asynchronously and returns result with `Server` handle on success or an error. +pub fn start_http( + addr: &SocketAddr, + cors_domains: Option>, + allowed_hosts: Option>, + panic_handler: Arc, + handler: RpcHandler, +) -> Result { -/// Http server. -pub struct RpcServer { - handler: Arc, -} - -impl Extendable for RpcServer { - /// Add io delegate. - fn add_delegate(&self, delegate: IoDelegate) { - self.handler.add_delegate(delegate); - } -} - -impl RpcServer { - /// Construct new http server object. - pub fn new() -> RpcServer { - RpcServer { - handler: Arc::new(IoHandler::new()), - } - } - - /// Start http server asynchronously and returns result with `Server` handle on success or an error. - pub fn start_http( - &self, - addr: &SocketAddr, - cors_domains: Option>, - allowed_hosts: Option>, - panic_handler: Arc, - ) -> Result { - - let cors_domains = cors_domains.map(|domains| { - domains.into_iter() - .map(|v| match v.as_str() { - "*" => jsonrpc_http_server::AccessControlAllowOrigin::Any, - "null" => jsonrpc_http_server::AccessControlAllowOrigin::Null, - v => jsonrpc_http_server::AccessControlAllowOrigin::Value(v.into()), - }) - .collect() - }); - - ServerBuilder::new(self.handler.clone()) - .cors(cors_domains.into()) - .allowed_hosts(allowed_hosts.into()) - .panic_handler(move || { - panic_handler.notify_all("Panic in RPC thread.".to_owned()); + let cors_domains = cors_domains.map(|domains| { + domains.into_iter() + .map(|v| match v.as_str() { + "*" => jsonrpc_http_server::AccessControlAllowOrigin::Any, + "null" => jsonrpc_http_server::AccessControlAllowOrigin::Null, + v => jsonrpc_http_server::AccessControlAllowOrigin::Value(v.into()), }) - .start_http(addr) - } + .collect() + }); - /// Start ipc server asynchronously and returns result with `Server` handle on success or an error. - pub fn start_ipc(&self, addr: &str) -> Result { - let server = ipc::Server::new(addr, &self.handler)?; - server.run_async()?; - Ok(server) - } + ServerBuilder::with_rpc_handler(handler) + .cors(cors_domains.into()) + .allowed_hosts(allowed_hosts.into()) + .panic_handler(move || { + panic_handler.notify_all("Panic in RPC thread.".to_owned()); + }) + .start_http(addr) +} + +/// Start ipc server asynchronously and returns result with `Server` handle on success or an error. +pub fn start_ipc(addr: &str, handler: RpcHandler) -> Result, ipc::Error> { + let server = ipc::Server::with_rpc_handler(addr, handler)?; + server.run_async()?; + Ok(server) } diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 80a0dab9a..00f383a04 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -36,6 +36,7 @@ mod codes { pub const UNKNOWN_ERROR: i64 = -32009; pub const TRANSACTION_ERROR: i64 = -32010; pub const EXECUTION_ERROR: i64 = -32015; + pub const EXCEPTION_ERROR: i64 = -32016; pub const ACCOUNT_LOCKED: i64 = -32020; pub const PASSWORD_INVALID: i64 = -32021; pub const ACCOUNT_ERROR: i64 = -32023; @@ -130,6 +131,14 @@ pub fn state_pruned() -> Error { } } +pub fn exceptional() -> Error { + Error { + code: ErrorCode::ServerError(codes::EXCEPTION_ERROR), + message: "The execution failed due to an exception.".into(), + data: None + } +} + pub fn no_work() -> Error { Error { code: ErrorCode::ServerError(codes::NO_WORK), @@ -286,6 +295,7 @@ pub fn from_rlp_error(error: DecoderError) -> Error { pub fn from_call_error(error: CallError) -> Error { match error { CallError::StatePruned => state_pruned(), + CallError::Exceptional => exceptional(), CallError::Execution(e) => execution(e), CallError::TransactionNotFound => internal("{}, this should not be the case with eth_call, most likely a bug.", CallError::TransactionNotFound), } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index fd5f3d4f3..855b17e2e 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -16,41 +16,45 @@ //! Eth rpc implementation. -extern crate ethash; - use std::io::{Write}; use std::process::{Command, Stdio}; use std::thread; use std::time::{Instant, Duration}; use std::sync::{Arc, Weak}; -use time::get_time; -use ethsync::{SyncProvider}; -use ethcore::miner::{MinerService, ExternalMinerService}; -use jsonrpc_core::*; -use jsonrpc_macros::Trailing; -use util::{H256, Address, FixedHash, U256, H64, Uint}; -use util::sha3::*; -use util::{FromHex, Mutex}; + +use futures::{self, BoxFuture, Future}; use rlp::{self, UntrustedRlp, View}; +use time::get_time; +use util::{H256, Address, FixedHash, U256, H64, Uint}; +use util::sha3::Hashable; +use util::{FromHex, Mutex}; + +use ethash::SeedHashCompute; use ethcore::account_provider::AccountProvider; -use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId}; -use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber}; use ethcore::block::IsBlock; +use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId}; use ethcore::ethereum::Ethash; -use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, PendingTransaction, Action}; -use ethcore::log_entry::LogEntry; use ethcore::filter::Filter as EthcoreFilter; +use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber}; +use ethcore::log_entry::LogEntry; +use ethcore::miner::{MinerService, ExternalMinerService}; +use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, PendingTransaction, Action}; use ethcore::snapshot::SnapshotService; -use self::ethash::SeedHashCompute; +use ethsync::{SyncProvider}; + +use jsonrpc_core::Error; +use jsonrpc_macros::Trailing; + use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, - Transaction, CallRequest, Index, Filter, Log, Receipt, Work, DappId, + Transaction, CallRequest, Index, Filter, Log, Receipt, Work, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, }; use v1::helpers::{CallRequest as CRequest, errors, limit_logs}; use v1::helpers::dispatch::{dispatch_transaction, default_gas_price}; use v1::helpers::block_import::is_major_importing; +use v1::metadata::Metadata; const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; @@ -271,7 +275,9 @@ impl Eth for EthClient where SN: SnapshotService + 'static, S: SyncProvider + 'static, M: MinerService + 'static, - EM: ExternalMinerService + 'static { + EM: ExternalMinerService + 'static, +{ + type Metadata = Metadata; fn protocol_version(&self) -> Result { self.active()?; @@ -338,18 +344,21 @@ impl Eth for EthClient where Ok(RpcU256::from(default_gas_price(&*client, &*miner))) } - fn accounts(&self, id: Trailing) -> Result, Error> { - self.active()?; + fn accounts(&self, meta: Metadata) -> BoxFuture, Error> { + let dapp = meta.dapp_id.unwrap_or_default(); - let dapp = id.0; + let accounts = move || { + self.active()?; - let store = take_weak!(self.accounts); - let accounts = store - .note_dapp_used(dapp.clone().into()) - .and_then(|_| store.dapps_addresses(dapp.into())) - .map_err(|e| errors::internal("Could not fetch accounts.", e))?; + let store = take_weak!(self.accounts); + let accounts = store + .note_dapp_used(dapp.clone().into()) + .and_then(|_| store.dapps_addresses(dapp.into())) + .map_err(|e| errors::internal("Could not fetch accounts.", e))?; + Ok(accounts.into_iter().map(Into::into).collect()) + }; - Ok(accounts.into_iter().map(Into::into).collect()) + futures::done(accounts()).boxed() } fn block_number(&self) -> Result { @@ -660,7 +669,6 @@ impl Eth for EthClient where num => take_weak!(self.client).call(&signed, num.into(), Default::default()), }; - result .map(|b| b.output.into()) .map_err(errors::from_call_error) diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index fa1f776b5..5970b1ec7 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -19,7 +19,7 @@ use std::sync::{Arc, Weak}; use std::collections::BTreeMap; use util::{Address}; -use ethkey::{Brain, Generator}; +use ethkey::{Brain, Generator, Secret}; use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; @@ -73,7 +73,8 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock self.active()?; let store = take_weak!(self.accounts); - store.insert_account(*Brain::new(phrase).generate().unwrap().secret(), &pass) + let brain = Brain::new(phrase).generate().unwrap(); + store.insert_account(brain.secret().clone(), &pass) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) } @@ -92,7 +93,9 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock self.active()?; let store = take_weak!(self.accounts); - store.insert_account(secret.into(), &pass) + let secret = Secret::from_slice(&secret.0) + .map_err(|e| errors::account("Could not create account.", e))?; + store.insert_account(secret, &pass) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) } diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index e0f9bca53..06ca70f2a 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -23,13 +23,11 @@ use ethcore::client::MiningBlockChainClient; use ethcore::mode::Mode; use ethsync::ManageNetwork; use fetch::{self, Fetch}; -use futures::Future; +use futures::{self, BoxFuture, Future}; use util::sha3; use updater::{Service as UpdateService}; -use parity_reactor::Remote; use jsonrpc_core::Error; -use jsonrpc_macros::Ready; use v1::helpers::errors; use v1::traits::ParitySet; use v1::types::{Bytes, H160, H256, U256, ReleaseInfo}; @@ -46,7 +44,6 @@ pub struct ParitySetClient where updater: Weak, net: Weak, fetch: F, - remote: Remote, } impl ParitySetClient where @@ -56,14 +53,13 @@ impl ParitySetClient where F: Fetch, { /// Creates new `ParitySetClient` with given `Fetch`. - pub fn new(client: &Arc, miner: &Arc, updater: &Arc, net: &Arc, fetch: F, remote: Remote) -> Self { + pub fn new(client: &Arc, miner: &Arc, updater: &Arc, net: &Arc, fetch: F) -> Self { ParitySetClient { client: Arc::downgrade(client), miner: Arc::downgrade(miner), updater: Arc::downgrade(updater), net: Arc::downgrade(net), fetch: fetch, - remote: remote, } } @@ -189,28 +185,19 @@ impl ParitySet for ParitySetClient where Ok(true) } - fn hash_content(&self, ready: Ready, url: String) { - let res = self.active(); - - match res { - Err(e) => ready.ready(Err(e)), - Ok(()) => { - let task = self.fetch.fetch(&url).then(move |result| { - let result = result - .map_err(errors::from_fetch_error) - .and_then(|response| { - sha3(&mut io::BufReader::new(response)).map_err(errors::from_fetch_error) - }) - .map(Into::into); - - // Receive ready and invoke with result. - ready.ready(result); - Ok(()) as Result<(), ()> - }); - - self.remote.spawn(task); - } + fn hash_content(&self, url: String) -> BoxFuture { + if let Err(e) = self.active() { + return futures::failed(e).boxed(); } + + self.fetch.process(self.fetch.fetch(&url).then(move |result| { + result + .map_err(errors::from_fetch_error) + .and_then(|response| { + sha3(&mut io::BufReader::new(response)).map_err(errors::from_fetch_error) + }) + .map(Into::into) + })) } fn upgrade_ready(&self) -> Result, Error> { diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index bd3f1ec3f..301f086e2 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -24,8 +24,8 @@ use ethcore::account_provider::AccountProvider; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; +use futures::{self, BoxFuture, Future}; use jsonrpc_core::Error; -use jsonrpc_macros::Ready; use v1::helpers::{ errors, dispatch, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, SignerService @@ -164,17 +164,21 @@ impl ParitySigning for SigningQueueClient where res } - fn decrypt_message(&self, ready: Ready, address: RpcH160, data: RpcBytes) { + fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { let res = self.active() .and_then(|_| self.dispatch(RpcConfirmationPayload::Decrypt((address, data).into()))); + + let (ready, p) = futures::oneshot(); // TODO [todr] typed handle_dispatch self.handle_dispatch(res, |response| { match response { - Ok(RpcConfirmationResponse::Decrypt(data)) => ready.ready(Ok(data)), - Err(e) => ready.ready(Err(e)), - e => ready.ready(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::Decrypt(data)) => ready.complete(Ok(data)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), } }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() } } @@ -182,37 +186,49 @@ impl EthSigning for SigningQueueClient where C: MiningBlockChainClient, M: MinerService, { - fn sign(&self, ready: Ready, address: RpcH160, data: RpcBytes) { + fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { let hash = data.0.sha3().into(); let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::Signature((address, hash).into()))); + + let (ready, p) = futures::oneshot(); self.handle_dispatch(res, |response| { match response { - Ok(RpcConfirmationResponse::Signature(signature)) => ready.ready(Ok(signature)), - Err(e) => ready.ready(Err(e)), - e => ready.ready(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::Signature(signature)) => ready.complete(Ok(signature)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), } }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() } - fn send_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn send_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SendTransaction(request))); + + let (ready, p) = futures::oneshot(); self.handle_dispatch(res, |response| { match response { - Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.ready(Ok(hash)), - Err(e) => ready.ready(Err(e)), - e => ready.ready(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.complete(Ok(hash)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), } }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() } - fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn sign_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request))); + + let (ready, p) = futures::oneshot(); self.handle_dispatch(res, |response| { match response { - Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.ready(Ok(tx)), - Err(e) => ready.ready(Err(e)), - e => ready.ready(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.complete(Ok(tx)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), } }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() } } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index d64281bc9..41ef79998 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -23,8 +23,8 @@ use ethcore::account_provider::AccountProvider; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; +use futures::{self, BoxFuture, Future}; use jsonrpc_core::Error; -use jsonrpc_macros::Ready; use v1::helpers::errors; use v1::helpers::dispatch; use v1::traits::{EthSigning, ParitySigning}; @@ -85,32 +85,35 @@ impl EthSigning for SigningUnsafeClient where C: MiningBlockChainClient, M: MinerService, { - fn sign(&self, ready: Ready, address: RpcH160, data: RpcBytes) { + fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { let hash = data.0.sha3().into(); let result = match self.handle(RpcConfirmationPayload::Signature((address, hash).into())) { Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), Err(e) => Err(e), e => Err(errors::internal("Unexpected result", e)), }; - ready.ready(result); + + futures::done(result).boxed() } - fn send_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn send_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { let result = match self.handle(RpcConfirmationPayload::SendTransaction(request)) { Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), Err(e) => Err(e), e => Err(errors::internal("Unexpected result", e)), }; - ready.ready(result); + + futures::done(result).boxed() } - fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn sign_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) { Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), Err(e) => Err(e), e => Err(errors::internal("Unexpected result", e)), }; - ready.ready(result); + + futures::done(result).boxed() } } @@ -118,13 +121,14 @@ impl ParitySigning for SigningUnsafeClient where C: MiningBlockChainClient, M: MinerService, { - fn decrypt_message(&self, ready: Ready, address: RpcH160, data: RpcBytes) { + fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { let result = match self.handle(RpcConfirmationPayload::Decrypt((address, data).into())) { Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), Err(e) => Err(e), e => Err(errors::internal("Unexpected result", e)), }; - ready.ready(result); + + futures::done(result).boxed() } fn post_sign(&self, _: RpcH160, _: RpcH256) -> Result, Error> { diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 21f81cc16..cb1fb524a 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -77,7 +77,6 @@ impl TracesClient where C: BlockChainClient, M: MinerService { impl Traces for TracesClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn filter(&self, filter: TraceFilter) -> Result, Error> { self.active()?; - let client = take_weak!(self.client); let traces = client.filter_traces(filter.into()); let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); @@ -94,7 +93,6 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: fn transaction_traces(&self, transaction_hash: H256) -> Result, Error> { self.active()?; - let client = take_weak!(self.client); let traces = client.transaction_traces(TransactionId::Hash(transaction_hash.into())); let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); diff --git a/rpc/src/v1/metadata.rs b/rpc/src/v1/metadata.rs new file mode 100644 index 000000000..322d8a479 --- /dev/null +++ b/rpc/src/v1/metadata.rs @@ -0,0 +1,50 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +use jsonrpc_core; + +/// RPC methods metadata. +#[derive(Clone, Default, Debug, PartialEq)] +pub struct Metadata { + /// Current dapplication identifier + pub dapp_id: Option, + /// Request origin + pub origin: Origin, +} + +/// RPC request origin +#[derive(Clone, Debug, PartialEq)] +pub enum Origin { + /// RPC server + Rpc, + /// Dapps server + Dapps, + /// IPC server + Ipc, + /// Signer + Signer, + /// Unknown + Unknown, +} + +impl Default for Origin { + fn default() -> Self { + Origin::Unknown + } +} + +impl jsonrpc_core::Metadata for Metadata {} + diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 2942d81ed..96563d412 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -21,6 +21,7 @@ #[macro_use] mod helpers; mod impls; +mod metadata; pub mod traits; pub mod tests; @@ -29,3 +30,4 @@ pub mod types; pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, Signer, Personal, Traces, Rpc}; pub use self::impls::*; pub use self::helpers::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, block_import}; +pub use self::metadata::{Metadata, Origin}; diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 428cae3e0..154c700f9 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -18,6 +18,7 @@ use std::sync::Arc; use std::time::Duration; +use devtools::RandomTempPath; use ethcore::client::{BlockChainClient, Client, ClientConfig}; use ethcore::ids::BlockId; use ethcore::spec::{Genesis, Spec}; @@ -26,18 +27,17 @@ use ethcore::views::BlockView; use ethcore::ethereum; use ethcore::miner::{MinerOptions, Banning, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, PrioritizationStrategy, GasLimit}; use ethcore::account_provider::AccountProvider; -use devtools::RandomTempPath; -use util::Hashable; -use io::IoChannel; -use util::{U256, H256, Uint, Address}; -use jsonrpc_core::{IoHandler, GenericIoHandler}; use ethjson::blockchain::BlockChain; +use io::IoChannel; +use util::{U256, H256, Uint, Address, Hashable}; +use jsonrpc_core::IoHandler; use v1::impls::{EthClient, SigningUnsafeClient}; -use v1::types::U256 as NU256; +use v1::metadata::Metadata; +use v1::tests::helpers::{TestSnapshotService, TestSyncProvider, Config}; use v1::traits::eth::Eth; use v1::traits::eth_signing::EthSigning; -use v1::tests::helpers::{TestSnapshotService, TestSyncProvider, Config}; +use v1::types::U256 as NU256; fn account_provider() -> Arc { Arc::new(AccountProvider::transient_provider()) @@ -92,7 +92,7 @@ struct EthTester { _miner: Arc, _snapshot: Arc, accounts: Arc, - handler: IoHandler, + handler: IoHandler, } impl EthTester { @@ -147,9 +147,9 @@ impl EthTester { &miner_service ); - let handler = IoHandler::new(); - handler.add_delegate(eth_client.to_delegate()); - handler.add_delegate(eth_sign.to_delegate()); + let mut handler = IoHandler::default(); + handler.extend_with(eth_client.to_delegate()); + handler.extend_with(eth_sign.to_delegate()); EthTester { _miner: miner_service, @@ -307,7 +307,7 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{ #[test] fn eth_transaction_count() { - let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".into(); + let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".parse().unwrap(); let tester = EthTester::from_spec(Spec::load(TRANSACTION_COUNT_SPEC).expect("invalid chain spec")); let address = tester.accounts.insert_account(secret, "").unwrap(); tester.accounts.unlock_account_permanently(address, "".into()).unwrap(); diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 5f4424313..32d50e90e 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -31,9 +31,10 @@ use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{ExternalMiner, MinerService}; use ethsync::SyncState; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; +use v1::metadata::Metadata; fn blockchain_client() -> Arc { let client = TestBlockChainClient::new(); @@ -66,7 +67,7 @@ struct EthTester { pub miner: Arc, pub snapshot: Arc, hashrates: Arc>>, - pub io: IoHandler, + pub io: IoHandler, } impl Default for EthTester { @@ -87,10 +88,10 @@ impl EthTester { let eth = EthClient::new(&client, &snapshot, &sync, &ap, &miner, &external_miner, options).to_delegate(); let filter = EthFilterClient::new(&client, &miner).to_delegate(); let sign = SigningUnsafeClient::new(&client, &ap, &miner).to_delegate(); - let io = IoHandler::new(); - io.add_delegate(eth); - io.add_delegate(sign); - io.add_delegate(filter); + let mut io: IoHandler = IoHandler::default(); + io.extend_with(eth); + io.extend_with(sign); + io.extend_with(filter); EthTester { client: client, @@ -373,9 +374,11 @@ fn rpc_eth_accounts() { // when we add visible address it should return that. tester.accounts_provider.set_dapps_addresses("app1".into(), vec![10.into()]).unwrap(); - let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": ["app1"], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#; - assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + let mut meta = Metadata::default(); + meta.dapp_id = Some("app1".into()); + assert_eq!((*tester.io).handle_request_sync(request, meta), Some(response.to_owned())); } #[test] @@ -561,6 +564,7 @@ fn rpc_eth_code() { fn rpc_eth_call_latest() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -596,6 +600,7 @@ fn rpc_eth_call_latest() { fn rpc_eth_call() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -631,6 +636,7 @@ fn rpc_eth_call() { fn rpc_eth_call_default_block() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -665,6 +671,7 @@ fn rpc_eth_call_default_block() { fn rpc_eth_estimate_gas() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -700,6 +707,7 @@ fn rpc_eth_estimate_gas() { fn rpc_eth_estimate_gas_default_block() { let tester = EthTester::default(); tester.client.set_execution_result(Ok(Executed { + exception: None, gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index 1c4a4c1ac..81650590c 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{Net, NetClient}; use v1::tests::helpers::{Config, TestSyncProvider}; @@ -30,8 +30,8 @@ fn sync_provider() -> Arc { fn rpc_net_version() { let sync = sync_provider(); let net = NetClient::new(&sync).to_delegate(); - let io = IoHandler::new(); - io.add_delegate(net); + let mut io = IoHandler::new(); + io.extend_with(net); let request = r#"{"jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"3","id":1}"#; @@ -43,8 +43,8 @@ fn rpc_net_version() { fn rpc_net_peer_count() { let sync = sync_provider(); let net = NetClient::new(&sync).to_delegate(); - let io = IoHandler::new(); - io.add_delegate(net); + let mut io = IoHandler::new(); + io.extend_with(net); let request = r#"{"jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"0x78","id":1}"#; @@ -56,8 +56,8 @@ fn rpc_net_peer_count() { fn rpc_net_listening() { let sync = sync_provider(); let net = NetClient::new(&sync).to_delegate(); - let io = IoHandler::new(); - io.add_delegate(net); + let mut io = IoHandler::new(); + io.extend_with(net); let request = r#"{"jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 0fae89c1e..eb1f3e3eb 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -23,7 +23,7 @@ use ethcore::client::{TestBlockChainClient}; use ethcore::miner::LocalTransactionStatus; use ethstore::ethkey::{Generator, Random}; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{Parity, ParityClient}; use v1::helpers::{SignerService, NetworkSettings}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestUpdater}; @@ -87,14 +87,14 @@ impl Dependencies { } fn default_client(&self) -> IoHandler { - let io = IoHandler::new(); - io.add_delegate(self.client(None).to_delegate()); + let mut io = IoHandler::default(); + io.extend_with(self.client(None).to_delegate()); io } fn with_signer(&self, signer: SignerService) -> IoHandler { - let io = IoHandler::new(); - io.add_delegate(self.client(Some(Arc::new(signer))).to_delegate()); + let mut io = IoHandler::default(); + io.extend_with(self.client(Some(Arc::new(signer))).to_delegate()); io } } diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index 0c8443dde..4b031c9f9 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use ethcore::account_provider::AccountProvider; use ethcore::client::TestBlockChainClient; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{ParityAccounts, ParityAccountsClient}; struct ParityAccountsTester { @@ -44,8 +44,8 @@ fn setup() -> ParityAccountsTester { let client = blockchain_client(); let parity_accounts = ParityAccountsClient::new(&accounts, &client); - let io = IoHandler::new(); - io.add_delegate(parity_accounts.to_delegate()); + let mut io = IoHandler::default(); + io.extend_with(parity_accounts.to_delegate()); let tester = ParityAccountsTester { accounts: accounts, diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index e36b69058..f118c35c6 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -22,9 +22,8 @@ use util::{U256, Address}; use ethcore::miner::MinerService; use ethcore::client::TestBlockChainClient; use ethsync::ManageNetwork; -use parity_reactor::Remote; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{ParitySet, ParitySetClient}; use v1::tests::helpers::{TestMinerService, TestFetch, TestUpdater}; use super::manage_network::TestManageNetwork; @@ -48,7 +47,7 @@ fn updater_service() -> Arc { pub type TestParitySetClient = ParitySetClient; fn parity_set_client(client: &Arc, miner: &Arc, updater: &Arc, net: &Arc) -> TestParitySetClient { - ParitySetClient::new(client, miner, updater, &(net.clone() as Arc), TestFetch::default(), Remote::new_sync()) + ParitySetClient::new(client, miner, updater, &(net.clone() as Arc), TestFetch::default()) } #[test] @@ -57,8 +56,8 @@ fn rpc_parity_execute_upgrade() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_executeUpgrade", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -75,8 +74,8 @@ fn rpc_parity_upgrade_ready() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_upgradeReady", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"beta","version":{"major":1,"minor":5,"patch":1}}},"id":1}"#; @@ -95,8 +94,9 @@ fn rpc_parity_set_min_gas_price() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -111,8 +111,9 @@ fn rpc_parity_set_gas_floor_target() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -127,8 +128,9 @@ fn rpc_parity_set_extra_data() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -143,8 +145,8 @@ fn rpc_parity_set_author() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -159,8 +161,8 @@ fn rpc_parity_set_engine_signer() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setEngineSigner", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681", "password"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -177,8 +179,8 @@ fn rpc_parity_set_transactions_limit() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_setTransactionsLimit", "params":[10240240], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -193,8 +195,8 @@ fn rpc_parity_set_hash_content() { let client = client_service(); let network = network_service(); let updater = updater_service(); - let io = IoHandler::new(); - io.add_delegate(parity_set_client(&client, &miner, &updater, &network).to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_hashContent", "params":["https://ethcore.io/assets/images/ethcore-black-horizontal.png"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e","id":1}"#; diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index c2984240f..8ee936934 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use std::str::FromStr; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use util::{U256, Uint, Address}; use ethcore::account_provider::AccountProvider; use v1::{PersonalClient, Personal}; @@ -52,8 +52,8 @@ fn setup() -> PersonalTester { let miner = miner_service(); let personal = PersonalClient::new(&accounts, &client, &miner, false); - let io = IoHandler::new(); - io.add_delegate(personal.to_delegate()); + let mut io = IoHandler::default(); + io.extend_with(personal.to_delegate()); let tester = PersonalTester { accounts: accounts, diff --git a/rpc/src/v1/tests/mocked/rpc.rs b/rpc/src/v1/tests/mocked/rpc.rs index 52ba78859..b1e20ebdf 100644 --- a/rpc/src/v1/tests/mocked/rpc.rs +++ b/rpc/src/v1/tests/mocked/rpc.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{Rpc, RpcClient}; @@ -30,8 +30,8 @@ fn rpc_client() -> RpcClient { #[test] fn modules() { let rpc = rpc_client().to_delegate(); - let io = IoHandler::new(); - io.add_delegate(rpc); + let mut io = IoHandler::new(); + io.extend_with(rpc); let request = r#"{"jsonrpc": "2.0", "method": "modules", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":{"rpc":"1.0","web3":"1.0"},"id":1}"#; @@ -42,8 +42,8 @@ fn modules() { #[test] fn rpc_modules() { let rpc = rpc_client().to_delegate(); - let io = IoHandler::new(); - io.add_delegate(rpc); + let mut io = IoHandler::new(); + io.extend_with(rpc); let request = r#"{"jsonrpc": "2.0", "method": "rpc_modules", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":{"ethcore":"1.0","rpc":"1.0","web3":"1.0"},"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index 4001dd996..5ed231db4 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -24,7 +24,7 @@ use ethcore::transaction::{Transaction, Action}; use rlp::encode; use serde_json; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::{SignerClient, Signer}; use v1::tests::helpers::TestMinerService; use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload}; @@ -58,8 +58,8 @@ fn signer_tester() -> SignerTester { let client = blockchain_client(); let miner = miner_service(); - let io = IoHandler::new(); - io.add_delegate(SignerClient::new(&accounts, &client, &miner, &signer).to_delegate()); + let mut io = IoHandler::default(); + io.extend_with(SignerClient::new(&accounts, &client, &miner, &signer).to_delegate()); SignerTester { signer: signer, diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 4040b57b1..e6a27f832 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -15,10 +15,10 @@ // along with Parity. If not, see . use std::str::FromStr; -use std::sync::{mpsc, Arc}; +use std::sync::Arc; use rlp; -use jsonrpc_core::{IoHandler, Success, GenericIoHandler}; +use jsonrpc_core::{IoHandler, Success}; use v1::impls::SigningQueueClient; use v1::traits::{EthSigning, ParitySigning, Parity}; use v1::helpers::{SignerService, SigningQueue}; @@ -31,6 +31,7 @@ use ethcore::account_provider::AccountProvider; use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Transaction, Action}; use ethstore::ethkey::{Generator, Random}; +use futures::Future; use serde_json; struct SigningTester { @@ -47,11 +48,11 @@ impl Default for SigningTester { let client = Arc::new(TestBlockChainClient::default()); let miner = Arc::new(TestMinerService::default()); let accounts = Arc::new(AccountProvider::transient_provider()); - let io = IoHandler::new(); + let mut io = IoHandler::default(); let rpc = SigningQueueClient::new(&signer, &client, &miner, &accounts); - io.add_delegate(EthSigning::to_delegate(rpc)); + io.extend_with(EthSigning::to_delegate(rpc)); let rpc = SigningQueueClient::new(&signer, &client, &miner, &accounts); - io.add_delegate(ParitySigning::to_delegate(rpc)); + io.extend_with(ParitySigning::to_delegate(rpc)); SigningTester { signer: signer, @@ -87,15 +88,12 @@ fn should_add_sign_to_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#; // then - let (tx, rx) = mpsc::channel(); - tester.io.handle_request(&request, move |response| { - tx.send(response).unwrap(); - }); + let promise = tester.io.handle_request(&request); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(0.into()))); - let res = rx.try_recv().unwrap(); + let res = promise.wait().unwrap(); assert_eq!(res, Some(response.to_owned())); } @@ -230,15 +228,12 @@ fn should_add_transaction_to_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#; // then - let (tx, rx) = mpsc::channel(); - tester.io.handle_request(&request, move |response| { - tx.send(response).unwrap(); - }); + let promise = tester.io.handle_request(&request); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SendTransaction(0.into()))); - let res = rx.try_recv().unwrap(); + let res = promise.wait().unwrap(); assert_eq!(res, Some(response.to_owned())); } @@ -299,16 +294,13 @@ fn should_add_sign_transaction_to_the_queue() { r#"}},"id":1}"#; // then - let (tx, rx) = mpsc::channel(); tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); - tester.io.handle_request(&request, move |response| { - tx.send(response).unwrap(); - }); + let promise = tester.io.handle_request(&request); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(t.into()))); - let res = rx.try_recv().unwrap(); + let res = promise.wait().unwrap(); assert_eq!(res, Some(response.to_owned())); } @@ -352,9 +344,9 @@ fn should_dispatch_transaction_if_account_is_unlock() { #[test] fn should_decrypt_message_if_account_is_unlocked() { // given - let tester = eth_signing(); + let mut tester = eth_signing(); let parity = parity::Dependencies::new(); - tester.io.add_delegate(parity.client(None).to_delegate()); + tester.io.extend_with(parity.client(None).to_delegate()); let (address, public) = tester.accounts.new_account_and_public("test").unwrap(); tester.accounts.unlock_account_permanently(address, "test".into()).unwrap(); @@ -400,14 +392,11 @@ fn should_add_decryption_to_the_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#; // then - let (tx, rx) = mpsc::channel(); - tester.io.handle_request(&request, move |response| { - tx.send(response).unwrap(); - }); + let promise = tester.io.handle_request(&request); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); - let res = rx.try_recv().unwrap(); + let res = promise.wait().unwrap(); assert_eq!(res, Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index f9a9baa00..864329336 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -21,7 +21,7 @@ use ethcore::trace::trace::{Action, Res, Call}; use ethcore::trace::LocalizedTrace; use ethcore::client::{TestBlockChainClient}; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use v1::tests::helpers::{TestMinerService}; use v1::{Traces, TracesClient}; @@ -51,6 +51,7 @@ fn io() -> Tester { block_hash: 10.into(), }]); *client.execution_result.write() = Some(Ok(Executed { + exception: None, gas: 20_000.into(), gas_used: 10_000.into(), refunded: 0.into(), @@ -64,8 +65,8 @@ fn io() -> Tester { })); let miner = Arc::new(TestMinerService::default()); let traces = TracesClient::new(&client, &miner); - let io = IoHandler::new(); - io.add_delegate(traces.to_delegate()); + let mut io = IoHandler::new(); + io.extend_with(traces.to_delegate()); Tester { _client: client, diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index c7ad42426..953dab22d 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::IoHandler; use util::version; use v1::{Web3, Web3Client}; #[test] fn rpc_web3_version() { let web3 = Web3Client::new().to_delegate(); - let io = IoHandler::new(); - io.add_delegate(web3); + let mut io = IoHandler::new(); + io.extend_with(web3); let v = version().to_owned().replace("Parity/", "Parity//"); @@ -35,8 +35,8 @@ fn rpc_web3_version() { #[test] fn rpc_web3_sha3() { let web3 = Web3Client::new().to_delegate(); - let io = IoHandler::new(); - io.add_delegate(web3); + let mut io = IoHandler::new(); + io.extend_with(web3); let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x00"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a","id":1}"#; @@ -47,8 +47,8 @@ fn rpc_web3_sha3() { #[test] fn rpc_web3_sha3_wiki() { let web3 = Web3Client::new().to_delegate(); - let io = IoHandler::new(); - io.add_delegate(web3); + let mut io = IoHandler::new(); + io.extend_with(web3); let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f20776f726c64"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad","id":1}"#; diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 2d52b7c70..fa63858db 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -16,16 +16,19 @@ //! Eth rpc interface. use jsonrpc_core::Error; +use jsonrpc_macros::Trailing; -use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, DappId}; +use futures::BoxFuture; + +use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index}; use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; use v1::types::{H64, H160, H256, U256}; -use jsonrpc_macros::Trailing; - build_rpc_trait! { /// Eth rpc interface. pub trait Eth { + type Metadata; + /// Returns protocol version encoded as a string (quotes are necessary). #[rpc(name = "eth_protocolVersion")] fn protocol_version(&self) -> Result; @@ -51,8 +54,8 @@ build_rpc_trait! { fn gas_price(&self) -> Result; /// Returns accounts list. - #[rpc(name = "eth_accounts")] - fn accounts(&self, Trailing) -> Result, Error>; + #[rpc(meta, name = "eth_accounts")] + fn accounts(&self, Self::Metadata) -> BoxFuture, Error>; /// Returns highest block number. #[rpc(name = "eth_blockNumber")] diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index 442883339..bc16e9e04 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -16,7 +16,8 @@ //! Eth rpc interface. -use jsonrpc_macros::Ready; +use jsonrpc_core::Error; +use futures::BoxFuture; use v1::types::{Bytes, H160, H256, H520, TransactionRequest, RichRawTransaction}; @@ -25,18 +26,18 @@ build_rpc_trait! { pub trait EthSigning { /// Signs the hash of data with given address signature. #[rpc(async, name = "eth_sign")] - fn sign(&self, Ready, H160, Bytes); + fn sign(&self, H160, Bytes) -> BoxFuture; /// Sends transaction; will block waiting for signer to return the /// transaction hash. /// If Signer is disable it will require the account to be unlocked. #[rpc(async, name = "eth_sendTransaction")] - fn send_transaction(&self, Ready, TransactionRequest); + fn send_transaction(&self, TransactionRequest) -> BoxFuture; /// Signs transactions without dispatching it to the network. /// Returns signed transaction RLP representation and the transaction itself. /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. #[rpc(async, name = "eth_signTransaction")] - fn sign_transaction(&self, Ready, TransactionRequest); + fn sign_transaction(&self, TransactionRequest) -> BoxFuture; } } diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index f3e83bb55..197be5471 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -17,7 +17,7 @@ //! Parity-specific rpc interface for operations altering the settings. use jsonrpc_core::Error; -use jsonrpc_macros::Ready; +use futures::BoxFuture; use v1::types::{Bytes, H160, H256, U256, ReleaseInfo}; @@ -90,7 +90,7 @@ build_rpc_trait! { /// Hash a file content under given URL. #[rpc(async, name = "parity_hashContent")] - fn hash_content(&self, Ready, String); + fn hash_content(&self, String) -> BoxFuture; /// Is there a release ready for install? #[rpc(name = "parity_upgradeReady")] diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index a6fdbe2cd..9b21fee17 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -16,7 +16,7 @@ //! ParitySigning rpc interface. use jsonrpc_core::Error; -use jsonrpc_macros::Ready; +use futures::BoxFuture; use v1::types::{U256, H160, H256, Bytes, ConfirmationResponse, TransactionRequest, Either}; @@ -41,6 +41,6 @@ build_rpc_trait! { /// Decrypt some ECIES-encrypted message. /// First parameter is the address with which it is encrypted, second is the ciphertext. #[rpc(async, name = "parity_decryptMessage")] - fn decrypt_message(&self, Ready, H160, Bytes); + fn decrypt_message(&self, H160, Bytes) -> BoxFuture; } } diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs index cc4532e7c..978c3a0bf 100644 --- a/rpc/src/v1/types/hash.rs +++ b/rpc/src/v1/types/hash.rs @@ -25,7 +25,7 @@ use util::{H64 as Eth64, H160 as Eth160, H256 as Eth256, H520 as Eth520, H512 as macro_rules! impl_hash { ($name: ident, $other: ident, $size: expr) => { /// Hash serialization - pub struct $name([u8; $size]); + pub struct $name(pub [u8; $size]); impl Eq for $name { } diff --git a/signer/src/lib.rs b/signer/src/lib.rs index 196fb4fae..9797483a5 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -29,16 +29,22 @@ //! the transaction for you. //! //! ``` +//! extern crate jsonrpc_core; //! extern crate ethcore_signer; //! extern crate ethcore_rpc; //! //! use std::sync::Arc; +//! use jsonrpc_core::IoHandler; +//! use jsonrpc_core::reactor::RpcEventLoop; //! use ethcore_signer::ServerBuilder; //! use ethcore_rpc::ConfirmationsQueue; //! //! fn main() { //! let queue = Arc::new(ConfirmationsQueue::default()); -//! let _server = ServerBuilder::new(queue, "/tmp/authcodes".into()).start("127.0.0.1:8084".parse().unwrap()); +//! let io = Arc::new(IoHandler::new().into()); +//! let event_loop = RpcEventLoop::spawn(); +//! let _server = ServerBuilder::new(queue, "/tmp/authcodes".into()) +//! .start("127.0.0.1:8084".parse().unwrap(), event_loop.handler(io)); //! } //! ``` @@ -48,8 +54,8 @@ extern crate env_logger; extern crate rand; extern crate ethcore_util as util; -extern crate ethcore_io as io; extern crate ethcore_rpc as rpc; +extern crate ethcore_io as io; extern crate jsonrpc_core; extern crate ws; diff --git a/signer/src/tests/mod.rs b/signer/src/tests/mod.rs index ab46a0e6f..3139aaf99 100644 --- a/signer/src/tests/mod.rs +++ b/signer/src/tests/mod.rs @@ -21,6 +21,8 @@ use devtools::http_client; use devtools::RandomTempPath; use rpc::ConfirmationsQueue; +use jsonrpc_core::IoHandler; +use jsonrpc_core::reactor::RpcEventLoop; use rand; use ServerBuilder; @@ -45,14 +47,36 @@ impl DerefMut for GuardedAuthCodes { } } -/// Setup a mock signer for testsp -pub fn serve() -> (Server, usize, GuardedAuthCodes) { +/// Server with event loop +pub struct ServerLoop { + /// Signer Server + pub server: Server, + /// RPC Event Loop + pub event_loop: RpcEventLoop, +} + +impl Deref for ServerLoop { + type Target = Server; + + fn deref(&self) -> &Self::Target { + &self.server + } +} + +/// Setup a mock signer for tests +pub fn serve() -> (ServerLoop, usize, GuardedAuthCodes) { let mut path = RandomTempPath::new(); path.panic_on_drop_failure = false; let queue = Arc::new(ConfirmationsQueue::default()); let builder = ServerBuilder::new(queue, path.to_path_buf()); let port = 35000 + rand::random::() % 10000; - let res = builder.start(format!("127.0.0.1:{}", port).parse().unwrap()).unwrap(); + let event_loop = RpcEventLoop::spawn(); + let handler = event_loop.handler(Arc::new(IoHandler::default().into())); + let server = builder.start(format!("127.0.0.1:{}", port).parse().unwrap(), handler).unwrap(); + let res = ServerLoop { + server: server, + event_loop: event_loop, + }; (res, port, GuardedAuthCodes { authcodes: AuthCodes::from_file(&path).unwrap(), @@ -61,8 +85,8 @@ pub fn serve() -> (Server, usize, GuardedAuthCodes) { } /// Test a single request to running server -pub fn request(server: Server, request: &str) -> http_client::Response { - http_client::request(server.addr(), request) +pub fn request(server: ServerLoop, request: &str) -> http_client::Response { + http_client::request(server.server.addr(), request) } #[cfg(test)] diff --git a/signer/src/ws_server/mod.rs b/signer/src/ws_server/mod.rs index 83b0e23e6..e73f9dd6d 100644 --- a/signer/src/ws_server/mod.rs +++ b/signer/src/ws_server/mod.rs @@ -25,8 +25,9 @@ use std::ops::Drop; use std::sync::Arc; use std::net::SocketAddr; use io::{PanicHandler, OnPanicListener, MayPanic}; -use jsonrpc_core::{IoHandler, IoDelegate}; -use rpc::{Extendable, ConfirmationsQueue}; +use jsonrpc_core::Metadata; +use jsonrpc_core::reactor::RpcHandler; +use rpc::ConfirmationsQueue; mod session; @@ -51,23 +52,15 @@ impl From for ServerError { /// Builder for `WebSockets` server pub struct ServerBuilder { queue: Arc, - handler: Arc, authcodes_path: PathBuf, skip_origin_validation: bool, } -impl Extendable for ServerBuilder { - fn add_delegate(&self, delegate: IoDelegate) { - self.handler.add_delegate(delegate); - } -} - impl ServerBuilder { /// Creates new `ServerBuilder` pub fn new(queue: Arc, authcodes_path: PathBuf) -> Self { ServerBuilder { queue: queue, - handler: Arc::new(IoHandler::new()), authcodes_path: authcodes_path, skip_origin_validation: false, } @@ -82,14 +75,14 @@ impl ServerBuilder { /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. - pub fn start(self, addr: SocketAddr) -> Result { - Server::start(addr, self.handler, self.queue, self.authcodes_path, self.skip_origin_validation) + pub fn start(self, addr: SocketAddr, handler: RpcHandler) -> Result { + Server::start(addr, handler, self.queue, self.authcodes_path, self.skip_origin_validation) } } /// `WebSockets` server implementation. pub struct Server { - handle: Option>>, + handle: Option>, broadcaster_handle: Option>, queue: Arc, panic_handler: Arc, @@ -104,7 +97,7 @@ impl Server { /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. - fn start(addr: SocketAddr, handler: Arc, queue: Arc, authcodes_path: PathBuf, skip_origin_validation: bool) -> Result { + fn start(addr: SocketAddr, handler: RpcHandler, queue: Arc, authcodes_path: PathBuf, skip_origin_validation: bool) -> Result { let config = { let mut config = ws::Settings::default(); // accept only handshakes beginning with GET @@ -138,7 +131,7 @@ impl Server { )), Ok(server) => server, } - }).unwrap() + }).unwrap(); }); // Spawn a thread for broadcasting diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index 852b83f15..bf54e0159 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -21,7 +21,8 @@ use authcode_store::AuthCodes; use std::path::{PathBuf, Path}; use std::sync::Arc; use std::str::FromStr; -use jsonrpc_core::{IoHandler, GenericIoHandler}; +use jsonrpc_core::{Metadata}; +use jsonrpc_core::reactor::RpcHandler; use util::{H256, version}; #[cfg(feature = "parity-ui")] @@ -129,16 +130,16 @@ fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response { response } -pub struct Session { +pub struct Session { out: ws::Sender, skip_origin_validation: bool, self_origin: String, authcodes_path: PathBuf, - handler: Arc, + handler: RpcHandler, file_handler: Arc, } -impl ws::Handler for Session { +impl ws::Handler for Session { #[cfg_attr(feature="dev", allow(collapsible_if))] fn on_request(&mut self, req: &ws::Request) -> ws::Result<(ws::Response)> { trace!(target: "signer", "Handling request: {:?}", req); @@ -209,7 +210,10 @@ impl ws::Handler for Session { fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { let req = msg.as_text()?; let out = self.out.clone(); - self.handler.handle_request(req, move |response| { + // TODO [ToDr] Extract metadata for PubSub/Session + let metadata = Default::default(); + + self.handler.handle_request(req, metadata, move |response| { if let Some(result) = response { let res = out.send(result); if let Err(e) = res { @@ -221,16 +225,16 @@ impl ws::Handler for Session { } } -pub struct Factory { - handler: Arc, +pub struct Factory { + handler: RpcHandler, skip_origin_validation: bool, self_origin: String, authcodes_path: PathBuf, file_handler: Arc, } -impl Factory { - pub fn new(handler: Arc, self_origin: String, authcodes_path: PathBuf, skip_origin_validation: bool) -> Self { +impl Factory { + pub fn new(handler: RpcHandler, self_origin: String, authcodes_path: PathBuf, skip_origin_validation: bool) -> Self { Factory { handler: handler, skip_origin_validation: skip_origin_validation, @@ -241,8 +245,8 @@ impl Factory { } } -impl ws::Factory for Factory { - type Handler = Session; +impl ws::Factory for Factory { + type Handler = Session; fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { Session { diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml index 3b467b8f3..412d08904 100644 --- a/stratum/Cargo.toml +++ b/stratum/Cargo.toml @@ -12,6 +12,7 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } [dependencies] log = "0.3" jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-macros = { git = "https://github.com/ethcore/jsonrpc.git" } jsonrpc-tcp-server = { git = "https://github.com/ethcore/jsonrpc.git" } mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" } ethcore-util = { path = "../util" } diff --git a/stratum/src/lib.rs b/stratum/src/lib.rs index bb0ca7b22..1ff0100b3 100644 --- a/stratum/src/lib.rs +++ b/stratum/src/lib.rs @@ -18,6 +18,7 @@ extern crate jsonrpc_tcp_server; extern crate jsonrpc_core; +extern crate jsonrpc_macros; #[macro_use] extern crate log; extern crate ethcore_util as util; extern crate ethcore_ipc as ipc; @@ -45,16 +46,33 @@ pub use traits::{ }; use jsonrpc_tcp_server::Server as JsonRpcServer; -use jsonrpc_core::{IoHandler, Params, IoDelegate, to_value, from_params}; +use jsonrpc_core::{IoHandler, Params, to_value}; +use jsonrpc_macros::IoDelegate; use std::sync::Arc; use std::net::SocketAddr; use std::collections::{HashSet, HashMap}; use util::{H256, Hashable, RwLock, RwLockReadGuard}; +type RpcResult = Result; + +struct StratumRpc { + stratum: RwLock>>, +} +impl StratumRpc { + fn subscribe(&self, params: Params) -> RpcResult { + self.stratum.read().as_ref().expect("RPC methods are called after stratum is set.") + .subscribe(params) + } + + fn authorize(&self, params: Params) -> RpcResult { + self.stratum.read().as_ref().expect("RPC methods are called after stratum is set.") + .authorize(params) + } +} + pub struct Stratum { - rpc_server: JsonRpcServer, - handler: Arc, + rpc_server: JsonRpcServer<()>, /// Subscribed clients subscribers: RwLock>, /// List of workers supposed to receive job update @@ -73,29 +91,32 @@ impl Stratum { dispatcher: Arc, secret: Option, ) -> Result, jsonrpc_tcp_server::Error> { - let handler = Arc::new(IoHandler::new()); - let server = JsonRpcServer::new(addr, &handler)?; + let rpc = Arc::new(StratumRpc { + stratum: RwLock::new(None), + }); + let mut delegate = IoDelegate::::new(rpc.clone()); + delegate.add_method("miner.subscribe", StratumRpc::subscribe); + delegate.add_method("miner.authorize", StratumRpc::authorize); + + let mut handler = IoHandler::default(); + handler.extend_with(delegate); + let server = JsonRpcServer::new(addr, handler)?; let stratum = Arc::new(Stratum { rpc_server: server, - handler: handler, subscribers: RwLock::new(Vec::new()), job_que: RwLock::new(HashSet::new()), dispatcher: dispatcher, workers: Arc::new(RwLock::new(HashMap::new())), secret: secret, }); - - let mut delegate = IoDelegate::::new(stratum.clone()); - delegate.add_method("miner.subscribe", Stratum::subscribe); - delegate.add_method("miner.authorize", Stratum::authorize); - stratum.handler.add_delegate(delegate); + *rpc.stratum.write() = Some(stratum.clone()); stratum.rpc_server.run_async()?; Ok(stratum) } - fn subscribe(&self, _params: Params) -> std::result::Result { + fn subscribe(&self, _params: Params) -> RpcResult { use std::str::FromStr; if let Some(context) = self.rpc_server.request_context() { @@ -115,8 +136,8 @@ impl Stratum { }) } - fn authorize(&self, params: Params) -> std::result::Result { - from_params::<(String, String)>(params).map(|(worker_id, secret)|{ + fn authorize(&self, params: Params) -> RpcResult { + params.parse::<(String, String)>().map(|(worker_id, secret)|{ if let Some(valid_secret) = self.secret { let hash = secret.sha3(); if hash != valid_secret { diff --git a/sync/src/api.rs b/sync/src/api.rs index 6c2c43db4..1aa8213bd 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -23,6 +23,7 @@ use network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId, Pr AllowIP as NetworkAllowIP}; use util::{U256, H256, H512}; use io::{TimerToken}; +use ethcore::ethstore::ethkey::Secret; use ethcore::client::{BlockChainClient, ChainNotify}; use ethcore::snapshot::SnapshotService; use ethcore::header::BlockNumber; @@ -178,7 +179,7 @@ impl EthSync { }; let mut light_proto = LightProtocol::new(params.provider, light_params); - light_proto.add_handler(Box::new(TxRelay(params.chain.clone()))); + light_proto.add_handler(Arc::new(TxRelay(params.chain.clone()))); Arc::new(light_proto) }) @@ -476,7 +477,7 @@ pub struct NetworkConfiguration { /// List of initial node addresses pub boot_nodes: Vec, /// Use provided node key instead of default - pub use_secret: Option, + pub use_secret: Option, /// Max number of connected peers to maintain pub max_peers: u32, /// Min number of connected peers to maintain @@ -611,7 +612,7 @@ impl LightSync { let mut light_proto = LightProtocol::new(params.client.clone(), light_params); let sync_handler = try!(SyncHandler::new(params.client.clone())); - light_proto.add_handler(Box::new(sync_handler)); + light_proto.add_handler(Arc::new(sync_handler)); Arc::new(light_proto) }; @@ -667,3 +668,4 @@ impl ManageNetwork for LightSync { NetworkConfiguration::from(self.network.config().clone()) } } + diff --git a/sync/src/light_sync/mod.rs b/sync/src/light_sync/mod.rs index 226b1fdff..de7d7b05a 100644 --- a/sync/src/light_sync/mod.rs +++ b/sync/src/light_sync/mod.rs @@ -23,6 +23,14 @@ //! //! This is written assuming that the client and sync service are running //! in the same binary; unlike a full node which might communicate via IPC. +//! +//! +//! Sync strategy: +//! - Find a common ancestor with peers. +//! - Split the chain up into subchains, which are downloaded in parallel from various peers in rounds. +//! - When within a certain distance of the head of the chain, aggressively download all +//! announced blocks. +//! - On bad block/response, punish peer and reset. use std::collections::HashMap; use std::mem; @@ -43,6 +51,9 @@ use self::sync_round::{AbortReason, SyncRound, ResponseContext}; mod response; mod sync_round; +#[cfg(test)] +mod tests; + /// Peer chain info. #[derive(Clone)] struct ChainInfo { @@ -64,6 +75,7 @@ impl Peer { } } // search for a common ancestor with the best chain. +#[derive(Debug)] enum AncestorSearch { Queued(u64), // queued to search for blocks starting from here. Awaiting(ReqId, u64, request::Headers), // awaiting response for this request. @@ -125,6 +137,9 @@ impl AncestorSearch { match self { AncestorSearch::Queued(start) => { + trace!(target: "sync", "Requesting {} reverse headers from {} to find common ancestor", + BATCH_SIZE, start); + let req = request::Headers { start: start.into(), max: ::std::cmp::min(start as usize, BATCH_SIZE), @@ -143,8 +158,9 @@ impl AncestorSearch { } // synchronization state machine. +#[derive(Debug)] enum SyncState { - // Idle (waiting for peers) + // Idle (waiting for peers) or at chain head. Idle, // searching for common ancestor with best chain. // queue should be cleared at this phase. @@ -328,19 +344,19 @@ impl LightSync { return; } - trace!(target: "sync", "Beginning search for common ancestor"); - self.client.clear_queue(); + self.client.flush_queue(); let chain_info = self.client.chain_info(); + trace!(target: "sync", "Beginning search for common ancestor from {:?}", + (chain_info.best_block_number, chain_info.best_block_hash)); *state = SyncState::AncestorSearch(AncestorSearch::begin(chain_info.best_block_number)); } fn maintain_sync(&self, ctx: &BasicContext) { const DRAIN_AMOUNT: usize = 128; - debug!(target: "sync", "Maintaining sync."); - let mut state = self.state.lock(); + debug!(target: "sync", "Maintaining sync ({:?})", &*state); // drain any pending blocks into the queue. { @@ -358,6 +374,7 @@ impl LightSync { }; if sink.is_empty() { break } + trace!(target: "sync", "Drained {} headers to import", sink.len()); for header in sink.drain(..) { if let Err(e) = self.client.queue_header(header) { @@ -372,8 +389,12 @@ impl LightSync { // handle state transitions. { + let chain_info = self.client.chain_info(); + let best_td = chain_info.total_difficulty; match mem::replace(&mut *state, SyncState::Idle) { - SyncState::Rounds(SyncRound::Abort(reason)) => { + _ if self.best_seen.lock().as_ref().map_or(true, |&(_, td)| best_td >= td) + => *state = SyncState::Idle, + SyncState::Rounds(SyncRound::Abort(reason, _)) => { match reason { AbortReason::BadScaffold(bad_peers) => { debug!(target: "sync", "Disabling peers responsible for bad scaffold"); @@ -394,7 +415,7 @@ impl LightSync { } SyncState::AncestorSearch(AncestorSearch::Genesis) => { // Same here. - let g_hash = self.client.chain_info().genesis_hash; + let g_hash = chain_info.genesis_hash; *state = SyncState::Rounds(SyncRound::begin(0, g_hash)); } SyncState::Idle => self.begin_search(&mut state), diff --git a/sync/src/light_sync/sync_round.rs b/sync/src/light_sync/sync_round.rs index dc1927aae..29d93daa8 100644 --- a/sync/src/light_sync/sync_round.rs +++ b/sync/src/light_sync/sync_round.rs @@ -18,6 +18,7 @@ use std::cmp::Ordering; use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; +use std::fmt; use ethcore::header::Header; @@ -29,9 +30,9 @@ use util::{Bytes, H256}; use super::response; -// amount of blocks between each scaffold entry. +/// amount of blocks between each scaffold entry. // TODO: move these into parameters for `RoundStart::new`? -const ROUND_SKIP: u64 = 255; +pub const ROUND_SKIP: u64 = 255; // amount of scaffold frames: these are the blank spaces in "X___X___X" const ROUND_FRAMES: usize = 255; @@ -132,7 +133,7 @@ impl Fetcher { let end = match sparse_headers.last().map(|h| (h.number(), h.hash())) { Some(end) => end, - None => return SyncRound::abort(AbortReason::BadScaffold(contributors)), + None => return SyncRound::abort(AbortReason::BadScaffold(contributors), VecDeque::new()), }; SyncRound::Fetch(Fetcher { @@ -217,10 +218,11 @@ impl Fetcher { let subchain_parent = request.subchain_parent.1; + // check if the subchain portion has been completely filled. if request.headers_request.max == 0 { if parent_hash.map_or(true, |hash| hash != subchain_parent) { let abort = AbortReason::BadScaffold(self.scaffold_contributors); - return SyncRound::Abort(abort); + return SyncRound::abort(abort, self.ready); } self.complete_requests.insert(subchain_parent, request); @@ -271,6 +273,7 @@ impl Fetcher { headers.extend(self.ready.drain(0..max)); if self.sparse.is_empty() && self.ready.is_empty() { + trace!(target: "sync", "sync round complete. Starting anew from {:?}", self.end); SyncRound::Start(RoundStart::new(self.end)) } else { SyncRound::Fetch(self) @@ -309,7 +312,7 @@ impl RoundStart { if self.sparse_headers.len() > 1 { Fetcher::new(self.sparse_headers, self.contributors.into_iter().collect()) } else { - SyncRound::Abort(AbortReason::NoResponses) + SyncRound::Abort(AbortReason::NoResponses, self.sparse_headers.into()) } } else { SyncRound::Start(self) @@ -375,14 +378,19 @@ impl RoundStart { let start = (self.start_block.0 + 1) + self.sparse_headers.len() as u64 * (ROUND_SKIP + 1); + let max = (ROUND_FRAMES - 1) - self.sparse_headers.len(); + let headers_request = HeadersRequest { start: start.into(), - max: (ROUND_FRAMES - 1) - self.sparse_headers.len(), + max: max, skip: ROUND_SKIP, reverse: false, }; if let Some(req_id) = dispatcher(headers_request.clone()) { + trace!(target: "sync", "Requesting scaffold: {} headers forward from {}, skip={}", + max, start, ROUND_SKIP); + self.pending_req = Some((req_id, headers_request)); } } @@ -397,15 +405,15 @@ pub enum SyncRound { Start(RoundStart), /// Fetching intermediate blocks during a sync round. Fetch(Fetcher), - /// Aborted. - Abort(AbortReason), + /// Aborted + Sequential headers + Abort(AbortReason, VecDeque
), } impl SyncRound { - fn abort(reason: AbortReason) -> Self { - trace!(target: "sync", "Aborting sync round: {:?}", reason); + fn abort(reason: AbortReason, remaining: VecDeque
) -> Self { + trace!(target: "sync", "Aborting sync round: {:?}. To drain: {:?}", reason, remaining); - SyncRound::Abort(reason) + SyncRound::Abort(reason, remaining) } /// Begin sync rounds from a starting block. @@ -450,7 +458,23 @@ impl SyncRound { pub fn drain(self, v: &mut Vec
, max: Option) -> Self { match self { SyncRound::Fetch(fetcher) => fetcher.drain(v, max), + SyncRound::Abort(reason, mut remaining) => { + let len = ::std::cmp::min(max.unwrap_or(usize::max_value()), remaining.len()); + v.extend(remaining.drain(..len)); + SyncRound::Abort(reason, remaining) + } other => other, } } } + +impl fmt::Debug for SyncRound { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SyncRound::Start(ref state) => write!(f, "Scaffolding from {:?}", state.start_block), + SyncRound::Fetch(ref fetcher) => write!(f, "Filling scaffold up to {:?}", fetcher.end), + SyncRound::Abort(ref reason, ref remaining) => + write!(f, "Aborted: {:?}, {} remain", reason, remaining.len()), + } + } +} diff --git a/sync/src/light_sync/tests/mod.rs b/sync/src/light_sync/tests/mod.rs new file mode 100644 index 000000000..9eefbec41 --- /dev/null +++ b/sync/src/light_sync/tests/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +#![allow(dead_code)] + +mod test_net; diff --git a/sync/src/light_sync/tests/test_net.rs b/sync/src/light_sync/tests/test_net.rs new file mode 100644 index 000000000..fa5724666 --- /dev/null +++ b/sync/src/light_sync/tests/test_net.rs @@ -0,0 +1,211 @@ +// Copyright 2015, 2016 Parity Technologies (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 . + +//! TestNet peer definition. + +use std::collections::{HashSet, VecDeque}; +use std::sync::Arc; + +use light_sync::*; +use tests::helpers::{TestNet, Peer as PeerLike, TestPacket}; + +use ethcore::client::TestBlockChainClient; +use ethcore::spec::Spec; +use io::IoChannel; +use light::client::Client as LightClient; +use light::net::{LightProtocol, IoContext, Capabilities, Params as LightParams}; +use light::net::buffer_flow::FlowParams; +use network::{NodeId, PeerId}; +use util::RwLock; + +const NETWORK_ID: u64 = 0xcafebabe; + +struct TestIoContext<'a> { + queue: &'a RwLock>, + sender: Option, + to_disconnect: RwLock>, +} + +impl<'a> IoContext for TestIoContext<'a> { + fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { + self.queue.write().push_back(TestPacket { + data: packet_body, + packet_id: packet_id, + recipient: peer, + }) + } + + fn respond(&self, packet_id: u8, packet_body: Vec) { + if let Some(sender) = self.sender { + self.send(sender, packet_id, packet_body); + } + } + + fn disconnect_peer(&self, peer: PeerId) { + self.to_disconnect.write().insert(peer); + } + + fn disable_peer(&self, peer: PeerId) { self.disconnect_peer(peer) } + fn protocol_version(&self, _peer: PeerId) -> Option { Some(::light::net::MAX_PROTOCOL_VERSION) } + + fn persistent_peer_id(&self, _peer: PeerId) -> Option { unimplemented!() } +} + +// peer-specific data. +enum PeerData { + Light(Arc>, Arc), + Full(Arc) +} + +// test peer type. +// Either a full peer or a LES peer. +pub struct Peer { + proto: LightProtocol, + queue: RwLock>, + data: PeerData, +} + +impl Peer { + // create a new full-client peer for light client peers to sync to. + // buffer flow is made negligible. + pub fn new_full(chain: Arc) -> Self { + let params = LightParams { + network_id: NETWORK_ID, + flow_params: FlowParams::free(), + capabilities: Capabilities { + serve_headers: true, + serve_chain_since: None, + serve_state_since: None, + tx_relay: true, + }, + }; + + let proto = LightProtocol::new(chain.clone(), params); + Peer { + proto: proto, + queue: RwLock::new(VecDeque::new()), + data: PeerData::Full(chain), + } + } + + // create a new light-client peer to sync to full peers. + pub fn new_light(chain: Arc) -> Self { + let sync = Arc::new(LightSync::new(chain.clone()).unwrap()); + let params = LightParams { + network_id: NETWORK_ID, + flow_params: FlowParams::default(), + capabilities: Capabilities { + serve_headers: false, + serve_chain_since: None, + serve_state_since: None, + tx_relay: false, + }, + }; + + let mut proto = LightProtocol::new(chain.clone(), params); + proto.add_handler(sync.clone()); + Peer { + proto: proto, + queue: RwLock::new(VecDeque::new()), + data: PeerData::Light(sync, chain), + } + } + + // get the chain from the client, asserting that it is a full node. + pub fn chain(&self) -> &TestBlockChainClient { + match self.data { + PeerData::Full(ref chain) => &*chain, + _ => panic!("Attempted to access full chain on light peer."), + } + } + + // get the light chain from the peer, asserting that it is a light node. + pub fn light_chain(&self) -> &LightClient { + match self.data { + PeerData::Light(_, ref chain) => &*chain, + _ => panic!("Attempted to access light chain on full peer."), + } + } + + // get a test Io context based on + fn io(&self, sender: Option) -> TestIoContext { + TestIoContext { + queue: &self.queue, + sender: sender, + to_disconnect: RwLock::new(HashSet::new()), + } + } +} + +impl PeerLike for Peer { + type Message = TestPacket; + + fn on_connect(&self, other: PeerId) { + let io = self.io(Some(other)); + self.proto.on_connect(&other, &io); + } + + fn on_disconnect(&self, other: PeerId){ + let io = self.io(Some(other)); + self.proto.on_disconnect(other, &io); + } + + fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet { + let io = self.io(Some(from)); + self.proto.handle_packet(&io, &from, msg.packet_id, &msg.data); + io.to_disconnect.into_inner() + } + + fn pending_message(&self) -> Option { + self.queue.write().pop_front() + } + + fn is_done(&self) -> bool { + self.queue.read().is_empty() + } + + fn sync_step(&self) { + if let PeerData::Light(_, ref client) = self.data { + client.flush_queue(); + client.import_verified(); + } + } + + fn restart_sync(&self) { } +} + +impl TestNet { + /// Create a new `TestNet` for testing light synchronization. + /// The first parameter is the number of light nodes, + /// the second is the number of full nodes. + pub fn light(n_light: usize, n_full: usize) -> Self { + let mut peers = Vec::with_capacity(n_light + n_full); + for _ in 0..n_light { + let client = LightClient::new(Default::default(), &Spec::new_test(), IoChannel::disconnected()); + peers.push(Arc::new(Peer::new_light(Arc::new(client)))) + } + + for _ in 0..n_full { + peers.push(Arc::new(Peer::new_full(Arc::new(TestBlockChainClient::new())))) + } + + TestNet { + peers: peers, + started: false, + disconnect_events: Vec::new(), + } + } +} diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index 3a598ba62..8ffc30711 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -100,8 +100,11 @@ fn forked() { fn forked_with_misbehaving_peer() { ::env_logger::init().ok(); let mut net = TestNet::new(3); + + let mut alt_spec = ::ethcore::spec::Spec::new_test(); + alt_spec.extra_data = b"fork".to_vec(); // peer 0 is on a totally different chain with higher total difficulty - net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_extra_data(b"fork".to_vec())); + net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec)); net.peer(0).chain.add_blocks(50, EachBlockWith::Nothing); net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing); net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing); diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index ea8bd970d..82b990f46 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -22,7 +22,7 @@ use ethcore::spec::Spec; use ethcore::miner::MinerService; use ethcore::transaction::*; use ethcore::account_provider::AccountProvider; -use ethkey::KeyPair; +use ethkey::{KeyPair, Secret}; use super::helpers::*; use SyncConfig; @@ -41,7 +41,7 @@ impl IoHandler for TestIoHandler { } } -fn new_tx(secret: &H256, nonce: U256) -> PendingTransaction { +fn new_tx(secret: &Secret, nonce: U256) -> PendingTransaction { let signed = Transaction { nonce: nonce.into(), gas_price: 0.into(), @@ -55,8 +55,8 @@ fn new_tx(secret: &H256, nonce: U256) -> PendingTransaction { #[test] fn authority_round() { - let s0 = KeyPair::from_secret("1".sha3()).unwrap(); - let s1 = KeyPair::from_secret("0".sha3()).unwrap(); + let s0 = KeyPair::from_secret_slice(&"1".sha3()).unwrap(); + let s1 = KeyPair::from_secret_slice(&"0".sha3()).unwrap(); let spec_factory = || { let spec = Spec::new_test_round(); let account_provider = AccountProvider::transient_provider(); @@ -118,8 +118,8 @@ fn authority_round() { #[test] fn tendermint() { - let s0 = KeyPair::from_secret("1".sha3()).unwrap(); - let s1 = KeyPair::from_secret("0".sha3()).unwrap(); + let s0 = KeyPair::from_secret_slice(&"1".sha3()).unwrap(); + let s1 = KeyPair::from_secret_slice(&"0".sha3()).unwrap(); let spec_factory = || { let spec = Spec::new_test_tendermint(); let account_provider = AccountProvider::transient_provider(); diff --git a/util/fetch/src/client.rs b/util/fetch/src/client.rs index bbc9f11e8..6c8921432 100644 --- a/util/fetch/src/client.rs +++ b/util/fetch/src/client.rs @@ -47,8 +47,10 @@ pub trait Fetch: Clone + Send + Sync + 'static { /// Spawn the future in context of this `Fetch` thread pool. /// Implementation is optional. - fn process(&self, f: F) -> BoxFuture<(), ()> where - F: Future + Send + 'static, + fn process(&self, f: F) -> BoxFuture where + F: Future + Send + 'static, + I: Send + 'static, + E: Send + 'static, { f.boxed() } @@ -99,8 +101,10 @@ impl Fetch for Client { Self::with_limit(Some(50*1024*1024)) } - fn process(&self, f: F) -> BoxFuture<(), ()> where - F: Future + Send + 'static, + fn process(&self, f: F) -> BoxFuture where + F: Future + Send + 'static, + I: Send + 'static, + E: Send + 'static, { self.pool.spawn(f).boxed() } diff --git a/util/network/src/handshake.rs b/util/network/src/handshake.rs index eb04ce869..3de7417d1 100644 --- a/util/network/src/handshake.rs +++ b/util/network/src/handshake.rs @@ -165,7 +165,7 @@ impl Handshake { self.id.clone_from_slice(remote_public); self.remote_nonce.clone_from_slice(remote_nonce); self.remote_version = remote_version; - let shared = ecdh::agree(host_secret, &self.id)?; + let shared = *ecdh::agree(host_secret, &self.id)?; let signature = H520::from_slice(sig); self.remote_ephemeral = recover(&signature.into(), &(&shared ^ &self.remote_nonce))?; Ok(()) @@ -271,7 +271,7 @@ impl Handshake { let (nonce, _) = rest.split_at_mut(32); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - let shared = ecdh::agree(secret, &self.id)?; + let shared = *ecdh::agree(secret, &self.id)?; sig.copy_from_slice(&*sign(self.ecdhe.secret(), &(&shared ^ &self.nonce))?); self.ecdhe.public().sha3_into(hepubk); pubk.copy_from_slice(public); @@ -366,7 +366,7 @@ mod test { #[test] fn test_handshake_auth_plain() { let mut h = create_handshake(None); - let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".into(); + let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".parse().unwrap(); let auth = "\ 048ca79ad18e4b0659fab4853fe5bc58eb83992980f4c9cc147d2aa31532efd29a3d3dc6a3d89eaf\ @@ -387,7 +387,7 @@ mod test { #[test] fn test_handshake_auth_eip8() { let mut h = create_handshake(None); - let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".into(); + let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".parse().unwrap(); let auth = "\ 01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b\ @@ -413,7 +413,7 @@ mod test { #[test] fn test_handshake_auth_eip8_2() { let mut h = create_handshake(None); - let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".into(); + let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".parse().unwrap(); let auth = "\ 01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7\ @@ -444,7 +444,7 @@ mod test { fn test_handshake_ack_plain() { let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); let mut h = create_handshake(Some(&remote)); - let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".into(); + let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = "\ 049f8abcfa9c0dc65b982e98af921bc0ba6e4243169348a236abe9df5f93aa69d99cadddaa387662\ @@ -464,7 +464,7 @@ mod test { fn test_handshake_ack_eip8() { let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); let mut h = create_handshake(Some(&remote)); - let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".into(); + let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = "\ 01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470\ @@ -493,7 +493,7 @@ mod test { fn test_handshake_ack_eip8_2() { let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); let mut h = create_handshake(Some(&remote)); - let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".into(); + let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = "\ 01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7\ diff --git a/util/network/src/host.rs b/util/network/src/host.rs index ba966e07c..2f236a5f7 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -1207,7 +1207,7 @@ fn load_key(path: &Path) -> Option { fn key_save_load() { use ::devtools::RandomTempPath; let temp_path = RandomTempPath::create_dir(); - let key = H256::random(); + let key = Secret::from_slice(&H256::random()).unwrap(); save_key(temp_path.as_path(), &key); let r = load_key(temp_path.as_path()); assert_eq!(key, r.unwrap()); @@ -1217,8 +1217,9 @@ fn key_save_load() { #[test] fn host_client_url() { let mut config = NetworkConfiguration::new_local(); - let key = "6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2".into(); + let key = "6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2".parse().unwrap(); config.use_secret = Some(key); let host: Host = Host::new(config, Arc::new(NetworkStats::new())).unwrap(); assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); } + diff --git a/util/reactor/src/lib.rs b/util/reactor/src/lib.rs index dc3339376..a9d2aedbd 100644 --- a/util/reactor/src/lib.rs +++ b/util/reactor/src/lib.rs @@ -57,6 +57,17 @@ impl EventLoop { } } + /// Returns this event loop raw remote. + /// + /// Deprecated: Exists only to connect with current JSONRPC implementation. + pub fn raw_remote(&self) -> TokioRemote { + if let Mode::Tokio(ref remote) = self.remote.inner { + remote.clone() + } else { + panic!("Event loop is never initialized in other mode then Tokio.") + } + } + /// Returns event loop remote. pub fn remote(&self) -> Remote { self.remote.clone() @@ -76,6 +87,15 @@ pub struct Remote { } impl Remote { + /// Remote for existing event loop. + /// + /// Deprecated: Exists only to connect with current JSONRPC implementation. + pub fn new(remote: TokioRemote) -> Self { + Remote { + inner: Mode::Tokio(remote), + } + } + /// Synchronous remote, used mostly for tests. pub fn new_sync() -> Self { Remote { diff --git a/util/src/lib.rs b/util/src/lib.rs index e37214879..9b4d3be59 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -164,6 +164,3 @@ pub use timer::*; /// 160-bit integer representing account address pub type Address = H160; - -/// Secret -pub type Secret = H256;