diff --git a/.gitignore b/.gitignore index 47546d0ed..f31145039 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ out/ .vscode + +/parity.* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 28604d0cd..6deba8ec4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,8 +20,10 @@ linux-stable: - stable - triggers script: + - curl --data "secret=$RELEASES_SECRET" http://icarus.parity.io/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF - cargo build -j $(nproc) --release $CARGOFLAGS - strip target/release/parity + - export SHA3=$(target/release/parity tools hash target/release/parity) - md5sum target/release/parity > parity.md5 - sh scripts/deb-build.sh amd64 - cp target/release/parity deb/usr/bin/parity @@ -36,6 +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://icarus.parity.io/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu tags: - rust - rust-stable @@ -92,15 +95,18 @@ linux-centos: script: - export CXX="g++" - export CC="gcc" + - export PLATFORM=x86_64-unknown-centos-gnu - cargo build -j $(nproc) --release $CARGOFLAGS - strip target/release/parity - md5sum target/release/parity > parity.md5 + - export SHA3=$(target/release/parity tools hash target/release/parity) - aws configure set aws_access_key_id $s3_key - aws configure set aws_secret_access_key $s3_secret - if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi - 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://icarus.parity.io/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust - rust-centos @@ -119,22 +125,26 @@ linux-i686: script: - export HOST_CC=gcc - export HOST_CXX=g++ + - export COMMIT=$(git rev-parse HEAD) + - export PLATFORM=i686-unknown-linux-gnu - cargo build -j $(nproc) --target i686-unknown-linux-gnu --release $CARGOFLAGS - - strip target/i686-unknown-linux-gnu/release/parity - - md5sum target/i686-unknown-linux-gnu/release/parity > parity.md5 + - strip target/$PLATFORM/release/parity + - md5sum target/$PLATFORM/release/parity > parity.md5 + - export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity) - sh scripts/deb-build.sh i386 - - cp target/i686-unknown-linux-gnu/release/parity deb/usr/bin/parity + - cp target/$PLATFORM/release/parity deb/usr/bin/parity - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n") - dpkg-deb -b deb "parity_"$VER"_i386.deb" - md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5" - aws configure set aws_access_key_id $s3_key - aws configure set aws_secret_access_key $s3_secret - if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi - - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/i686-unknown-linux-gnu - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity --body target/i686-unknown-linux-gnu/release/parity - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity.md5 --body parity.md5 - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb" - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5" + - 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 + - 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://icarus.parity.io/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - rust - rust-i686 @@ -292,7 +302,6 @@ linux-aarch64: - aws configure set aws_secret_access_key $s3_secret - if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5 - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb" - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5" @@ -312,10 +321,13 @@ darwin: - stable - triggers script: + - export COMMIT=$(git rev-parse HEAD) + - export PLATFORM=x86_64-apple-darwin - cargo build -j 8 --release #$CARGOFLAGS - cargo build -j 8 --release -p ethstore #$CARGOFLAGS - rm -rf parity.md5 - md5sum target/release/parity > parity.md5 + - export SHA3=$(target/release/parity tools hash target/release/parity) - packagesbuild -v mac/Parity.pkgproj - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n") - mv target/release/Parity\ Ethereum.pkg "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" @@ -323,11 +335,12 @@ darwin: - aws configure set aws_access_key_id $s3_key - aws configure set aws_secret_access_key $s3_secret - if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi - - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-apple-darwin - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity.md5 --body parity.md5 - - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/"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/x86_64-apple-darwin/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" + - 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/release/parity + - 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://icarus.parity.io/push-build/$CI_BUILD_REF_NAME/$PLATFORM tags: - osx artifacts: @@ -345,12 +358,14 @@ windows: - stable - triggers script: + - set PLATFORM=x86_64-pc-windows-msvc - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt - set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64 - set RUST_BACKTRACE=1 - set RUSTFLAGS=%RUSTFLAGS% - rustup default stable-x86_64-pc-windows-msvc - cargo build --release #%CARGOFLAGS% + - FOR /F "tokens=* USEBACKQ" %%i IN ('target\release\parity.exe tools hash target\release\parity.exe') DO set SHA3=%%i - curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll - curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe - signtool sign /f %keyfile% /p %certpass% target\release\parity.exe @@ -385,6 +400,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://icarus.parity.io/push-build/$CI_BUILD_REF_NAME/%PLATFORM% tags: - rust-windows artifacts: @@ -399,9 +415,10 @@ test-darwin: - triggers before_script: - git submodule update --init --recursive + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) script: - export RUST_BACKTRACE=1 - - ./test.sh $CARGOFLAGS + - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - osx allow_failure: true @@ -413,7 +430,7 @@ test-windows: - git submodule update --init --recursive script: - set RUST_BACKTRACE=1 - - cargo -j 8 test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release + - echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release tags: - rust-windows allow_failure: true @@ -448,10 +465,10 @@ test-rust-beta: image: ethcore/rust:beta before_script: - git submodule update --init --recursive + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) script: - export RUST_BACKTRACE=1 - - echo $JS_FILES_MODIFIED - - ./test.sh $CARGOFLAGS + - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - rust - rust-beta @@ -463,9 +480,10 @@ test-rust-nightly: image: ethcore/rust:nightly before_script: - git submodule update --init --recursive + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) script: - export RUST_BACKTRACE=1 - - ./test.sh $CARGOFLAGS + - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - rust - rust-nightly diff --git a/Cargo.lock b/Cargo.lock index 037360608..a7887f9d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,7 +12,6 @@ dependencies = [ "ethcore 1.5.0", "ethcore-dapps 1.5.0", "ethcore-devtools 1.5.0", - "ethcore-hash-fetch 1.5.0", "ethcore-io 1.5.0", "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", @@ -33,14 +32,16 @@ dependencies = [ "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)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-hash-fetch 1.5.0", "parity-rpc-client 1.4.0", + "parity-updater 1.5.0", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rpc-cli 1.4.0", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.2.3 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", @@ -306,6 +307,7 @@ dependencies = [ "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.5.0", "ethcore-bloom-journal 0.1.0", "ethcore-devtools 1.5.0", @@ -330,7 +332,7 @@ dependencies = [ "rlp 0.1.0", "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)", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (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)", ] @@ -359,7 +361,6 @@ dependencies = [ "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.5.0", - "ethcore-hash-fetch 1.5.0", "ethcore-rpc 1.5.0", "ethcore-util 1.5.0", "fetch 0.1.0", @@ -371,6 +372,7 @@ dependencies = [ "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.6.1 (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-hash-fetch 1.5.0", "parity-ui 1.5.0", "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)", @@ -390,18 +392,6 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ethcore-hash-fetch" -version = "1.5.0" -dependencies = [ - "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-util 1.5.0", - "fetch 0.1.0", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ethcore-io" version = "1.5.0" @@ -420,7 +410,7 @@ dependencies = [ "ethcore-devtools 1.5.0", "ethcore-util 1.5.0", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -443,7 +433,7 @@ dependencies = [ "ethcore-ipc-nano 1.5.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -468,7 +458,7 @@ dependencies = [ "ethcore-util 1.5.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -546,8 +536,10 @@ dependencies = [ "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)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-updater 1.5.0", "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.4 (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)", @@ -589,7 +581,7 @@ dependencies = [ "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)", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -705,7 +697,7 @@ dependencies = [ "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -794,6 +786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "https-fetch" version = "0.1.0" dependencies = [ + "ethabi 0.2.2 (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)", "rustls 0.1.2 (git+https://github.com/ctz/rustls)", @@ -860,6 +853,17 @@ dependencies = [ "xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ipc-common-types" +version = "1.5.0" +dependencies = [ + "ethcore-ipc 1.5.0", + "ethcore-ipc-codegen 1.5.0", + "ethcore-util 1.5.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "isatty" version = "0.1.1" @@ -1312,6 +1316,18 @@ dependencies = [ "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-hash-fetch" +version = "1.5.0" +dependencies = [ + "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-util 1.5.0", + "fetch 0.1.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-rpc-client" version = "1.4.0" @@ -1351,11 +1367,26 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#2d07c405453bcf1e603c3965387b7f920565b6d8" +source = "git+https://github.com/ethcore/js-precompiled.git#3d390b35737ce212d358f26b5ec8d9644b252a88" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-updater" +version = "1.5.0" +dependencies = [ + "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore 1.5.0", + "ethcore-ipc 1.5.0", + "ethcore-ipc-codegen 1.5.0", + "ethcore-util 1.5.0", + "ethsync 1.5.0", + "ipc-common-types 1.5.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-hash-fetch 1.5.0", +] + [[package]] name = "parking_lot" version = "0.3.6" @@ -1674,6 +1705,23 @@ dependencies = [ "nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "semver" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "serde" version = "0.8.4" @@ -2240,6 +2288,8 @@ dependencies = [ "checksum rustls 0.1.2 (git+https://github.com/ctz/rustls)" = "" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" "checksum semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d5b7638a1f03815d94e88cb3b3c08e87f0db4d683ef499d1836aaf70a45623f" +"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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b1dfda9ebb31d29fa8b94d7eb3031a86a8dcec065f0fe268a30f98867bf45775" "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" diff --git a/Cargo.toml b/Cargo.toml index 2d16e0bd4..632d8d2a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Ethcore client." +description = "Parity Ethereum client" name = "parity" version = "1.5.0" license = "GPL-3.0" @@ -20,7 +20,7 @@ time = "0.1" num_cpus = "0.2" number_prefix = "0.2" rpassword = "0.2.1" -semver = "0.2" +semver = "0.5" ansi_term = "0.7" lazy_static = "0.2" regex = "0.1" @@ -32,25 +32,26 @@ app_dirs = "1.1.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" } +ethsync = { path = "sync" } ethcore = { path = "ethcore" } ethcore-util = { path = "util" } -ethsync = { path = "sync" } ethcore-io = { path = "util/io" } ethcore-devtools = { path = "devtools" } ethcore-rpc = { path = "rpc" } ethcore-signer = { path = "signer" } -ethcore-ipc-nano = { path = "ipc/nano" } ethcore-ipc = { path = "ipc/rpc" } +ethcore-ipc-nano = { path = "ipc/nano" } ethcore-ipc-hypervisor = { path = "ipc/hypervisor" } ethcore-logger = { path = "logger" } -ethcore-hash-fetch = { path = "ethcore/hash-fetch" } -rlp = { path = "util/rlp" } ethcore-stratum = { path = "stratum" } ethcore-dapps = { path = "dapps", optional = true } -clippy = { version = "0.0.103", optional = true} 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" } [target.'cfg(windows)'.dependencies] winapi = "0.2" diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 93c82790a..75e8e346c 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -30,9 +30,9 @@ zip = { version = "0.1", default-features = false } ethcore-devtools = { path = "../devtools" } ethcore-rpc = { path = "../rpc" } ethcore-util = { path = "../util" } -ethcore-hash-fetch = { path = "../ethcore/hash-fetch" } fetch = { path = "../util/fetch" } parity-ui = { path = "./ui" } +parity-hash-fetch = { path = "../hash-fetch" } clippy = { version = "0.0.103", optional = true} diff --git a/dapps/src/apps/fetcher.rs b/dapps/src/apps/fetcher.rs index 2430af035..006858e73 100644 --- a/dapps/src/apps/fetcher.rs +++ b/dapps/src/apps/fetcher.rs @@ -120,7 +120,7 @@ impl ContentFetcher { // Content is already being fetched Some(&mut ContentStatus::Fetching(ref fetch_control)) => { trace!(target: "dapps", "Content fetching in progress. Waiting..."); - (None, fetch_control.to_handler(control)) + (None, fetch_control.to_async_handler(path, control)) }, // We need to start fetching the content None => { @@ -129,11 +129,12 @@ impl ContentFetcher { let content = self.resolver.resolve(content_hex); let cache = self.cache.clone(); - let on_done = move |id: String, result: Option| { + let id = content_id.clone(); + let on_done = move |result: Option| { let mut cache = cache.lock(); match result { Some(endpoint) => { - cache.insert(id, ContentStatus::Ready(endpoint)); + cache.insert(id.clone(), ContentStatus::Ready(endpoint)); }, // In case of error None => { @@ -150,6 +151,7 @@ impl ContentFetcher { Some(URLHintResult::Dapp(dapp)) => { let (handler, fetch_control) = ContentFetcherHandler::new( dapp.url(), + path, control, DappInstaller { id: content_id.clone(), @@ -165,6 +167,7 @@ impl ContentFetcher { Some(URLHintResult::Content(content)) => { let (handler, fetch_control) = ContentFetcherHandler::new( content.url, + path, control, ContentInstaller { id: content_id.clone(), @@ -248,43 +251,45 @@ struct ContentInstaller { id: String, mime: String, content_path: PathBuf, - on_done: Box) + Send>, + on_done: Box) + Send>, } impl ContentValidator for ContentInstaller { type Error = ValidationError; - fn validate_and_install(&self, path: PathBuf) -> Result<(String, LocalPageEndpoint), ValidationError> { - // Create dir - try!(fs::create_dir_all(&self.content_path)); + fn validate_and_install(&self, path: PathBuf) -> Result { + let validate = || { + // Create dir + try!(fs::create_dir_all(&self.content_path)); - // Validate hash - let mut file_reader = io::BufReader::new(try!(fs::File::open(&path))); - let hash = try!(sha3(&mut file_reader)); - let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId)); - if id != hash { - return Err(ValidationError::HashMismatch { - expected: id, - got: hash, - }); - } + // Validate hash + let mut file_reader = io::BufReader::new(try!(fs::File::open(&path))); + let hash = try!(sha3(&mut file_reader)); + let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId)); + if id != hash { + return Err(ValidationError::HashMismatch { + expected: id, + got: hash, + }); + } - // And prepare path for a file - let filename = path.file_name().expect("We always fetch a file."); - let mut content_path = self.content_path.clone(); - content_path.push(&filename); + // And prepare path for a file + let filename = path.file_name().expect("We always fetch a file."); + let mut content_path = self.content_path.clone(); + content_path.push(&filename); - if content_path.exists() { - try!(fs::remove_dir_all(&content_path)) - } + if content_path.exists() { + try!(fs::remove_dir_all(&content_path)) + } - try!(fs::copy(&path, &content_path)); + try!(fs::copy(&path, &content_path)); + Ok(LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled)) + }; - Ok((self.id.clone(), LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled))) - } - - fn done(&self, endpoint: Option) { - (self.on_done)(self.id.clone(), endpoint) + // Make sure to always call on_done (even in case of errors)! + let result = validate(); + (self.on_done)(result.as_ref().ok().cloned()); + result } } @@ -292,7 +297,7 @@ impl ContentValidator for ContentInstaller { struct DappInstaller { id: String, dapps_path: PathBuf, - on_done: Box) + Send>, + on_done: Box) + Send>, embeddable_on: Option<(String, u16)>, } @@ -331,69 +336,68 @@ impl DappInstaller { impl ContentValidator for DappInstaller { type Error = ValidationError; - fn validate_and_install(&self, app_path: PathBuf) -> Result<(String, LocalPageEndpoint), ValidationError> { - trace!(target: "dapps", "Opening dapp bundle at {:?}", app_path); - let mut file_reader = io::BufReader::new(try!(fs::File::open(app_path))); - let hash = try!(sha3(&mut file_reader)); - let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId)); - if id != hash { - return Err(ValidationError::HashMismatch { - expected: id, - got: hash, - }); - } - let file = file_reader.into_inner(); - // Unpack archive - let mut zip = try!(zip::ZipArchive::new(file)); - // First find manifest file - let (mut manifest, manifest_dir) = try!(Self::find_manifest(&mut zip)); - // Overwrite id to match hash - manifest.id = self.id.clone(); + fn validate_and_install(&self, path: PathBuf) -> Result { + trace!(target: "dapps", "Opening dapp bundle at {:?}", path); + let validate = || { + let mut file_reader = io::BufReader::new(try!(fs::File::open(path))); + let hash = try!(sha3(&mut file_reader)); + let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId)); + if id != hash { + return Err(ValidationError::HashMismatch { + expected: id, + got: hash, + }); + } + let file = file_reader.into_inner(); + // Unpack archive + let mut zip = try!(zip::ZipArchive::new(file)); + // First find manifest file + let (mut manifest, manifest_dir) = try!(Self::find_manifest(&mut zip)); + // Overwrite id to match hash + manifest.id = self.id.clone(); - let target = self.dapp_target_path(&manifest); + let target = self.dapp_target_path(&manifest); - // Remove old directory - if target.exists() { - warn!(target: "dapps", "Overwriting existing dapp: {}", manifest.id); - try!(fs::remove_dir_all(target.clone())); - } + // Remove old directory + if target.exists() { + warn!(target: "dapps", "Overwriting existing dapp: {}", manifest.id); + try!(fs::remove_dir_all(target.clone())); + } - // Unpack zip - for i in 0..zip.len() { - let mut file = try!(zip.by_index(i)); - // TODO [todr] Check if it's consistent on windows. - let is_dir = file.name().chars().rev().next() == Some('/'); + // Unpack zip + for i in 0..zip.len() { + let mut file = try!(zip.by_index(i)); + // TODO [todr] Check if it's consistent on windows. + let is_dir = file.name().chars().rev().next() == Some('/'); - let file_path = PathBuf::from(file.name()); - let location_in_manifest_base = file_path.strip_prefix(&manifest_dir); - // Create files that are inside manifest directory - if let Ok(location_in_manifest_base) = location_in_manifest_base { - let p = target.join(location_in_manifest_base); - // Check if it's a directory - if is_dir { - try!(fs::create_dir_all(p)); - } else { - let mut target = try!(fs::File::create(p)); - try!(io::copy(&mut file, &mut target)); + let file_path = PathBuf::from(file.name()); + let location_in_manifest_base = file_path.strip_prefix(&manifest_dir); + // Create files that are inside manifest directory + if let Ok(location_in_manifest_base) = location_in_manifest_base { + let p = target.join(location_in_manifest_base); + // Check if it's a directory + if is_dir { + try!(fs::create_dir_all(p)); + } else { + let mut target = try!(fs::File::create(p)); + try!(io::copy(&mut file, &mut target)); + } } } - } - // Write manifest - let manifest_str = try!(serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization)); - let manifest_path = target.join(MANIFEST_FILENAME); - let mut manifest_file = try!(fs::File::create(manifest_path)); - try!(manifest_file.write_all(manifest_str.as_bytes())); + // Write manifest + let manifest_str = try!(serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization)); + let manifest_path = target.join(MANIFEST_FILENAME); + let mut manifest_file = try!(fs::File::create(manifest_path)); + try!(manifest_file.write_all(manifest_str.as_bytes())); + // Create endpoint + let endpoint = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone()); + Ok(endpoint) + }; - // Create endpoint - let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone()); - - // Return modified app manifest - Ok((manifest.id.clone(), app)) - } - - fn done(&self, endpoint: Option) { - (self.on_done)(self.id.clone(), endpoint) + let result = validate(); + (self.on_done)(result.as_ref().ok().cloned()); + result } } diff --git a/dapps/src/handlers/fetch.rs b/dapps/src/handlers/fetch.rs index 6fb524293..d62b425d9 100644 --- a/dapps/src/handlers/fetch.rs +++ b/dapps/src/handlers/fetch.rs @@ -22,35 +22,41 @@ use std::sync::{mpsc, Arc}; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Instant, Duration}; use util::Mutex; -use url::Url; use fetch::{Client, Fetch, FetchResult}; use hyper::{server, Decoder, Encoder, Next, Method, Control}; use hyper::net::HttpStream; +use hyper::uri::RequestUri; use hyper::status::StatusCode; -use handlers::{ContentHandler, Redirection, extract_url}; -use page::LocalPageEndpoint; +use endpoint::EndpointPath; +use handlers::ContentHandler; +use page::{LocalPageEndpoint, PageHandlerWaiting}; const FETCH_TIMEOUT: u64 = 30; enum FetchState { + Waiting, NotStarted(String), Error(ContentHandler), InProgress(mpsc::Receiver), - Done(String, LocalPageEndpoint, Redirection), + Done(LocalPageEndpoint, Box), +} + +enum WaitResult { + Error(ContentHandler), + Done(LocalPageEndpoint), } pub trait ContentValidator { type Error: fmt::Debug + fmt::Display; - fn validate_and_install(&self, app: PathBuf) -> Result<(String, LocalPageEndpoint), Self::Error>; - fn done(&self, Option); + fn validate_and_install(&self, path: PathBuf) -> Result; } pub struct FetchControl { abort: Arc, - listeners: Mutex)>>, + listeners: Mutex)>>, deadline: Instant, } @@ -65,9 +71,10 @@ impl Default for FetchControl { } impl FetchControl { - fn notify FetchState>(&self, status: F) { + fn notify WaitResult>(&self, status: F) { let mut listeners = self.listeners.lock(); for (control, sender) in listeners.drain(..) { + trace!(target: "dapps", "Resuming request waiting for content..."); if let Err(e) = sender.send(status()) { trace!(target: "dapps", "Waiting listener notification failed: {:?}", e); } else { @@ -78,9 +85,9 @@ impl FetchControl { fn set_status(&self, status: &FetchState) { match *status { - FetchState::Error(ref handler) => self.notify(|| FetchState::Error(handler.clone())), - FetchState::Done(ref id, ref endpoint, ref handler) => self.notify(|| FetchState::Done(id.clone(), endpoint.clone(), handler.clone())), - FetchState::NotStarted(_) | FetchState::InProgress(_) => {}, + FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())), + FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())), + FetchState::NotStarted(_) | FetchState::InProgress(_) | FetchState::Waiting => {}, } } @@ -88,44 +95,66 @@ impl FetchControl { self.abort.store(true, Ordering::SeqCst); } - pub fn to_handler(&self, control: Control) -> Box + Send> { + pub fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box + Send> { let (tx, rx) = mpsc::channel(); self.listeners.lock().push((control, tx)); Box::new(WaitingHandler { receiver: rx, - state: None, + state: FetchState::Waiting, + uri: RequestUri::default(), + path: path, }) } } pub struct WaitingHandler { - receiver: mpsc::Receiver, - state: Option, + receiver: mpsc::Receiver, + state: FetchState, + uri: RequestUri, + path: EndpointPath, } impl server::Handler for WaitingHandler { - fn on_request(&mut self, _request: server::Request) -> Next { + fn on_request(&mut self, request: server::Request) -> Next { + self.uri = request.uri().clone(); Next::wait() } - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { - self.state = self.receiver.try_recv().ok(); - Next::write() + fn on_request_readable(&mut self, decoder: &mut Decoder) -> Next { + let result = self.receiver.try_recv().ok(); + self.state = match result { + Some(WaitResult::Error(handler)) => FetchState::Error(handler), + Some(WaitResult::Done(endpoint)) => { + let mut page_handler = endpoint.to_page_handler(self.path.clone()); + page_handler.set_uri(&self.uri); + FetchState::Done(endpoint, page_handler) + }, + None => { + warn!("A result for waiting request was not received."); + FetchState::Waiting + }, + }; + + match self.state { + FetchState::Done(_, ref mut handler) => handler.on_request_readable(decoder), + FetchState::Error(ref mut handler) => handler.on_request_readable(decoder), + _ => Next::write(), + } } fn on_response(&mut self, res: &mut server::Response) -> Next { match self.state { - Some(FetchState::Done(_, _, ref mut handler)) => handler.on_response(res), - Some(FetchState::Error(ref mut handler)) => handler.on_response(res), + FetchState::Done(_, ref mut handler) => handler.on_response(res), + FetchState::Error(ref mut handler) => handler.on_response(res), _ => Next::end(), } } fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { match self.state { - Some(FetchState::Done(_, _, ref mut handler)) => handler.on_response_writable(encoder), - Some(FetchState::Error(ref mut handler)) => handler.on_response_writable(encoder), + FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder), + FetchState::Error(ref mut handler) => handler.on_response_writable(encoder), _ => Next::end(), } } @@ -137,29 +166,19 @@ pub struct ContentFetcherHandler { status: FetchState, client: Option, installer: H, - request_url: Option, + path: EndpointPath, + uri: RequestUri, embeddable_on: Option<(String, u16)>, } -impl Drop for ContentFetcherHandler { - fn drop(&mut self) { - let result = match self.status { - FetchState::Done(_, ref result, _) => Some(result.clone()), - _ => None, - }; - self.installer.done(result); - } -} - impl ContentFetcherHandler { - pub fn new( url: String, + path: EndpointPath, control: Control, handler: H, embeddable_on: Option<(String, u16)>, ) -> (Self, Arc) { - let fetch_control = Arc::new(FetchControl::default()); let client = Client::default(); let handler = ContentFetcherHandler { @@ -168,7 +187,8 @@ impl ContentFetcherHandler { client: Some(client), status: FetchState::NotStarted(url), installer: handler, - request_url: None, + path: path, + uri: RequestUri::default(), embeddable_on: embeddable_on, }; @@ -192,7 +212,6 @@ impl ContentFetcherHandler { impl server::Handler for ContentFetcherHandler { fn on_request(&mut self, request: server::Request) -> Next { - self.request_url = extract_url(&request); let status = if let FetchState::NotStarted(ref url) = self.status { Some(match *request.method() { // Start fetching content @@ -205,8 +224,8 @@ impl server::Handler for ContentFetcherHandler< Ok(receiver) => FetchState::InProgress(receiver), Err(e) => FetchState::Error(ContentHandler::error( StatusCode::BadGateway, - "Unable To Start Dapp Download", - "Could not initialize download of the dapp. It might be a problem with the remote server.", + "Unable To Start Content Download", + "Could not initialize download of the content. It might be a problem with the remote server.", Some(&format!("{}", e)), self.embeddable_on.clone(), )), @@ -227,6 +246,7 @@ impl server::Handler for ContentFetcherHandler< self.fetch_control.set_status(&status); self.status = status; } + self.uri = request.uri().clone(); Next::read() } @@ -266,11 +286,10 @@ impl server::Handler for ContentFetcherHandler< self.embeddable_on.clone(), )) }, - Ok((id, result)) => { - let url: String = self.request_url.take() - .map(|url| url.raw.into_string()) - .expect("Request URL always read in on_request; qed"); - FetchState::Done(id, result, Redirection::new(&url)) + Ok(endpoint) => { + let mut handler = endpoint.to_page_handler(self.path.clone()); + handler.set_uri(&self.uri); + FetchState::Done(endpoint, handler) }, }; // Remove temporary zip file @@ -306,7 +325,7 @@ impl server::Handler for ContentFetcherHandler< fn on_response(&mut self, res: &mut server::Response) -> Next { match self.status { - FetchState::Done(_, _, ref mut handler) => handler.on_response(res), + FetchState::Done(_, ref mut handler) => handler.on_response(res), FetchState::Error(ref mut handler) => handler.on_response(res), _ => Next::end(), } @@ -314,7 +333,7 @@ impl server::Handler for ContentFetcherHandler< fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { match self.status { - FetchState::Done(_, _, ref mut handler) => handler.on_response_writable(encoder), + FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder), FetchState::Error(ref mut handler) => handler.on_response_writable(encoder), _ => Next::end(), } diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index c1ce5de19..72a246596 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -57,7 +57,7 @@ extern crate mime_guess; extern crate rustc_serialize; extern crate ethcore_rpc; extern crate ethcore_util as util; -extern crate ethcore_hash_fetch as hash_fetch; +extern crate parity_hash_fetch as hash_fetch; extern crate linked_hash_map; extern crate fetch; extern crate parity_dapps_glue as parity_dapps; diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 382dfa5d1..ba7a7ee04 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -83,13 +83,19 @@ impl Default for PageCache { } } +/// A generic type for `PageHandler` allowing to set the URL. +/// Used by dapps fetching to set the URL after the content was downloaded. +pub trait PageHandlerWaiting: server::Handler + Send { + fn set_uri(&mut self, uri: &RequestUri); +} + /// A handler for a single webapp. /// Resolves correct paths and serves as a plumbing code between /// hyper server and dapp. pub struct PageHandler { /// A Dapp. pub app: T, - /// File currently being served (or `None` if file does not exist). + /// File currently being served pub file: ServedFile, /// Optional prefix to strip from path. pub prefix: Option, @@ -101,6 +107,21 @@ pub struct PageHandler { pub cache: PageCache, } +impl PageHandlerWaiting for PageHandler { + fn set_uri(&mut self, uri: &RequestUri) { + trace!(target: "dapps", "Setting URI: {:?}", uri); + self.file = match *uri { + RequestUri::AbsolutePath { ref path, .. } => { + self.app.file(&self.extract_path(path)) + }, + RequestUri::AbsoluteUri(ref url) => { + self.app.file(&self.extract_path(url.path())) + }, + _ => None, + }.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f)); + } +} + impl PageHandler { fn extract_path(&self, path: &str) -> String { let app_id = &self.path.app_id; @@ -124,15 +145,7 @@ impl PageHandler { impl server::Handler for PageHandler { fn on_request(&mut self, req: server::Request) -> Next { - self.file = match *req.uri() { - RequestUri::AbsolutePath { ref path, .. } => { - self.app.file(&self.extract_path(path)) - }, - RequestUri::AbsoluteUri(ref url) => { - self.app.file(&self.extract_path(url.path())) - }, - _ => None, - }.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f)); + self.set_uri(req.uri()); Next::write() } diff --git a/dapps/src/page/local.rs b/dapps/src/page/local.rs index 77c91019d..e8ab9ce14 100644 --- a/dapps/src/page/local.rs +++ b/dapps/src/page/local.rs @@ -18,7 +18,7 @@ use mime_guess; use std::io::{Seek, Read, SeekFrom}; use std::fs; use std::path::{Path, PathBuf}; -use page::handler::{self, PageCache}; +use page::handler::{self, PageCache, PageHandlerWaiting}; use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler}; #[derive(Debug, Clone)] @@ -54,6 +54,36 @@ impl LocalPageEndpoint { pub fn path(&self) -> PathBuf { self.path.clone() } + + fn page_handler_with_mime(&self, path: EndpointPath, mime: &str) -> handler::PageHandler { + handler::PageHandler { + app: LocalSingleFile { path: self.path.clone(), mime: mime.into() }, + prefix: None, + path: path, + file: handler::ServedFile::new(None), + safe_to_embed_on: self.embeddable_on.clone(), + cache: self.cache, + } + } + + fn page_handler(&self, path: EndpointPath) -> handler::PageHandler { + handler::PageHandler { + app: LocalDapp { path: self.path.clone() }, + prefix: None, + path: path, + file: handler::ServedFile::new(None), + safe_to_embed_on: self.embeddable_on.clone(), + cache: self.cache, + } + } + + pub fn to_page_handler(&self, path: EndpointPath) -> Box { + if let Some(ref mime) = self.mime { + Box::new(self.page_handler_with_mime(path, mime)) + } else { + Box::new(self.page_handler(path)) + } + } } impl Endpoint for LocalPageEndpoint { @@ -63,23 +93,9 @@ impl Endpoint for LocalPageEndpoint { fn to_handler(&self, path: EndpointPath) -> Box { if let Some(ref mime) = self.mime { - Box::new(handler::PageHandler { - app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() }, - prefix: None, - path: path, - file: handler::ServedFile::new(None), - safe_to_embed_on: self.embeddable_on.clone(), - cache: self.cache, - }) + Box::new(self.page_handler_with_mime(path, mime)) } else { - Box::new(handler::PageHandler { - app: LocalDapp { path: self.path.clone() }, - prefix: None, - path: path, - file: handler::ServedFile::new(None), - safe_to_embed_on: self.embeddable_on.clone(), - cache: self.cache, - }) + Box::new(self.page_handler(path)) } } } diff --git a/dapps/src/page/mod.rs b/dapps/src/page/mod.rs index 9619f1b10..5c2b008f8 100644 --- a/dapps/src/page/mod.rs +++ b/dapps/src/page/mod.rs @@ -21,5 +21,5 @@ mod handler; pub use self::local::LocalPageEndpoint; pub use self::builtin::PageEndpoint; -pub use self::handler::PageCache; +pub use self::handler::{PageCache, PageHandlerWaiting}; diff --git a/dapps/src/tests/helpers.rs b/dapps/src/tests/helpers.rs index 13d7c69d2..87edd00e0 100644 --- a/dapps/src/tests/helpers.rs +++ b/dapps/src/tests/helpers.rs @@ -65,7 +65,7 @@ fn init_logger() { if let Ok(log) = env::var("RUST_LOG") { let mut builder = LogBuilder::new(); builder.parse(&log); - builder.init().expect("Logger is initialized only once."); + let _ = builder.init(); // ignore errors since ./test.sh will call this multiple times. } } diff --git a/db/Cargo.toml b/db/Cargo.toml index 30bfda90a..d0e45a489 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -15,7 +15,7 @@ clippy = { version = "0.0.103", optional = true} ethcore-devtools = { path = "../devtools" } ethcore-ipc = { path = "../ipc/rpc" } rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" } -semver = "0.2" +semver = "0.5" ethcore-ipc-nano = { path = "../ipc/nano" } nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } crossbeam = "0.2" diff --git a/db/src/database.rs b/db/src/database.rs index c17270034..36f349e32 100644 --- a/db/src/database.rs +++ b/db/src/database.rs @@ -270,8 +270,7 @@ impl DatabaseService for Database { Ok(next_iterator) } - fn iter_next(&self, handle: IteratorHandle) -> Option - { + fn iter_next(&self, handle: IteratorHandle) -> Option { let mut iterators = self.iterators.write(); let mut iterator = match iterators.get_mut(&handle) { Some(some_iterator) => some_iterator, diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 40d767ab0..a815bddd6 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -21,7 +21,7 @@ crossbeam = "0.2.9" lazy_static = "0.2" bloomchain = "0.1" rayon = "0.4.2" -semver = "0.2" +semver = "0.5" bit-set = "0.4" time = "0.1" rand = "0.3" @@ -42,6 +42,7 @@ ethcore-ipc-nano = { path = "../ipc/nano" } rlp = { path = "../util/rlp" } lru-cache = "0.1.0" ethcore-bloom-journal = { path = "../util/bloom" } +ethabi = "0.2.2" [dependencies.hyper] git = "https://github.com/ethcore/hyper" diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index d388ce9a1..906e1c90c 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -15,7 +15,8 @@ "eip155Transition": 10, "eip160Transition": 10, "eip161abcTransition": 10, - "eip161dTransition": 10 + "eip161dTransition": 10, + "maxCodeSize": 24576 } } }, diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index e8f4624b7..9028c4801 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be +Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8 diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 64ba8d756..4e54f22b1 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -13,7 +13,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . + use std::collections::{HashSet, HashMap, BTreeMap, VecDeque}; +use std::str::FromStr; use std::sync::{Arc, Weak}; use std::path::{Path}; use std::fmt; @@ -22,7 +24,7 @@ use std::time::{Instant}; use time::precise_time_ns; // util -use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock, Hashable}; +use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock, MutexGuard, Hashable}; use util::{journaldb, TrieFactory, Trie}; use util::{U256, H256, Address, H2048, Uint, FixedHash}; use util::trie::TrieSpec; @@ -42,7 +44,7 @@ use env_info::LastHashes; use verification; use verification::{PreverifiedBlock, Verifier}; use block::*; -use transaction::{LocalizedTransaction, SignedTransaction, PendingTransaction, Action}; +use transaction::{LocalizedTransaction, SignedTransaction, Transaction, PendingTransaction, Action}; use blockchain::extras::TransactionAddress; use types::filter::Filter; use types::mode::Mode as IpcMode; @@ -68,6 +70,7 @@ use factory::Factories; use rlp::{decode, View, UntrustedRlp}; use state_db::StateDB; use rand::OsRng; +use client::registry::Registry; // re-export pub use types::blockchain_info::BlockChainInfo; @@ -124,6 +127,7 @@ impl SleepState { /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. pub struct Client { + enabled: AtomicBool, mode: Mutex, chain: RwLock>, tracedb: RwLock>, @@ -148,6 +152,7 @@ pub struct Client { history: u64, rng: Mutex, on_mode_change: Mutex>>, + registrar: Mutex>, } impl Client { @@ -160,6 +165,7 @@ impl Client { message_channel: IoChannel, db_config: &DatabaseConfig, ) -> Result, ClientError> { + let path = path.to_path_buf(); let gb = spec.genesis_block(); @@ -221,7 +227,8 @@ impl Client { accountdb: Default::default(), }; - let client = Client { + let client = Arc::new(Client { + enabled: AtomicBool::new(true), sleep_state: Mutex::new(SleepState::new(awake)), liveness: AtomicBool::new(awake), mode: Mutex::new(config.mode.clone()), @@ -246,8 +253,15 @@ impl Client { history: history, rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))), on_mode_change: Mutex::new(None), - }; - Ok(Arc::new(client)) + registrar: Mutex::new(None), + }); + if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) { + trace!(target: "client", "Found registrar at {}", reg_addr); + let weak = Arc::downgrade(&client); + let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d))); + *client.registrar.lock() = Some(registrar); + } + Ok(client) } /// Adds an actor to be notified on certain events @@ -268,6 +282,11 @@ impl Client { } } + /// Get the Registry object - useful for looking up names. + pub fn registrar(&self) -> MutexGuard> { + self.registrar.lock() + } + /// Register an action to be done if a mode change happens. pub fn on_mode_change(&self, f: F) where F: 'static + FnMut(&Mode) + Send { *self.on_mode_change.lock() = Some(Box::new(f)); @@ -395,6 +414,12 @@ impl Client { /// This is triggered by a message coming from a block queue when the block is ready for insertion pub fn import_verified_blocks(&self) -> usize { + + // Shortcut out if we know we're incapable of syncing the chain. + if !self.enabled.load(AtomicOrdering::Relaxed) { + return 0; + } + let max_blocks_to_import = 4; let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, is_empty) = { let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); @@ -663,10 +688,17 @@ impl Client { /// Tick the client. // TODO: manage by real events. pub fn tick(&self) { + self.check_garbage(); + self.check_snooze(); + } + + fn check_garbage(&self) { self.chain.read().collect_garbage(); self.block_queue.collect_garbage(); self.tracedb.read().collect_garbage(); + } + fn check_snooze(&self) { let mode = self.mode.lock().clone(); match mode { Mode::Dark(timeout) => { @@ -700,16 +732,6 @@ impl Client { } } - /// Look up the block number for the given block ID. - pub fn block_number(&self, id: BlockId) -> Option { - match id { - BlockId::Number(number) => Some(number), - BlockId::Hash(ref hash) => self.chain.read().block_number(hash), - BlockId::Earliest => Some(0), - BlockId::Latest | BlockId::Pending => Some(self.chain.read().best_block_number()), - } - } - /// Take a snapshot at the given block. /// If the ID given is "latest", this will default to 1000 blocks behind. pub fn take_snapshot(&self, writer: W, at: BlockId, p: &snapshot::Progress) -> Result<(), EthcoreError> { @@ -908,8 +930,17 @@ impl BlockChainClient for Client { r } + fn disable(&self) { + self.set_mode(IpcMode::Off); + self.enabled.store(false, AtomicOrdering::Relaxed); + self.clear_queue(); + } + fn set_mode(&self, new_mode: IpcMode) { trace!(target: "mode", "Client::set_mode({:?})", new_mode); + if !self.enabled.load(AtomicOrdering::Relaxed) { + return; + } { let mut mode = self.mode.lock(); *mode = new_mode.clone().into(); @@ -935,6 +966,15 @@ impl BlockChainClient for Client { Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash)) } + fn block_number(&self, id: BlockId) -> Option { + match id { + BlockId::Number(number) => Some(number), + BlockId::Hash(ref hash) => self.chain.read().block_number(hash), + BlockId::Earliest => Some(0), + BlockId::Latest | BlockId::Pending => Some(self.chain.read().best_block_number()), + } + } + fn block_body(&self, id: BlockId) -> Option { let chain = self.chain.read(); Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash)) @@ -1331,6 +1371,34 @@ impl BlockChainClient for Client { earliest_state: self.state_db.lock().journal_db().earliest_era().unwrap_or(0), } } + + fn call_contract(&self, address: Address, data: Bytes) -> Result { + let from = Address::default(); + let transaction = Transaction { + nonce: self.latest_nonce(&from), + action: Action::Call(address), + gas: U256::from(50_000_000), + gas_price: U256::default(), + value: U256::default(), + data: data, + }.fake_sign(from); + + self.call(&transaction, BlockId::Latest, Default::default()) + .map_err(|e| format!("{:?}", e)) + .map(|executed| { + executed.output + }) + } + + fn registrar_address(&self) -> Option
{ + self.registrar.lock().as_ref().map(|r| r.address.clone()) + } + + fn registry_address(&self, name: String) -> Option
{ + self.registrar.lock().as_ref() + .and_then(|r| r.get_address(&(name.as_bytes().sha3()), "A").ok()) + .and_then(|a| if a.is_zero() { None } else { Some(a) }) + } } impl MiningBlockChainClient for Client { diff --git a/ethcore/src/client/error.rs b/ethcore/src/client/error.rs index 8dcc2b773..e532ec7ed 100644 --- a/ethcore/src/client/error.rs +++ b/ethcore/src/client/error.rs @@ -1,3 +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 . + use trace::Error as TraceError; use util::UtilError; use std::fmt::{Display, Formatter, Error as FmtError}; diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 2b64ef628..d8e5f19e5 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -16,6 +16,7 @@ //! Blockchain database client. +mod registry; mod config; mod error; mod test_client; diff --git a/ethcore/src/client/registry.rs b/ethcore/src/client/registry.rs new file mode 100644 index 000000000..7693feaeb --- /dev/null +++ b/ethcore/src/client/registry.rs @@ -0,0 +1,264 @@ +// Autogenerated from JSON contract definition using Rust contract convertor. + +use std::string::String; +use std::result::Result; +use std::fmt; +use {util, ethabi}; +use util::FixedHash; +use util::Uint; + +pub struct Registry { + contract: ethabi::Contract, + pub address: util::Address, + do_call: Box) -> Result, String> + Send + 'static>, +} +impl Registry { + pub fn new(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec) -> Result, String> + Send + 'static { + Registry { + contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"string\"}],\"name\":\"confirmReverse\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"}],\"name\":\"reserve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_key\",\"type\":\"string\"},{\"name\":\"_value\",\"type\":\"bytes32\"}],\"name\":\"set\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"}],\"name\":\"drop\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_key\",\"type\":\"string\"}],\"name\":\"getAddress\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"setFee\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"}],\"name\":\"reserved\",\"outputs\":[{\"name\":\"reserved\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"drain\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"string\"},{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"proposeReverse\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_key\",\"type\":\"string\"}],\"name\":\"getUint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_key\",\"type\":\"string\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"fee\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"}],\"name\":\"getOwner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"reverse\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_key\",\"type\":\"string\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"setUint\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"removeReverse\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_name\",\"type\":\"bytes32\"},{\"name\":\"_key\",\"type\":\"string\"},{\"name\":\"_value\",\"type\":\"address\"}],\"name\":\"setAddress\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Drained\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"Reserved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"oldOwner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"Transferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"Dropped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"key\",\"type\":\"string\"},{\"indexed\":false,\"name\":\"plainKey\",\"type\":\"string\"}],\"name\":\"DataChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"string\"},{\"indexed\":true,\"name\":\"reverse\",\"type\":\"address\"}],\"name\":\"ReverseProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"string\"},{\"indexed\":true,\"name\":\"reverse\",\"type\":\"address\"}],\"name\":\"ReverseConfirmed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"name\",\"type\":\"string\"},{\"indexed\":true,\"name\":\"reverse\",\"type\":\"address\"}],\"name\":\"ReverseRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"old\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"current\",\"type\":\"address\"}],\"name\":\"NewOwner\",\"type\":\"event\"}]").expect("JSON is autogenerated; qed")), + address: address, + do_call: Box::new(do_call), + } + } + fn as_string(e: T) -> String { format!("{:?}", e) } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set_owner(&self, _new: &util::Address) -> Result<(), String> { + let call = self.contract.function("setOwner".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_new.clone().0)] + ).map_err(Self::as_string)?; + call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"confirmReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn confirm_reverse(&self, _name: &str) -> Result { + let call = self.contract.function("confirmReverse".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::String(_name.to_owned())] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn reserve(&self, _name: &util::H256) -> Result { + let call = self.contract.function("reserve".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned())] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"set","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set(&self, _name: &util::H256, _key: &str, _value: &util::H256) -> Result { + let call = self.contract.function("set".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::String(_key.to_owned()), ethabi::Token::FixedBytes(_value.as_ref().to_owned())] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn drop(&self, _name: &util::H256) -> Result { + let call = self.contract.function("drop".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned())] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get_address(&self, _name: &util::H256, _key: &str) -> Result { + let call = self.contract.function("getAddress".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::String(_key.to_owned())] + ).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 = try!(r.to_address().ok_or("Invalid type returned")); util::Address::from(r) })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set_fee(&self, _amount: util::U256) -> Result<(), String> { + let call = self.contract.function("setFee".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Uint({ let mut r = [0u8; 32]; _amount.to_big_endian(&mut r); r })] + ).map_err(Self::as_string)?; + call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_to","type":"address"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn transfer(&self, _name: &util::H256, _to: &util::Address) -> Result { + let call = self.contract.function("transfer".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::Address(_to.clone().0)] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn owner(&self) -> Result { + let call = self.contract.function("owner".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 = try!(r.to_address().ok_or("Invalid type returned")); util::Address::from(r) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn reserved(&self, _name: &util::H256) -> Result { + let call = self.contract.function("reserved".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned())] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[],"name":"drain","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn drain(&self) -> Result<(), String> { + let call = self.contract.function("drain".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![] + ).map_err(Self::as_string)?; + call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"proposeReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn propose_reverse(&self, _name: &str, _who: &util::Address) -> Result { + let call = self.contract.function("proposeReverse".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::String(_name.to_owned()), ethabi::Token::Address(_who.clone().0)] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get_uint(&self, _name: &util::H256, _key: &str) -> Result { + let call = self.contract.function("getUint".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::String(_key.to_owned())] + ).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 = try!(r.to_uint().ok_or("Invalid type returned")); util::U256::from(r.as_ref()) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get(&self, _name: &util::H256, _key: &str) -> Result { + let call = self.contract.function("get".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::String(_key.to_owned())] + ).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 = try!(r.to_fixed_bytes().ok_or("Invalid type returned")); util::H256::from_slice(r.as_ref()) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn fee(&self) -> Result { + let call = self.contract.function("fee".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 = try!(r.to_uint().ok_or("Invalid type returned")); util::U256::from(r.as_ref()) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get_owner(&self, _name: &util::H256) -> Result { + let call = self.contract.function("getOwner".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned())] + ).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 = try!(r.to_address().ok_or("Invalid type returned")); util::Address::from(r) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn reverse(&self, _1: &util::Address) -> Result { + let call = self.contract.function("reverse".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_1.clone().0)] + ).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 = try!(r.to_string().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set_uint(&self, _name: &util::H256, _key: &str, _value: util::U256) -> Result { + let call = self.contract.function("setUint".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::String(_key.to_owned()), ethabi::Token::Uint({ let mut r = [0u8; 32]; _value.to_big_endian(&mut r); r })] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn remove_reverse(&self) -> Result<(), String> { + let call = self.contract.function("removeReverse".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![] + ).map_err(Self::as_string)?; + call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"address"}],"name":"setAddress","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set_address(&self, _name: &util::H256, _key: &str, _value: &util::Address) -> Result { + let call = self.contract.function("setAddress".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::FixedBytes(_name.as_ref().to_owned()), ethabi::Token::String(_key.to_owned()), ethabi::Token::Address(_value.clone().0)] + ).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 = try!(r.to_bool().ok_or("Invalid type returned")); r })) + } +} \ No newline at end of file diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 99134f597..a3344dab7 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -488,6 +488,10 @@ impl BlockChainClient for TestBlockChainClient { self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) } + fn block_number(&self, _id: BlockId) -> Option { + unimplemented!() + } + fn block_body(&self, id: BlockId) -> Option { self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| { let mut stream = RlpStream::new_list(2); @@ -694,10 +698,18 @@ impl BlockChainClient for TestBlockChainClient { fn set_mode(&self, _: Mode) { unimplemented!(); } + fn disable(&self) { unimplemented!(); } + fn pruning_info(&self) -> PruningInfo { PruningInfo { earliest_chain: 1, earliest_state: 1, } } + + fn call_contract(&self, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } + + fn registrar_address(&self) -> Option
{ None } + + fn registry_address(&self, _name: String) -> Option
{ None } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index c91dc0347..4c3b2a37e 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -52,6 +52,9 @@ pub trait BlockChainClient : Sync + Send { /// Get raw block header data by block id. fn block_header(&self, id: BlockId) -> Option; + /// Look up the block number for the given block ID. + fn block_number(&self, id: BlockId) -> Option; + /// Get raw block body data by block id. /// Block body is an RLP list of two items: uncles and transactions. fn block_body(&self, id: BlockId) -> Option; @@ -255,6 +258,10 @@ pub trait BlockChainClient : Sync + Send { /// Set the mode. fn set_mode(&self, mode: Mode); + /// Disable the client from importing blocks. This cannot be undone in this session and indicates + /// that a subsystem has reason to believe this executable incapable of syncing the chain. + fn disable(&self); + /// Returns engine-related extra info for `BlockId`. fn block_extra_info(&self, id: BlockId) -> Option>; @@ -263,6 +270,15 @@ pub trait BlockChainClient : Sync + Send { /// Returns information about pruning/data availability. fn pruning_info(&self) -> PruningInfo; + + /// Like `call`, but with various defaults. Designed to be used for calling contracts. + fn call_contract(&self, address: Address, data: Bytes) -> Result; + + /// Get the address of the registry itself. + fn registrar_address(&self) -> Option
; + + /// Get the address of a particular blockchain service, if available. + fn registry_address(&self, name: String) -> Option
; } impl IpcConfig for BlockChainClient { } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 8d1c004c5..11d77bc78 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -100,6 +100,7 @@ impl AsMillis for Duration { impl AuthorityRound { /// Create a new instance of AuthorityRound engine. pub fn new(params: CommonParams, our_params: AuthorityRoundParams, builtins: BTreeMap) -> Result, Error> { + let should_timeout = our_params.start_step.is_none(); let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / our_params.step_duration.as_secs())) as usize; let engine = Arc::new( AuthorityRound { @@ -113,18 +114,17 @@ impl AuthorityRound { account_provider: Mutex::new(None), password: RwLock::new(None), }); - let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; - try!(engine.transition_service.register_handler(Arc::new(handler))); + // Do not initialize timeouts for tests. + if should_timeout { + let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; + try!(engine.transition_service.register_handler(Arc::new(handler))); + } Ok(engine) } - fn step(&self) -> usize { - self.step.load(AtomicOrdering::SeqCst) - } - fn remaining_step_duration(&self) -> Duration { let now = unix_now(); - let step_end = self.our_params.step_duration * (self.step() as u32 + 1); + let step_end = self.our_params.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1); if step_end > now { step_end - now } else { @@ -228,7 +228,7 @@ impl Engine for AuthorityRound { fn generate_seal(&self, block: &ExecutedBlock) -> Seal { if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; } let header = block.header(); - let step = self.step(); + let step = self.step.load(AtomicOrdering::SeqCst); if self.is_step_proposer(step, header.author()) { if let Some(ref ap) = *self.account_provider.lock() { // Account should be permanently unlocked, otherwise sealing will fail. @@ -265,7 +265,7 @@ impl Engine for AuthorityRound { fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { let header_step = try!(header_step(header)); // Give one step slack if step is lagging, double vote is still not possible. - if header_step <= self.step() + 1 { + if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 { let proposer_signature = try!(header_signature(header)); let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash())); if ok_sig { diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 3a20f1e43..0f7683046 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -29,6 +29,12 @@ pub use self::denominations::*; use super::spec::*; +/// Most recent fork block that we support on Mainnet. +pub const FORK_SUPPORTED_FRONTIER: u64 = 2675000; + +/// Most recent fork block that we support on Ropsten. +pub const FORK_SUPPORTED_ROPSTEN: u64 = 10; + fn load(b: &[u8]) -> Spec { Spec::load(b).expect("chain spec is invalid") } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 244ab932f..df85ca4c7 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -118,6 +118,7 @@ extern crate lru_cache; #[cfg(feature = "jit" )] extern crate evmjit; +extern crate ethabi; pub extern crate ethstore; diff --git a/ethcore/src/types/blockchain_info.rs b/ethcore/src/types/blockchain_info.rs index 6757acf64..33657ff0e 100644 --- a/ethcore/src/types/blockchain_info.rs +++ b/ethcore/src/types/blockchain_info.rs @@ -18,6 +18,7 @@ use util::{U256, H256}; use header::BlockNumber; +use types::security_level::SecurityLevel; /// Information about the blockchain gathered together. #[derive(Clone, Debug, Binary)] @@ -41,3 +42,15 @@ pub struct BlockChainInfo { /// Number of the first block on the best sequence. pub first_block_number: Option, } + +impl BlockChainInfo { + /// Determine the security model for the current state. + pub fn security_level(&self) -> SecurityLevel { + // TODO: Detect SecurityLevel::FullState : https://github.com/ethcore/parity/issues/3834 + if self.ancient_block_number.is_none() || self.first_block_number.is_none() { + SecurityLevel::FullProofOfWork + } else { + SecurityLevel::PartialProofOfWork(self.best_block_number - self.first_block_number.expect("Guard condition means this is not none")) + } + } +} \ No newline at end of file diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index 47a4b6a84..a5a8fb4ef 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -34,4 +34,5 @@ pub mod block_import_error; pub mod restoration_status; pub mod snapshot_manifest; pub mod mode; -pub mod pruning_info; \ No newline at end of file +pub mod pruning_info; +pub mod security_level; \ No newline at end of file diff --git a/ethcore/src/types/security_level.rs b/ethcore/src/types/security_level.rs new file mode 100644 index 000000000..0415b9bb1 --- /dev/null +++ b/ethcore/src/types/security_level.rs @@ -0,0 +1,40 @@ +// 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 . + +//! Indication of how secure the chain is. + +use header::BlockNumber; + +/// Indication of how secure the chain is. +#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq, Binary)] +pub enum SecurityLevel { + /// All blocks from genesis to chain head are known to have valid state transitions and PoW. + FullState, + /// All blocks from genesis to chain head are known to have a valid PoW. + FullProofOfWork, + /// Some recent headers (the argument) are known to have a valid PoW. + PartialProofOfWork(BlockNumber), +} + +impl SecurityLevel { + /// `true` for `FullPoW`/`FullState`. + pub fn is_full(&self) -> bool { + match *self { + SecurityLevel::FullState | SecurityLevel::FullProofOfWork => true, + _ => false, + } + } +} \ No newline at end of file diff --git a/ethcore/hash-fetch/Cargo.toml b/hash-fetch/Cargo.toml similarity index 71% rename from ethcore/hash-fetch/Cargo.toml rename to hash-fetch/Cargo.toml index 3d1a57240..fa98f2fce 100644 --- a/ethcore/hash-fetch/Cargo.toml +++ b/hash-fetch/Cargo.toml @@ -2,7 +2,7 @@ description = "Fetching hash-addressed content." homepage = "http://parity.io" license = "GPL-3.0" -name = "ethcore-hash-fetch" +name = "parity-hash-fetch" version = "1.5.0" authors = ["Parity Technologies "] @@ -11,5 +11,5 @@ log = "0.3" rustc-serialize = "0.3" ethabi = "0.2.2" mime_guess = "1.6.1" -fetch = { path = "../../util/fetch" } -ethcore-util = { path = "../../util" } +fetch = { path = "../util/fetch" } +ethcore-util = { path = "../util" } diff --git a/ethcore/hash-fetch/res/registrar.json b/hash-fetch/res/registrar.json similarity index 100% rename from ethcore/hash-fetch/res/registrar.json rename to hash-fetch/res/registrar.json diff --git a/ethcore/hash-fetch/res/urlhint.json b/hash-fetch/res/urlhint.json similarity index 100% rename from ethcore/hash-fetch/res/urlhint.json rename to hash-fetch/res/urlhint.json diff --git a/ethcore/hash-fetch/src/client.rs b/hash-fetch/src/client.rs similarity index 91% rename from ethcore/hash-fetch/src/client.rs rename to hash-fetch/src/client.rs index b72aa3bc9..57a1b104f 100644 --- a/ethcore/hash-fetch/src/client.rs +++ b/hash-fetch/src/client.rs @@ -26,7 +26,7 @@ use fetch::{Fetch, FetchError, Client as FetchClient}; use urlhint::{ContractClient, URLHintContract, URLHint, URLHintResult}; /// API for fetching by hash. -pub trait HashFetch { +pub trait HashFetch: Send + Sync + 'static { /// Fetch hash-addressed content. /// Parameters: /// 1. `hash` - content hash @@ -42,7 +42,12 @@ pub enum Error { /// Hash could not be resolved to a valid content address. NoResolution, /// Downloaded content hash does not match. - HashMismatch { expected: H256, got: H256 }, + HashMismatch { + /// Expected hash + expected: H256, + /// Computed hash + got: H256, + }, /// IO Error while validating hash. IO(io::Error), /// Error during fetch. @@ -79,7 +84,7 @@ impl Client { impl HashFetch for Client { fn fetch(&self, hash: H256, on_done: Box) + Send>) -> Result<(), Error> { - debug!(target: "dapps", "Fetching: {:?}", hash); + debug!(target: "fetch", "Fetching: {:?}", hash); let url = try!( self.contract.resolve(hash.to_vec()).map(|content| match content { @@ -92,7 +97,7 @@ impl HashFetch for Client { }).ok_or_else(|| Error::NoResolution) ); - debug!(target: "dapps", "Resolved {:?} to {:?}. Fetching...", hash, url); + debug!(target: "fetch", "Resolved {:?} to {:?}. Fetching...", hash, url); self.fetch.lock().request_async(&url, Default::default(), Box::new(move |result| { fn validate_hash(hash: H256, result: Result) -> Result { @@ -107,7 +112,7 @@ impl HashFetch for Client { } } - debug!(target: "dapps", "Content fetched, validating hash ({:?})", hash); + debug!(target: "fetch", "Content fetched, validating hash ({:?})", hash); on_done(validate_hash(hash, result)) })).map_err(Into::into) } diff --git a/ethcore/hash-fetch/src/lib.rs b/hash-fetch/src/lib.rs similarity index 95% rename from ethcore/hash-fetch/src/lib.rs rename to hash-fetch/src/lib.rs index d8d1123d2..e333b6df2 100644 --- a/ethcore/hash-fetch/src/lib.rs +++ b/hash-fetch/src/lib.rs @@ -30,4 +30,4 @@ mod client; pub mod urlhint; -pub use client::{HashFetch, Client}; +pub use client::{HashFetch, Client, Error}; diff --git a/ethcore/hash-fetch/src/urlhint.rs b/hash-fetch/src/urlhint.rs similarity index 100% rename from ethcore/hash-fetch/src/urlhint.rs rename to hash-fetch/src/urlhint.rs diff --git a/ipc-common-types/Cargo.toml b/ipc-common-types/Cargo.toml new file mode 100644 index 000000000..a483edadc --- /dev/null +++ b/ipc-common-types/Cargo.toml @@ -0,0 +1,20 @@ +[package] +description = "Types that implement IPC and are common to multiple modules." +name = "ipc-common-types" +version = "1.5.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] +build = "build.rs" + +[build-dependencies] +ethcore-ipc-codegen = { path = "../ipc/codegen" } + +[dependencies] +log = "0.3" +semver = "0.5" +ethcore-ipc = { path = "../ipc/rpc" } +ethcore-util = { path = "../util" } + +[profile.release] +debug = true +lto = false diff --git a/ipc-common-types/build.rs b/ipc-common-types/build.rs new file mode 100644 index 000000000..a8e3ec1e0 --- /dev/null +++ b/ipc-common-types/build.rs @@ -0,0 +1,21 @@ +// 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 . + +extern crate ethcore_ipc_codegen; + +fn main() { + ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap(); +} diff --git a/ipc-common-types/src/lib.rs b/ipc-common-types/src/lib.rs new file mode 100644 index 000000000..116921312 --- /dev/null +++ b/ipc-common-types/src/lib.rs @@ -0,0 +1,26 @@ +// 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 . + +//! Updater for Parity executables + +#[macro_use] extern crate log; +extern crate semver; +extern crate ethcore_util as util; +extern crate ethcore_ipc as ipc; + +mod types; + +pub use types::*; \ No newline at end of file diff --git a/ipc-common-types/src/types/mod.rs b/ipc-common-types/src/types/mod.rs new file mode 100644 index 000000000..57e33ad3f --- /dev/null +++ b/ipc-common-types/src/types/mod.rs @@ -0,0 +1,21 @@ +// 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 . + +//! Types used in the public api + +#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues +include!(concat!(env!("OUT_DIR"), "/mod.rs.in")); + diff --git a/ipc-common-types/src/types/mod.rs.in b/ipc-common-types/src/types/mod.rs.in new file mode 100644 index 000000000..c1177d636 --- /dev/null +++ b/ipc-common-types/src/types/mod.rs.in @@ -0,0 +1,21 @@ +// 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 . + +pub mod release_track; +pub mod version_info; + +pub use release_track::{ReleaseTrack}; +pub use version_info::{VersionInfo}; \ No newline at end of file diff --git a/ipc-common-types/src/types/release_track.rs b/ipc-common-types/src/types/release_track.rs new file mode 100644 index 000000000..3fcc0bb64 --- /dev/null +++ b/ipc-common-types/src/types/release_track.rs @@ -0,0 +1,82 @@ +// 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 . + +//! Types used in the public API + +use std::fmt; + +/// A release's track. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Binary)] +pub enum ReleaseTrack { + /// Stable track. + Stable, + /// Beta track. + Beta, + /// Nightly track. + Nightly, + /// Testing track. + Testing, + /// No known track, also "current executable's track" when it's not yet known. + Unknown, +} + +impl fmt::Display for ReleaseTrack { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", match *self { + ReleaseTrack::Stable => "stable", + ReleaseTrack::Beta => "beta", + ReleaseTrack::Nightly => "nightly", + ReleaseTrack::Testing => "testing", + ReleaseTrack::Unknown => "unknown", + }) + } +} + +impl<'a> From<&'a str> for ReleaseTrack { + fn from(s: &'a str) -> Self { + match s { + "stable" => ReleaseTrack::Stable, + "beta" => ReleaseTrack::Beta, + "nightly" => ReleaseTrack::Nightly, + "testing" => ReleaseTrack::Testing, + _ => ReleaseTrack::Unknown, + } + } +} + +impl From for ReleaseTrack { + fn from(i: u8) -> Self { + match i { + 1 => ReleaseTrack::Stable, + 2 => ReleaseTrack::Beta, + 3 => ReleaseTrack::Nightly, + 4 => ReleaseTrack::Testing, + _ => ReleaseTrack::Unknown, + } + } +} + +impl Into for ReleaseTrack { + fn into(self) -> u8 { + match self { + ReleaseTrack::Stable => 1, + ReleaseTrack::Beta => 2, + ReleaseTrack::Nightly => 3, + ReleaseTrack::Testing => 4, + ReleaseTrack::Unknown => 0, + } + } +} diff --git a/ipc-common-types/src/types/version_info.rs b/ipc-common-types/src/types/version_info.rs new file mode 100644 index 000000000..cbffbe198 --- /dev/null +++ b/ipc-common-types/src/types/version_info.rs @@ -0,0 +1,68 @@ +// 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 . + +//! Types used in the public API + +use std::fmt; +use semver::{Version}; +use util::{H160}; +use util::misc::raw_package_info; +use release_track::ReleaseTrack; + +/// Version information of a particular release. +#[derive(Debug, Clone, PartialEq, Binary)] +pub struct VersionInfo { + /// The track on which it was released. + pub track: ReleaseTrack, + /// The version. + pub version: Version, + /// The (SHA1?) 160-bit hash of this build's code base. + pub hash: H160, +} + +impl fmt::Display for VersionInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}.{}.{}-{}-{}", self.version.major, self.version.minor, self.version.patch, self.track, self.hash) + } +} + +impl VersionInfo { + /// Get information for this (currently running) binary. + pub fn this() -> Self { + let raw = raw_package_info(); + VersionInfo { + track: raw.0.into(), + version: { let mut v = Version::parse(raw.1).expect("Environment variables are known to be valid; qed"); v.build = vec![]; v.pre = vec![]; v }, + hash: raw.2.into(), + } + } + + /// Compose the information from the provided raw fields. + pub fn from_raw(semver: u32, track: u8, hash: H160) -> Self { + let t = track.into(); + VersionInfo { + version: Version { + major: (semver >> 16) as u64, + minor: ((semver >> 8) & 0xff) as u64, + patch: (semver & 0xff) as u64, + build: vec![], + pre: vec![], + }, + track: t, + hash: hash, + } + } +} diff --git a/ipc/codegen/src/serialization.rs b/ipc/codegen/src/serialization.rs index c7d288010..ed5b490c3 100644 --- a/ipc/codegen/src/serialization.rs +++ b/ipc/codegen/src/serialization.rs @@ -263,7 +263,7 @@ fn binary_expr_struct( let range_ident = builder.id(format!("r{}", index)); let error_message = "Error serializing member: ".to_owned() + &::syntax::print::pprust::expr_to_string(&member_expr); - let error_message_literal = builder.expr().lit().str::<&str>(&error_message); + let _error_message_literal = builder.expr().lit().str::<&str>(&error_message); match raw_ident.as_ref() { "u8" => { @@ -286,7 +286,6 @@ fn binary_expr_struct( post_write_stmts.push(quote_stmt!(cx, if $range_ident.end - $range_ident.start > 0 { if let Err(e) = $member_expr .to_bytes(&mut buffer[$range_ident], length_stack) { - warn!(target: "ipc", $error_message_literal); return Err(e) }; } diff --git a/ipc/hypervisor/Cargo.toml b/ipc/hypervisor/Cargo.toml index abce5a3ec..21955bdf3 100644 --- a/ipc/hypervisor/Cargo.toml +++ b/ipc/hypervisor/Cargo.toml @@ -11,7 +11,7 @@ build = "build.rs" ethcore-ipc = { path = "../rpc" } nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } ethcore-ipc-nano = { path = "../nano" } -semver = "0.2" +semver = "0.5" log = "0.3" time = "0.1" diff --git a/ipc/rpc/Cargo.toml b/ipc/rpc/Cargo.toml index 312433be1..a89d1a9ee 100644 --- a/ipc/rpc/Cargo.toml +++ b/ipc/rpc/Cargo.toml @@ -10,4 +10,4 @@ license = "GPL-3.0" ethcore-devtools = { path = "../../devtools" } nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } ethcore-util = { path = "../../util" } -semver = "0.2" +semver = "0.5" diff --git a/ipc/rpc/src/binary.rs b/ipc/rpc/src/binary.rs index 3c7060260..eed894fce 100644 --- a/ipc/rpc/src/binary.rs +++ b/ipc/rpc/src/binary.rs @@ -804,6 +804,17 @@ binary_fixed_size!(H512); binary_fixed_size!(H2048); binary_fixed_size!(Address); binary_fixed_size!(BinHandshake); +binary_fixed_size!(BinVersion); + +impl BinaryConvertable for ::semver::Version { + fn from_bytes(bytes: &[u8], length_stack: &mut ::std::collections::VecDeque) -> Result { + BinVersion::from_bytes(bytes, length_stack).map(BinVersion::to_semver) + } + + fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut ::std::collections::VecDeque) -> Result<(), BinaryConvertError> { + BinVersion::from(self.clone()).to_bytes(buffer, length_stack) + } +} #[test] fn vec_serialize() { diff --git a/ipc/rpc/src/lib.rs b/ipc/rpc/src/lib.rs index c17acff98..e6586397a 100644 --- a/ipc/rpc/src/lib.rs +++ b/ipc/rpc/src/lib.rs @@ -24,4 +24,4 @@ extern crate ethcore_util as util; pub mod interface; pub mod binary; pub use interface::{IpcInterface, IpcSocket, invoke, IpcConfig, Handshake, Error, WithSocket}; -pub use binary::{BinaryConvertable, BinaryConvertError, BinHandshake}; +pub use binary::{BinaryConvertable, BinaryConvertError, BinVersion, BinHandshake}; diff --git a/ipc/tests/Cargo.toml b/ipc/tests/Cargo.toml index 1f7723a4f..1e29c5c35 100644 --- a/ipc/tests/Cargo.toml +++ b/ipc/tests/Cargo.toml @@ -10,7 +10,7 @@ path = "run.rs" [dependencies] ethcore-ipc = { path = "../rpc" } ethcore-devtools = { path = "../../devtools" } -semver = "0.2" +semver = "0.5" nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } ethcore-ipc-nano = { path = "../nano" } ethcore-util = { path = "../../util" } diff --git a/js/package.json b/js/package.json index d247714ab..6f8693b39 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.126", + "version": "0.2.128", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", @@ -36,7 +36,7 @@ "ci:build:npm": "NODE_ENV=production webpack --config webpack/npm", "start": "npm install && npm run build:lib && npm run build:dll && npm run start:app", "start:app": "node webpack/dev.server", - "clean": "rm -rf ./build ./coverage", + "clean": "rm -rf ./.build ./.coverage ./.happypack ./.npmjs ./build", "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "lint": "npm run lint:css && npm run lint:js", "lint:cached": "npm run lint:css && npm run lint:js:cached", diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index 632ab6f28..ca1d54ede 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -110,7 +110,8 @@ export function outPeers (peers) { return { active: outNumber(peers.active), connected: outNumber(peers.connected), - max: outNumber(peers.max) + max: outNumber(peers.max), + peers: peers.peers.map(p => { Object.keys(p.protocols).forEach(k => { p.protocols[k].difficulty = outNumber(p.protocols[k].difficulty); }); return p; }) }; } diff --git a/js/src/api/format/output.spec.js b/js/src/api/format/output.spec.js index b44047610..1958b57d8 100644 --- a/js/src/api/format/output.spec.js +++ b/js/src/api/format/output.spec.js @@ -147,10 +147,50 @@ describe('api/format/output', () => { describe('outPeers', () => { it('converts all internal numbers to BigNumbers', () => { - expect(outPeers({ active: 789, connected: '456', max: 0x7b })).to.deep.equal({ + expect(outPeers({ + active: 789, + connected: '456', + max: 0x7b, + peers: [ + { + caps: ['par/1'], + id: '0x01', + name: 'Parity', + network: { + localAddress: '10.0.0.1', + remoteAddress: '10.0.0.1' + }, + protocols: { + par: { + difficulty: '0x0f', + head: '0x02', + version: 63 + } + } + } + ] + })).to.deep.equal({ active: new BigNumber(789), connected: new BigNumber(456), - max: new BigNumber(123) + max: new BigNumber(123), + peers: [ + { + caps: ['par/1'], + id: '0x01', + name: 'Parity', + network: { + localAddress: '10.0.0.1', + remoteAddress: '10.0.0.1' + }, + protocols: { + par: { + difficulty: new BigNumber(15), + head: '0x02', + version: 63 + } + } + } + ] }); }); }); diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index 2c1d04d93..2605e41e5 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -54,6 +54,11 @@ export default class Parity { .execute('parity_checkRequest', inNumber16(requestId)); } + consensusCapability () { + return this._transport + .execute('parity_consensusCapability'); + } + dappsPort () { return this._transport .execute('parity_dappsPort') @@ -90,6 +95,11 @@ export default class Parity { .execute('parity_enode'); } + executeUpgrade () { + return this._transport + .execute('parity_executeUpgrade'); + } + extraData () { return this._transport .execute('parity_extraData'); @@ -243,6 +253,11 @@ export default class Parity { .then(outAddress); } + releasesInfo () { + return this._transport + .execute('parity_releasesInfo'); + } + removeReservedPeer (encode) { return this._transport .execute('parity_removeReservedPeer', encode); @@ -315,4 +330,14 @@ export default class Parity { .execute('parity_unsignedTransactionsCount') .then(outNumber); } + + upgradeReady () { + return this._transport + .execute('parity_upgradeReady'); + } + + versionInfo () { + return this._transport + .execute('parity_versionInfo'); + } } diff --git a/js/src/api/rpc/parity/parity.spec.js b/js/src/api/rpc/parity/parity.spec.js index d0d97cd0b..b58c8f85c 100644 --- a/js/src/api/rpc/parity/parity.spec.js +++ b/js/src/api/rpc/parity/parity.spec.js @@ -80,7 +80,7 @@ describe('api/rpc/parity', () => { describe('newPeers', () => { it('returns the peer structure, formatted', () => { - mockHttp([{ method: 'parity_netPeers', reply: { result: { active: 123, connected: 456, max: 789 } } }]); + mockHttp([{ method: 'parity_netPeers', reply: { result: { active: 123, connected: 456, max: 789, peers: [] } } }]); return instance.netPeers().then((peers) => { expect(peers.active.eq(123)).to.be.true; diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index bab9ad6e3..3f36a93b3 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -100,6 +100,15 @@ export default { } }, + consensusCapability: { + desc: 'Returns an object or string detailing the state of parity capability of maintaining consensus', + params: [], + returns: { + type: Object, + desc: 'Either "capable", {"capableUntil":N}, {"incapableSince":N} or "unknown" (N is a block number)' + } + }, + dappsPort: { desc: 'Returns the port the dapps are running on, error if not enabled', params: [], @@ -163,6 +172,15 @@ export default { } }, + executeUpgrade: { + desc: 'Performs an upgrade', + params: [], + returns: { + type: Boolean, + desc: 'returns true if the upgrade to the release specified in parity_upgradeReady was successfully executed, false if not' + } + }, + extraData: { desc: 'Returns currently set extra data', params: [], @@ -468,6 +486,15 @@ export default { } }, + releasesInfo: { + desc: 'returns a ReleasesInfo object describing the current status of releases', + params: [], + returns: { + type: Object, + desc: '"fork":N,"minor":null,"this_fork":MN,"track":R} (N is a block number representing the latest known fork of this chain which may be in the future, MN is a block number representing the latest known fork that the currently running binary can sync past or null if not known, R is a ReleaseInfo object describing the latest release in this release track)' + } + }, + removeReservedPeer: { desc: '?', params: [ @@ -651,5 +678,23 @@ export default { type: Quantity, desc: 'Number of unsigned transactions' } + }, + + upgradeReady: { + desc: 'returns a ReleaseInfo object describing the release which is available for upgrade or null if none is available', + params: [], + returns: { + type: Object, + desc: '{"binary":H,"fork":15100,"is_critical":true,"version":V} where H is the Keccak-256 checksum of the release parity binary and V is a VersionInfo object describing the release' + } + }, + + versionInfo: { + desc: 'returns a VersionInfo object describing our current version', + params: [], + returns: { + type: Object, + desc: '{"hash":H,"track":T,"version":{"major":N,"minor":N,"patch":N}} (H is a 160-bit Git commit hash, T is a ReleaseTrack, either "stable", "beta", "nightly" or "unknown" and N is a version number)' + } } }; diff --git a/js/src/modals/UpgradeParity/index.js b/js/src/modals/UpgradeParity/index.js new file mode 100644 index 000000000..523e372fa --- /dev/null +++ b/js/src/modals/UpgradeParity/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './upgradeParity'; diff --git a/js/src/modals/UpgradeParity/store.js b/js/src/modals/UpgradeParity/store.js new file mode 100644 index 000000000..d7a4efd3e --- /dev/null +++ b/js/src/modals/UpgradeParity/store.js @@ -0,0 +1,146 @@ +// 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 { action, computed, observable, transaction } from 'mobx'; +import store from 'store'; + +const LS_UPDATE = '_parity::update'; + +const A_MINUTE = 60 * 1000; +const A_DAY = 24 * 60 * A_MINUTE; + +const STEP_INFO = 0; +const STEP_UPDATING = 1; +const STEP_COMPLETED = 2; +const STEP_ERROR = 2; + +const CHECK_INTERVAL = 1 * A_MINUTE; + +export default class Store { + @observable available = null; + @observable consensusCapability = null; + @observable closed = true; + @observable error = null; + @observable remindAt = 0; + @observable step = 0; + @observable upgrading = null; + @observable version = null; + + constructor (api) { + this._api = api; + + this.loadStorage(); + this.checkUpgrade(); + + setInterval(this.checkUpgrade, CHECK_INTERVAL); + } + + @computed get isVisible () { + return !this.closed && Date.now() >= this.remindAt; + } + + @action closeModal = () => { + transaction(() => { + this.closed = true; + this.setStep(0, null); + }); + } + + @action loadStorage = () => { + const values = store.get(LS_UPDATE) || {}; + + this.remindAt = values.remindAt ? values.remindAt : 0; + + return values; + } + + @action openModal = () => { + this.closed = false; + } + + @action setStep = (step, error = null) => { + transaction(() => { + this.error = error; + this.step = step; + }); + } + + @action setUpgrading () { + transaction(() => { + this.upgrading = this.available; + this.setStep(STEP_UPDATING, null); + }); + } + + @action setVersions (available, version, consensusCapability) { + transaction(() => { + this.available = available; + this.consensusCapability = consensusCapability; + this.version = version; + }); + } + + @action snoozeTillTomorrow = () => { + this.remindAt = Date.now() + A_DAY; + store.set(LS_UPDATE, Object.assign(this.loadStorage(), { remindAt: this.remindAt })); + } + + @action upgradeNow = () => { + this.setUpgrading(); + + return this._api.parity + .executeUpgrade() + .then((result) => { + if (!result) { + throw new Error('Unable to complete update'); + } + + this.setStep(STEP_COMPLETED, null); + }) + .catch((error) => { + console.error('upgradeNow', error); + + this.setStep(STEP_ERROR, error); + }); + } + + checkUpgrade = () => { + if (!this._api) { + return; + } + + Promise + .all([ + this._api.parity.upgradeReady(), + this._api.parity.consensusCapability(), + this._api.parity.versionInfo() + ]) + .then(([available, consensusCapability, version]) => { + console.log('[checkUpgrade]', 'available:', available, 'version:', version, 'consensusCapability:', consensusCapability); + this.setVersions(available, version, consensusCapability); + }) + .catch((error) => { + console.warn('checkUpgrade', error); + }); + } +} + +export { + STEP_COMPLETED, + STEP_ERROR, + STEP_INFO, + STEP_UPDATING +}; diff --git a/js/src/modals/UpgradeParity/store.spec.js b/js/src/modals/UpgradeParity/store.spec.js new file mode 100644 index 000000000..1e0111284 --- /dev/null +++ b/js/src/modals/UpgradeParity/store.spec.js @@ -0,0 +1,58 @@ +// 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 Store from './store'; + +let store; + +describe('modals/UpgradeParity/store', () => { + describe('@actions', () => { + beforeEach(() => { + store = new Store(); + }); + + describe('openModal & closeModal', () => { + it('toggles between the closed states', () => { + expect(store.closed).to.be.true; + store.openModal(); + expect(store.closed).to.be.false; + store.closeModal(); + expect(store.closed).to.be.true; + }); + + it('resets the step state upon closing', () => { + store.setStep(5, 'soem error'); + store.closeModal(); + expect(store.step).to.equal(0); + expect(store.error).to.be.null; + }); + }); + + describe('setStep', () => { + it('sets the step as provided', () => { + expect(store.step).to.equal(0); + store.setStep(3); + expect(store.step).to.equal(3); + }); + + it('sets the error when provided', () => { + expect(store.error).to.be.null; + store.setStep(3, new Error('some error')); + expect(store.error).to.match(/some error/); + }); + }); + }); +}); diff --git a/js/src/modals/UpgradeParity/upgradeParity.css b/js/src/modals/UpgradeParity/upgradeParity.css new file mode 100644 index 000000000..bca0fe47d --- /dev/null +++ b/js/src/modals/UpgradeParity/upgradeParity.css @@ -0,0 +1,32 @@ +/* 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 . +*/ + +.error { + padding-top: 1.25em; +} + +.infoStep { + div+div { + padding-top: 1.25em; + } +} + +.version { + display: inline; + opacity: 0.5; + white-space: normal; +} diff --git a/js/src/modals/UpgradeParity/upgradeParity.js b/js/src/modals/UpgradeParity/upgradeParity.js new file mode 100644 index 000000000..a11e1a1fe --- /dev/null +++ b/js/src/modals/UpgradeParity/upgradeParity.js @@ -0,0 +1,257 @@ +// 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 { observer } from 'mobx-react'; +import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { Button } from '~/ui'; +import { CancelIcon, DoneIcon, NextIcon } from '~/ui/Icons'; +import Modal, { Busy, Completed } from '~/ui/Modal'; + +import { STEP_COMPLETED, STEP_ERROR, STEP_INFO, STEP_UPDATING } from './store'; +import styles from './upgradeParity.css'; + +@observer +export default class UpgradeParity extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + + static propTypes = { + store: PropTypes.object.isRequired + } + + render () { + const { store } = this.props; + + if (!store.isVisible) { + return null; + } + + return ( + , + , + store.step === STEP_ERROR + ? + : + ] } + visible> + { this.renderStep() } + + ); + } + + renderActions () { + const { store } = this.props; + + const closeButton = +