Merge branch 'master' into auth-round-no-mocknet

This commit is contained in:
keorn 2016-10-26 17:50:04 +01:00
commit 3ff26dd544
261 changed files with 3793 additions and 1842 deletions

View File

@ -5,6 +5,7 @@ variables:
GIT_DEPTH: "3" GIT_DEPTH: "3"
SIMPLECOV: "true" SIMPLECOV: "true"
RUST_BACKTRACE: "1" RUST_BACKTRACE: "1"
RUSTFLAGS: "-D warnings"
cache: cache:
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME" key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
untracked: true untracked: true
@ -20,10 +21,17 @@ linux-stable:
- cargo build --release --verbose - cargo build --release --verbose
- strip target/release/parity - strip target/release/parity
- md5sum target/release/parity >> parity.md5 - md5sum target/release/parity >> parity.md5
- sh scripts/deb-build.sh amd64
- cp target/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"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5 - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
tags: tags:
- rust - rust
- rust-stable - rust-stable
@ -43,10 +51,17 @@ linux-stable-14.04:
- cargo build --release --verbose - cargo build --release --verbose
- strip target/release/parity - strip target/release/parity
- md5sum target/release/parity >> parity.md5 - md5sum target/release/parity >> parity.md5
- sh scripts/deb-build.sh amd64
- cp target/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"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity --body target/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity --body target/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity.md5 --body parity.md5 - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
tags: tags:
- rust - rust
- rust-14.04 - rust-14.04
@ -133,10 +148,17 @@ linux-armv7:
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose - cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity - arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> parity.md5 - md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> parity.md5
- sh scripts/deb-build.sh armhf
- cp target/armv7-unknown-linux-gnueabihf/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"_armhf.deb"
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5 - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
tags: tags:
- rust - rust
- rust-arm - rust-arm
@ -161,10 +183,17 @@ linux-arm:
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose - cargo build --target arm-unknown-linux-gnueabihf --release --verbose
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity - arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> parity.md5 - md5sum target/arm-unknown-linux-gnueabihf/release/parity >> parity.md5
- sh scripts/deb-build.sh armhf
- cp target/arm-unknown-linux-gnueabihf/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"_armhf.deb"
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5 - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
tags: tags:
- rust - rust
- rust-arm - rust-arm
@ -217,10 +246,17 @@ linux-aarch64:
- cargo build --target aarch64-unknown-linux-gnu --release --verbose - cargo build --target aarch64-unknown-linux-gnu --release --verbose
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity - aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> parity.md5 - md5sum target/aarch64-unknown-linux-gnu/release/parity >> parity.md5
- sh scripts/deb-build.sh arm64
- cp target/aarch64-unknown-linux-gnu/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"_arm64.deb"
- md5sum "parity_"$VER"_arm64.deb" >> "parity_"$VER"_arm64.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5 - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
tags: tags:
- rust - rust
- rust-arm - rust-arm
@ -262,7 +298,6 @@ windows:
- set RUST_BACKTRACE=1 - set RUST_BACKTRACE=1
- set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off -D warnings - set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off -D warnings
- rustup default stable-x86_64-pc-windows-msvc - rustup default stable-x86_64-pc-windows-msvc
- git submodule update --init
- cargo build --release --verbose - cargo build --release --verbose
- 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/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 - curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
@ -298,7 +333,6 @@ windows:
- target/release/parity.pdb - target/release/parity.pdb
- nsis/InstallParity.exe - nsis/InstallParity.exe
name: "x86_64-pc-windows-msvc_parity" name: "x86_64-pc-windows-msvc_parity"
allow_failure: true
test-linux: test-linux:
stage: test stage: test
before_script: before_script:
@ -314,7 +348,6 @@ js-release:
stage: build stage: build
image: ethcore/javascript:latest image: ethcore/javascript:latest
only: only:
- js
- master - master
- beta - beta
- tags - tags

3
.gitmodules vendored
View File

@ -2,6 +2,3 @@
path = ethcore/res/ethereum/tests path = ethcore/res/ethereum/tests
url = https://github.com/ethereum/tests.git url = https://github.com/ethereum/tests.git
branch = develop branch = develop
[submodule "js/build"]
path = js/build
url = https://github.com/ethcore/js-precompiled

73
Cargo.lock generated
View File

@ -222,8 +222,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "elastic-array" name = "elastic-array"
version = "0.5.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/ethcore/elastic-array#70e4012e691b732c7c4cb04e9232799e6aa268bc"
dependencies = [
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
@ -328,6 +331,7 @@ name = "ethcore-dapps"
version = "1.4.0" version = "1.4.0"
dependencies = [ dependencies = [
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.90 (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)", "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-rpc 1.4.0", "ethcore-rpc 1.4.0",
@ -340,9 +344,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.4.0",
"parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
"parity-ui 1.4.0", "parity-ui 1.4.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -509,7 +511,7 @@ dependencies = [
"ethcore-util 1.4.0", "ethcore-util 1.4.0",
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.4.0", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-ui 1.4.0", "parity-ui 1.4.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -541,7 +543,7 @@ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-bigint 0.1.1", "ethcore-bigint 0.1.1",
@ -614,6 +616,7 @@ dependencies = [
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -813,7 +816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "json-ipc-server" name = "json-ipc-server"
version = "0.2.4" version = "0.2.4"
source = "git+https://github.com/ethcore/json-ipc-server.git#5fbd0253750d3097b9a8fb27effa84c18d630bbb" source = "git+https://github.com/ethcore/json-ipc-server.git#4642cd03ec1d23db89df80d22d5a88e7364ab885"
dependencies = [ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -854,7 +857,7 @@ dependencies = [
[[package]] [[package]]
name = "jsonrpc-http-server" name = "jsonrpc-http-server"
version = "6.1.1" version = "6.1.1"
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#ee72e4778583daf901b5692468fc622f46abecb6" source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5"
dependencies = [ dependencies = [
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1175,23 +1178,10 @@ name = "owning_ref"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "parity-dapps"
version = "1.4.0"
source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
dependencies = [
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "parity-dapps-glue" name = "parity-dapps-glue"
version = "1.4.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1202,19 +1192,28 @@ dependencies = [
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "parity-dapps-home"
version = "1.4.0"
source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
dependencies = [
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
]
[[package]] [[package]]
name = "parity-ui" name = "parity-ui"
version = "1.4.0" version = "1.4.0"
dependencies = [ dependencies = [
"parity-dapps-glue 1.4.0", "parity-ui-dev 1.4.0",
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-ui-dev"
version = "1.4.0"
dependencies = [
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/ethcore/js-precompiled.git#9f8baa9d0e54056c41a842b351597d0565beda98"
dependencies = [
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1425,7 +1424,7 @@ dependencies = [
name = "rlp" name = "rlp"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
"ethcore-bigint 0.1.1", "ethcore-bigint 0.1.1",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1884,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "ws" name = "ws"
version = "0.5.2" version = "0.5.2"
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#609b21fdab96c8fffedec8699755ce3bea9454cb" source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#00bd2134b07b4bc8ea47b7f6c7afce16bbe34c8f"
dependencies = [ dependencies = [
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)", "bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1958,7 +1957,7 @@ dependencies = [
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76" "checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" "checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
"checksum elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bc9250a632e7c001b741eb0ec6cee93c9a5b6d5f1879696a4b94d62b012210a" "checksum elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
"checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f" "checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f"
@ -2016,8 +2015,8 @@ dependencies = [
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77" "checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1" "checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7" "checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>" "checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab"
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>" "checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "<none>"
"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6" "checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6"
"checksum parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc5847584161f273e69edc63c1a86254a22f570a0b5dd87aa6f9773f6f7d125" "checksum parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc5847584161f273e69edc63c1a86254a22f570a0b5dd87aa6f9773f6f7d125"
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068" "checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"

View File

@ -26,7 +26,11 @@ lazy_static = "0.2"
regex = "0.1" regex = "0.1"
isatty = "0.1" isatty = "0.1"
toml = "0.2" toml = "0.2"
serde = "0.8.0"
serde_json = "0.8.0"
hyper = { version = "0.9", default-features = false }
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
fdlimit = { path = "util/fdlimit" } fdlimit = { path = "util/fdlimit" }
ethcore = { path = "ethcore" } ethcore = { path = "ethcore" }
ethcore-util = { path = "util" } ethcore-util = { path = "util" }
@ -40,12 +44,9 @@ ethcore-ipc = { path = "ipc/rpc" }
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" } ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
ethcore-logger = { path = "logger" } ethcore-logger = { path = "logger" }
rlp = { path = "util/rlp" } rlp = { path = "util/rlp" }
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } ethcore-stratum = { path = "stratum" }
ethcore-dapps = { path = "dapps", optional = true } ethcore-dapps = { path = "dapps", optional = true }
clippy = { version = "0.0.90", optional = true} clippy = { version = "0.0.90", optional = true}
ethcore-stratum = { path = "stratum" }
serde = "0.8.0"
serde_json = "0.8.0"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = "0.2" winapi = "0.2"
@ -53,19 +54,26 @@ winapi = "0.2"
[target.'cfg(not(windows))'.dependencies] [target.'cfg(not(windows))'.dependencies]
daemonize = "0.2" daemonize = "0.2"
[dependencies.hyper]
version = "0.9"
default-features = false
[features] [features]
default = ["ui", "use-precompiled-js"] default = ["ui-precompiled"]
ui = ["dapps", "ethcore-signer/ui"]
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"] ui = [
"dapps",
"ethcore-dapps/ui",
"ethcore-signer/ui",
]
ui-precompiled = [
"dapps",
"ethcore-signer/ui-precompiled",
"ethcore-dapps/ui-precompiled",
]
dapps = ["ethcore-dapps"] dapps = ["ethcore-dapps"]
ipc = ["ethcore/ipc", "ethsync/ipc"] ipc = ["ethcore/ipc", "ethsync/ipc"]
jit = ["ethcore/jit"] jit = ["ethcore/jit"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"] dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
json-tests = ["ethcore/json-tests"] json-tests = ["ethcore/json-tests"]
test-heavy = ["ethcore/test-heavy"]
stratum = ["ipc"] stratum = ["ipc"]
ethkey-cli = ["ethcore/ethkey-cli"] ethkey-cli = ["ethcore/ethkey-cli"]
ethstore-cli = ["ethcore/ethstore-cli"] ethstore-cli = ["ethcore/ethstore-cli"]

View File

@ -11,6 +11,7 @@ build = "build.rs"
[dependencies] [dependencies]
rand = "0.3.14" rand = "0.3.14"
log = "0.3" log = "0.3"
env_logger = "0.3"
jsonrpc-core = "3.0" jsonrpc-core = "3.0"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
@ -19,21 +20,17 @@ url = "1.0"
rustc-serialize = "0.3" rustc-serialize = "0.3"
serde = "0.8" serde = "0.8"
serde_json = "0.8" serde_json = "0.8"
serde_macros = { version = "0.8", optional = true }
zip = { version = "0.1", default-features = false }
ethabi = "0.2.2" ethabi = "0.2.2"
linked-hash-map = "0.3" linked-hash-map = "0.3"
parity-dapps-glue = "1.4"
mime = "0.2"
serde_macros = { version = "0.8", optional = true }
zip = { version = "0.1", default-features = false }
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
ethcore-rpc = { path = "../rpc" } ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
fetch = { path = "../util/fetch" } fetch = { path = "../util/fetch" }
parity-ui = { path = "./ui" } parity-ui = { path = "./ui" }
parity-dapps-glue = { path = "./js-glue" }
mime = "0.2"
### DEPRECATED
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
### /DEPRECATED
mime_guess = { version = "1.6.1" } mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.90", optional = true} clippy = { version = "0.0.90", optional = true}
@ -46,7 +43,5 @@ default = ["serde_codegen"]
nightly = ["serde_macros"] nightly = ["serde_macros"]
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"] dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
use-precompiled-js = [ ui = ["parity-ui/no-precompiled-js"]
"parity-ui/use-precompiled-js", ui-precompiled = ["parity-ui/use-precompiled-js"]
"parity-dapps-home/use-precompiled-js",
]

View File

@ -53,11 +53,11 @@ fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
pub fn test(_path: &str) { pub fn test(_path: &str) {
} }
#[cfg(feature = "use-precompiled-js")] #[cfg(feature = "use-precompiled-js")]
pub fn build(_path: &str) { pub fn build(_path: &str, _dest: &str) {
} }
#[cfg(not(feature = "use-precompiled-js"))] #[cfg(not(feature = "use-precompiled-js"))]
pub fn build(path: &str) { pub fn build(path: &str, dest: &str) {
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD)) let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
.arg("install") .arg("install")
.arg("--no-progress") .arg("--no-progress")
@ -70,7 +70,7 @@ pub fn build(path: &str) {
.arg("run") .arg("run")
.arg("build") .arg("build")
.env("NODE_ENV", "production") .env("NODE_ENV", "production")
.env("BUILD_DEST", "build") .env("BUILD_DEST", dest)
.current_dir(path) .current_dir(path)
.status() .status()
.unwrap_or_else(|e| die("Building JS code", e)); .unwrap_or_else(|e| die("Building JS code", e));

View File

@ -15,24 +15,31 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use unicase::UniCase;
use hyper::{server, net, Decoder, Encoder, Next, Control}; use hyper::{server, net, Decoder, Encoder, Next, Control};
use hyper::header;
use hyper::method::Method;
use hyper::header::AccessControlAllowOrigin;
use api::types::{App, ApiError}; use api::types::{App, ApiError};
use api::response::{as_json, as_json_error, ping_response}; use api::response;
use apps::fetcher::ContentFetcher;
use handlers::extract_url; use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use apps::fetcher::ContentFetcher; use jsonrpc_http_server::cors;
#[derive(Clone)] #[derive(Clone)]
pub struct RestApi { pub struct RestApi {
local_domain: String, cors_domains: Option<Vec<AccessControlAllowOrigin>>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
fetcher: Arc<ContentFetcher>, fetcher: Arc<ContentFetcher>,
} }
impl RestApi { impl RestApi {
pub fn new(local_domain: String, endpoints: Arc<Endpoints>, fetcher: Arc<ContentFetcher>) -> Box<Endpoint> { pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<ContentFetcher>) -> Box<Endpoint> {
Box::new(RestApi { Box::new(RestApi {
local_domain: local_domain, cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
endpoints: endpoints, endpoints: endpoints,
fetcher: fetcher, fetcher: fetcher,
}) })
@ -53,6 +60,7 @@ impl Endpoint for RestApi {
struct RestApiRouter { struct RestApiRouter {
api: RestApi, api: RestApi,
origin: Option<String>,
path: Option<EndpointPath>, path: Option<EndpointPath>,
control: Option<Control>, control: Option<Control>,
handler: Box<Handler>, handler: Box<Handler>,
@ -62,9 +70,10 @@ impl RestApiRouter {
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self { fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
RestApiRouter { RestApiRouter {
path: Some(path), path: Some(path),
origin: None,
control: Some(control), control: Some(control),
api: api, api: api,
handler: as_json_error(&ApiError { handler: response::as_json_error(&ApiError {
code: "404".into(), code: "404".into(),
title: "Not Found".into(), title: "Not Found".into(),
detail: "Resource you requested has not been found.".into(), detail: "Resource you requested has not been found.".into(),
@ -80,11 +89,40 @@ impl RestApiRouter {
_ => None _ => None
} }
} }
/// Returns basic headers for a response (it may be overwritten by the handler)
fn response_headers(&self) -> header::Headers {
let mut headers = header::Headers::new();
headers.set(header::AccessControlAllowCredentials);
headers.set(header::AccessControlAllowMethods(vec![
Method::Options,
Method::Post,
Method::Get,
]));
headers.set(header::AccessControlAllowHeaders(vec![
UniCase("origin".to_owned()),
UniCase("content-type".to_owned()),
UniCase("accept".to_owned()),
]));
if let Some(cors_header) = cors::get_cors_header(&self.api.cors_domains, &self.origin) {
headers.set(cors_header);
}
headers
}
} }
impl server::Handler<net::HttpStream> for RestApiRouter { impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next { fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
self.origin = cors::read_origin(&request);
if let Method::Options = *request.method() {
self.handler = response::empty();
return Next::write();
}
let url = extract_url(&request); let url = extract_url(&request);
if url.is_none() { if url.is_none() {
// Just return 404 if we can't parse URL // Just return 404 if we can't parse URL
@ -99,11 +137,11 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
let hash = url.path.get(2).map(|v| v.as_str()); let hash = url.path.get(2).map(|v| v.as_str());
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise // at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
// we will try and retrieve 'api' as the hash when doing the /api/content route // we will try and retrieve 'api' as the hash when doing the /api/content route
if let Some(hash) = hash.clone() { path.app_id = hash.to_owned() } if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
let handler = endpoint.and_then(|v| match v { let handler = endpoint.and_then(|v| match v {
"apps" => Some(as_json(&self.api.list_apps())), "apps" => Some(response::as_json(&self.api.list_apps())),
"ping" => Some(ping_response(&self.api.local_domain)), "ping" => Some(response::ping()),
"content" => self.resolve_content(hash, path, control), "content" => self.resolve_content(hash, path, control),
_ => None _ => None
}); });
@ -121,6 +159,7 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
} }
fn on_response(&mut self, res: &mut server::Response) -> Next { fn on_response(&mut self, res: &mut server::Response) -> Next {
*res.headers_mut() = self.response_headers();
self.handler.on_response(res) self.handler.on_response(res)
} }

0
dapps/src/api/cors.rs Normal file
View File

View File

@ -19,6 +19,10 @@ use serde_json;
use endpoint::Handler; use endpoint::Handler;
use handlers::{ContentHandler, EchoHandler}; use handlers::{ContentHandler, EchoHandler};
pub fn empty() -> Box<Handler> {
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
}
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> { pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
let json = serde_json::to_string(val) let json = serde_json::to_string(val)
.expect("serialization to string is infallible; qed"); .expect("serialization to string is infallible; qed");
@ -31,10 +35,6 @@ pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
Box::new(ContentHandler::not_found(json, mime!(Application/Json))) Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
} }
pub fn ping_response(local_domain: &str) -> Box<Handler> { pub fn ping() -> Box<Handler> {
Box::new(EchoHandler::cors(vec![ Box::new(EchoHandler::default())
format!("http://{}", local_domain),
// Allow CORS calls also for localhost
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
]))
} }

View File

@ -66,10 +66,10 @@ impl ContentCache {
}, },
ContentStatus::Ready(ref endpoint) => { ContentStatus::Ready(ref endpoint) => {
trace!(target: "dapps", "Removing {} because of limit.", entry.0); trace!(target: "dapps", "Removing {} because of limit.", entry.0);
// Remove path // Remove path (dir or file)
let res = fs::remove_dir_all(&endpoint.path()); let res = fs::remove_dir_all(&endpoint.path()).or_else(|_| fs::remove_file(&endpoint.path()));
if let Err(e) = res { if let Err(e) = res {
warn!(target: "dapps", "Unable to remove dapp: {:?}", e); warn!(target: "dapps", "Unable to remove dapp/content from cache: {:?}", e);
} }
} }
} }

View File

@ -46,6 +46,7 @@ pub struct ContentFetcher<R: URLHint = URLHintContract> {
resolver: R, resolver: R,
cache: Arc<Mutex<ContentCache>>, cache: Arc<Mutex<ContentCache>>,
sync: Arc<SyncStatus>, sync: Arc<SyncStatus>,
embeddable_at: Option<u16>,
} }
impl<R: URLHint> Drop for ContentFetcher<R> { impl<R: URLHint> Drop for ContentFetcher<R> {
@ -57,7 +58,7 @@ impl<R: URLHint> Drop for ContentFetcher<R> {
impl<R: URLHint> ContentFetcher<R> { impl<R: URLHint> ContentFetcher<R> {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>) -> Self { pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_at: Option<u16>) -> Self {
let mut dapps_path = env::temp_dir(); let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename()); dapps_path.push(random_filename());
@ -66,9 +67,19 @@ impl<R: URLHint> ContentFetcher<R> {
resolver: resolver, resolver: resolver,
sync: sync_status, sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())), cache: Arc::new(Mutex::new(ContentCache::default())),
embeddable_at: embeddable_at,
} }
} }
fn still_syncing() -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Sync In Progress",
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>")
))
}
#[cfg(test)] #[cfg(test)]
fn set_status(&self, content_id: &str, status: ContentStatus) { fn set_status(&self, content_id: &str, status: ContentStatus) {
self.cache.lock().insert(content_id.to_owned(), status); self.cache.lock().insert(content_id.to_owned(), status);
@ -84,12 +95,10 @@ impl<R: URLHint> ContentFetcher<R> {
} }
// fallback to resolver // fallback to resolver
if let Ok(content_id) = content_id.from_hex() { if let Ok(content_id) = content_id.from_hex() {
// if app_id is valid, but we are syncing always return true.
if self.sync.is_major_importing() {
return true;
}
// else try to resolve the app_id // else try to resolve the app_id
self.resolver.resolve(content_id).is_some() let has_content = self.resolver.resolve(content_id).is_some();
// if there is content or we are syncing return true
has_content || self.sync.is_major_importing()
} else { } else {
false false
} }
@ -99,28 +108,19 @@ impl<R: URLHint> ContentFetcher<R> {
let mut cache = self.cache.lock(); let mut cache = self.cache.lock();
let content_id = path.app_id.clone(); let content_id = path.app_id.clone();
if self.sync.is_major_importing() {
return Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Sync In Progress",
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>")
));
}
let (new_status, handler) = { let (new_status, handler) = {
let status = cache.get(&content_id); let status = cache.get(&content_id);
match status { match status {
// Just server dapp // Just serve the content
Some(&mut ContentStatus::Ready(ref endpoint)) => { Some(&mut ContentStatus::Ready(ref endpoint)) => {
(None, endpoint.to_async_handler(path, control)) (None, endpoint.to_async_handler(path, control))
}, },
// App is already being fetched // Content is already being fetched
Some(&mut ContentStatus::Fetching(ref fetch_control)) => { Some(&mut ContentStatus::Fetching(ref fetch_control)) => {
trace!(target: "dapps", "Content fetching in progress. Waiting..."); trace!(target: "dapps", "Content fetching in progress. Waiting...");
(None, fetch_control.to_handler(control)) (None, fetch_control.to_handler(control))
}, },
// We need to start fetching app // We need to start fetching the content
None => { None => {
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id); trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true."); let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
@ -141,6 +141,10 @@ impl<R: URLHint> ContentFetcher<R> {
}; };
match content { match content {
// Don't serve dapps if we are still syncing (but serve content)
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
(None, Self::still_syncing())
},
Some(URLHintResult::Dapp(dapp)) => { Some(URLHintResult::Dapp(dapp)) => {
let (handler, fetch_control) = ContentFetcherHandler::new( let (handler, fetch_control) = ContentFetcherHandler::new(
dapp.url(), dapp.url(),
@ -150,6 +154,7 @@ impl<R: URLHint> ContentFetcher<R> {
id: content_id.clone(), id: content_id.clone(),
dapps_path: self.dapps_path.clone(), dapps_path: self.dapps_path.clone(),
on_done: Box::new(on_done), on_done: Box::new(on_done),
embeddable_at: self.embeddable_at,
} }
); );
@ -170,6 +175,9 @@ impl<R: URLHint> ContentFetcher<R> {
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>) (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
}, },
None if self.sync.is_major_importing() => {
(None, Self::still_syncing())
},
None => { None => {
// This may happen when sync status changes in between // This may happen when sync status changes in between
// `contains` and `to_handler` // `contains` and `to_handler`
@ -271,6 +279,7 @@ struct DappInstaller {
id: String, id: String,
dapps_path: PathBuf, dapps_path: PathBuf,
on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>, on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>,
embeddable_at: Option<u16>,
} }
impl DappInstaller { impl DappInstaller {
@ -363,7 +372,7 @@ impl ContentValidator for DappInstaller {
try!(manifest_file.write_all(manifest_str.as_bytes())); try!(manifest_file.write_all(manifest_str.as_bytes()));
// Create endpoint // Create endpoint
let app = LocalPageEndpoint::new(target, manifest.clone().into()); let app = LocalPageEndpoint::new(target, manifest.clone().into(), self.embeddable_at);
// Return modified app manifest // Return modified app manifest
Ok((manifest.id.clone(), app)) Ok((manifest.id.clone(), app))
@ -396,14 +405,14 @@ mod tests {
fn should_true_if_contains_the_app() { fn should_true_if_contains_the_app() {
// given // given
let path = env::temp_dir(); let path = env::temp_dir();
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false)); let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None);
let handler = LocalPageEndpoint::new(path, EndpointInfo { let handler = LocalPageEndpoint::new(path, EndpointInfo {
name: "fake".into(), name: "fake".into(),
description: "".into(), description: "".into(),
version: "".into(), version: "".into(),
author: "".into(), author: "".into(),
icon_url: "".into(), icon_url: "".into(),
}); }, None);
// when // when
fetcher.set_status("test", ContentStatus::Ready(handler)); fetcher.set_status("test", ContentStatus::Ready(handler));

View File

@ -97,12 +97,12 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
}) })
} }
pub fn local_endpoints(dapps_path: String) -> Endpoints { pub fn local_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints {
let mut pages = Endpoints::new(); let mut pages = Endpoints::new();
for dapp in local_dapps(dapps_path) { for dapp in local_dapps(dapps_path) {
pages.insert( pages.insert(
dapp.id, dapp.id,
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info)) Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, signer_port))
); );
} }
pages pages

View File

@ -17,8 +17,7 @@
use endpoint::{Endpoints, Endpoint}; use endpoint::{Endpoints, Endpoint};
use page::PageEndpoint; use page::PageEndpoint;
use proxypac::ProxyPac; use proxypac::ProxyPac;
use parity_dapps::{self, WebApp}; use parity_dapps::WebApp;
use parity_dapps_glue::WebApp as NewWebApp;
mod cache; mod cache;
mod fs; mod fs;
@ -26,17 +25,14 @@ pub mod urlhint;
pub mod fetcher; pub mod fetcher;
pub mod manifest; pub mod manifest;
extern crate parity_dapps_home;
extern crate parity_ui; extern crate parity_ui;
pub const HOME_PAGE: &'static str = "home";
pub const DAPPS_DOMAIN : &'static str = ".parity"; pub const DAPPS_DOMAIN : &'static str = ".parity";
pub const RPC_PATH : &'static str = "rpc"; pub const RPC_PATH : &'static str = "rpc";
pub const API_PATH : &'static str = "api"; pub const API_PATH : &'static str = "api";
pub const UTILS_PATH : &'static str = "parity-utils"; pub const UTILS_PATH : &'static str = "parity-utils";
pub fn main_page() -> &'static str {
"home"
}
pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String { pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String {
if using_dapps_domains { if using_dapps_domains {
format!("http://{}{}/", app_id, DAPPS_DOMAIN) format!("http://{}{}/", app_id, DAPPS_DOMAIN)
@ -46,72 +42,29 @@ pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String {
} }
pub fn utils() -> Box<Endpoint> { pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_dapps_home::App::default(), UTILS_PATH.to_owned())) Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
} }
pub fn all_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints { pub fn all_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins // fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path); let mut pages = fs::local_endpoints(dapps_path, signer_port);
// NOTE [ToDr] Dapps will be currently embeded on 8180 // NOTE [ToDr] Dapps will be currently embeded on 8180
pages.insert("ui".into(), Box::new( insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_port));
PageEndpoint::new_safe_to_embed(NewUi::default(), signer_port) pages.insert("proxy".into(), ProxyPac::boxed(signer_port));
));
pages.insert("proxy".into(), ProxyPac::boxed());
insert::<parity_dapps_home::App>(&mut pages, "home");
pages pages
} }
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) { fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default()))); pages.insert(id.to_owned(), Box::new(match embed_at {
Embeddable::Yes(port) => PageEndpoint::new_safe_to_embed(T::default(), port),
Embeddable::No => PageEndpoint::new(T::default()),
}));
} }
// TODO [ToDr] Temporary wrapper until we get rid of old built-ins. enum Embeddable {
use std::collections::HashMap; Yes(Option<u16>),
#[allow(dead_code)]
struct NewUi { No,
app: parity_ui::App,
files: HashMap<&'static str, parity_dapps::File>,
}
impl Default for NewUi {
fn default() -> Self {
let app = parity_ui::App::default();
let files = {
let mut files = HashMap::new();
for (k, v) in &app.files {
files.insert(*k, parity_dapps::File {
path: v.path,
content: v.content,
content_type: v.content_type,
});
}
files
};
NewUi {
app: app,
files: files,
}
}
}
impl WebApp for NewUi {
fn file(&self, path: &str) -> Option<&parity_dapps::File> {
self.files.get(path)
}
fn info(&self) -> parity_dapps::Info {
let info = self.app.info();
parity_dapps::Info {
name: info.name,
version: info.version,
author: info.author,
description: info.description,
icon_url: info.icon_url,
}
}
} }

View File

@ -17,76 +17,19 @@
//! Echo Handler //! Echo Handler
use std::io::Read; use std::io::Read;
use hyper::{header, server, Decoder, Encoder, Next}; use hyper::{server, Decoder, Encoder, Next};
use hyper::method::Method;
use hyper::net::HttpStream; use hyper::net::HttpStream;
use unicase::UniCase;
use super::ContentHandler; use super::ContentHandler;
#[derive(Debug, PartialEq)] #[derive(Default)]
/// Type of Cross-Origin request
enum Cors {
/// Not a Cross-Origin request - no headers needed
No,
/// Cross-Origin request with valid Origin
Allowed(String),
/// Cross-Origin request with invalid Origin
Forbidden,
}
pub struct EchoHandler { pub struct EchoHandler {
safe_origins: Vec<String>,
content: String, content: String,
cors: Cors,
handler: Option<ContentHandler>, handler: Option<ContentHandler>,
} }
impl EchoHandler {
pub fn cors(safe_origins: Vec<String>) -> Self {
EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::Forbidden,
handler: None,
}
}
fn cors_header(&self, origin: Option<String>) -> Cors {
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
for safe in safe_origins {
if origin.starts_with(safe) {
return true;
}
}
false
}
match origin {
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
Cors::Allowed(origin.clone())
},
None => Cors::No,
_ => Cors::Forbidden,
}
}
}
impl server::Handler<HttpStream> for EchoHandler { impl server::Handler<HttpStream> for EchoHandler {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next { fn on_request(&mut self, _: server::Request<HttpStream>) -> Next {
let origin = request.headers().get_raw("origin") Next::read()
.and_then(|list| list.get(0))
.and_then(|origin| String::from_utf8(origin.clone()).ok());
self.cors = self.cors_header(origin);
// Don't even read the payload if origin is forbidden!
if let Cors::Forbidden = self.cors {
self.handler = Some(ContentHandler::ok(String::new(), mime!(Text/Plain)));
Next::write()
} else {
Next::read()
}
} }
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next { fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
@ -104,16 +47,6 @@ impl server::Handler<HttpStream> for EchoHandler {
} }
fn on_response(&mut self, res: &mut server::Response) -> Next { fn on_response(&mut self, res: &mut server::Response) -> Next {
if let Cors::Allowed(ref domain) = self.cors {
let mut headers = res.headers_mut();
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
headers.set(header::AccessControlAllowHeaders(vec![
UniCase("origin".to_owned()),
UniCase("content-type".to_owned()),
UniCase("accept".to_owned()),
]));
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
}
self.handler.as_mut() self.handler.as_mut()
.expect("handler always set in on_request, which is before now; qed") .expect("handler always set in on_request, which is before now; qed")
.on_response(res) .on_response(res)
@ -125,28 +58,3 @@ impl server::Handler<HttpStream> for EchoHandler {
.on_response_writable(encoder) .on_response_writable(encoder)
} }
} }
#[test]
fn should_return_correct_cors_value() {
// given
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
let cut = EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::No,
handler: None,
};
// when
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
let res4 = cut.cors_header(None);
// then
assert_eq!(res1, Cors::Forbidden);
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
assert_eq!(res4, Cors::No);
}

View File

@ -30,6 +30,7 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl};
use url::Url; use url::Url;
use hyper::{server, header, net, uri}; use hyper::{server, header, net, uri};
use signer_address;
/// Adds security-related headers to the Response. /// Adds security-related headers to the Response.
pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option<u16>) { pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option<u16>) {
@ -40,7 +41,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option
if let Some(port) = embeddable_at { if let Some(port) = embeddable_at {
headers.set_raw( headers.set_raw(
"X-Frame-Options", "X-Frame-Options",
vec![format!("ALLOW-FROM http://127.0.0.1:{}", port).into_bytes()] vec![format!("ALLOW-FROM http://{}", signer_address(port)).into_bytes()]
); );
} else { } else {
// TODO [ToDr] Should we be more strict here (DENY?)? // TODO [ToDr] Should we be more strict here (DENY?)?

View File

@ -59,19 +59,17 @@ extern crate ethcore_rpc;
extern crate ethcore_util as util; extern crate ethcore_util as util;
extern crate linked_hash_map; extern crate linked_hash_map;
extern crate fetch; extern crate fetch;
extern crate parity_dapps_glue as parity_dapps;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[macro_use] #[macro_use]
extern crate mime; extern crate mime;
#[cfg(test)] #[cfg(test)]
extern crate ethcore_devtools as devtools; extern crate ethcore_devtools as devtools;
#[cfg(test)]
extern crate env_logger;
extern crate parity_dapps_glue;
// TODO [ToDr] - Deprecate when we get rid of old dapps.
extern crate parity_dapps;
mod endpoint; mod endpoint;
mod apps; mod apps;
@ -95,7 +93,7 @@ use jsonrpc_core::{IoHandler, IoDelegate};
use router::auth::{Authorization, NoAuth, HttpBasicAuth}; use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use ethcore_rpc::Extendable; use ethcore_rpc::Extendable;
static DAPPS_DOMAIN : &'static str = ".parity"; use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
/// Indicates sync status /// Indicates sync status
pub trait SyncStatus: Send + Sync { pub trait SyncStatus: Send + Sync {
@ -197,6 +195,17 @@ impl Server {
Some(allowed) Some(allowed)
} }
/// Returns a list of CORS domains for API endpoint.
fn cors_domains(signer_port: Option<u16>) -> Vec<String> {
match signer_port {
Some(port) => vec![
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("http://{}", signer_address(port)),
],
None => vec![],
}
}
fn start_http<A: Authorization + 'static>( fn start_http<A: Authorization + 'static>(
addr: &SocketAddr, addr: &SocketAddr,
hosts: Option<Vec<String>>, hosts: Option<Vec<String>>,
@ -209,15 +218,17 @@ impl Server {
) -> Result<Server, ServerError> { ) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None)); let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization); let authorization = Arc::new(authorization);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status)); let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_port));
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_port)); let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_port.clone()));
let cors_domains = Self::cors_domains(signer_port);
let special = Arc::new({ let special = Arc::new({
let mut special = HashMap::new(); let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone())); special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils()); special.insert(router::SpecialEndpoint::Utils, apps::utils());
special.insert( special.insert(
router::SpecialEndpoint::Api, router::SpecialEndpoint::Api,
api::RestApi::new(format!("{}", addr), endpoints.clone(), content_fetcher.clone()) api::RestApi::new(cors_domains, endpoints.clone(), content_fetcher.clone())
); );
special special
}); });
@ -226,7 +237,7 @@ impl Server {
try!(hyper::Server::http(addr)) try!(hyper::Server::http(addr))
.handle(move |ctrl| router::Router::new( .handle(move |ctrl| router::Router::new(
ctrl, ctrl,
apps::main_page(), signer_port.clone(),
content_fetcher.clone(), content_fetcher.clone(),
endpoints.clone(), endpoints.clone(),
special.clone(), special.clone(),
@ -290,6 +301,10 @@ pub fn random_filename() -> String {
rng.gen_ascii_chars().take(12).collect() rng.gen_ascii_chars().take(12).collect()
} }
fn signer_address(port: u16) -> String {
format!("127.0.0.1:{}", port)
}
#[cfg(test)] #[cfg(test)]
mod util_tests { mod util_tests {
use super::Server; use super::Server;
@ -309,4 +324,17 @@ mod util_tests {
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()])); assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()])); assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
} }
#[test]
fn should_return_cors_domains() {
// given
// when
let none = Server::cors_domains(None);
let some = Server::cors_domains(Some(18180));
// then
assert_eq!(none, Vec::<String>::new());
assert_eq!(some, vec!["http://home.parity".to_owned(), "http://127.0.0.1:18180".into()]);
}
} }

View File

@ -26,14 +26,16 @@ pub struct LocalPageEndpoint {
path: PathBuf, path: PathBuf,
mime: Option<String>, mime: Option<String>,
info: Option<EndpointInfo>, info: Option<EndpointInfo>,
embeddable_at: Option<u16>,
} }
impl LocalPageEndpoint { impl LocalPageEndpoint {
pub fn new(path: PathBuf, info: EndpointInfo) -> Self { pub fn new(path: PathBuf, info: EndpointInfo, embeddable_at: Option<u16>) -> Self {
LocalPageEndpoint { LocalPageEndpoint {
path: path, path: path,
mime: None, mime: None,
info: Some(info), info: Some(info),
embeddable_at: embeddable_at,
} }
} }
@ -42,6 +44,7 @@ impl LocalPageEndpoint {
path: path, path: path,
mime: Some(mime), mime: Some(mime),
info: None, info: None,
embeddable_at: None,
} }
} }
@ -62,7 +65,7 @@ impl Endpoint for LocalPageEndpoint {
prefix: None, prefix: None,
path: path, path: path,
file: handler::ServedFile::new(None), file: handler::ServedFile::new(None),
safe_to_embed_at_port: None, safe_to_embed_at_port: self.embeddable_at,
}) })
} else { } else {
Box::new(handler::PageHandler { Box::new(handler::PageHandler {
@ -70,7 +73,7 @@ impl Endpoint for LocalPageEndpoint {
prefix: None, prefix: None,
path: path, path: path,
file: handler::ServedFile::new(None), file: handler::ServedFile::new(None),
safe_to_embed_at_port: None, safe_to_embed_at_port: self.embeddable_at,
}) })
} }
} }

View File

@ -18,30 +18,45 @@
use endpoint::{Endpoint, Handler, EndpointPath}; use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler; use handlers::ContentHandler;
use apps::DAPPS_DOMAIN; use apps::{HOME_PAGE, DAPPS_DOMAIN};
use signer_address;
pub struct ProxyPac; pub struct ProxyPac {
signer_port: Option<u16>,
}
impl ProxyPac { impl ProxyPac {
pub fn boxed() -> Box<Endpoint> { pub fn boxed(signer_port: Option<u16>) -> Box<Endpoint> {
Box::new(ProxyPac) Box::new(ProxyPac {
signer_port: signer_port
})
} }
} }
impl Endpoint for ProxyPac { impl Endpoint for ProxyPac {
fn to_handler(&self, path: EndpointPath) -> Box<Handler> { fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
let signer = self.signer_port
.map(signer_address)
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
let content = format!( let content = format!(
r#" r#"
function FindProxyForURL(url, host) {{ function FindProxyForURL(url, host) {{
if (shExpMatch(host, "*{0}")) if (shExpMatch(host, "{0}{1}"))
{{ {{
return "PROXY {1}:{2}"; return "PROXY {4}";
}}
if (shExpMatch(host, "*{1}"))
{{
return "PROXY {2}:{3}";
}} }}
return "DIRECT"; return "DIRECT";
}} }}
"#, "#,
DAPPS_DOMAIN, path.host, path.port); HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
Box::new(ContentHandler::ok(content, mime!(Application/Javascript))) Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
} }
} }

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use DAPPS_DOMAIN; use apps::DAPPS_DOMAIN;
use hyper::{server, header, StatusCode}; use hyper::{server, header, StatusCode};
use hyper::net::HttpStream; use hyper::net::HttpStream;

View File

@ -20,13 +20,13 @@
pub mod auth; pub mod auth;
mod host_validation; mod host_validation;
use DAPPS_DOMAIN; use signer_address;
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use url::{Url, Host}; use url::{Url, Host};
use hyper::{self, server, Next, Encoder, Decoder, Control, StatusCode}; use hyper::{self, server, Next, Encoder, Decoder, Control, StatusCode};
use hyper::net::HttpStream; use hyper::net::HttpStream;
use apps; use apps::{self, DAPPS_DOMAIN};
use apps::fetcher::ContentFetcher; use apps::fetcher::ContentFetcher;
use endpoint::{Endpoint, Endpoints, EndpointPath}; use endpoint::{Endpoint, Endpoints, EndpointPath};
use handlers::{Redirection, extract_url, ContentHandler}; use handlers::{Redirection, extract_url, ContentHandler};
@ -43,7 +43,7 @@ pub enum SpecialEndpoint {
pub struct Router<A: Authorization + 'static> { pub struct Router<A: Authorization + 'static> {
control: Option<Control>, control: Option<Control>,
main_page: &'static str, signer_port: Option<u16>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
fetch: Arc<ContentFetcher>, fetch: Arc<ContentFetcher>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -61,57 +61,78 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
let endpoint = extract_endpoint(&url); let endpoint = extract_endpoint(&url);
let is_utils = endpoint.1 == SpecialEndpoint::Utils; let is_utils = endpoint.1 == SpecialEndpoint::Utils;
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
// Validate Host header // Validate Host header
if let Some(ref hosts) = self.allowed_hosts { if let Some(ref hosts) = self.allowed_hosts {
trace!(target: "dapps", "Validating host headers against: {:?}", hosts);
let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()); let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect());
if !is_valid { if !is_valid {
debug!(target: "dapps", "Rejecting invalid host header.");
self.handler = host_validation::host_invalid_response(); self.handler = host_validation::host_invalid_response();
return self.handler.on_request(req); return self.handler.on_request(req);
} }
} }
trace!(target: "dapps", "Checking authorization.");
// Check authorization // Check authorization
let auth = self.authorization.is_authorized(&req); let auth = self.authorization.is_authorized(&req);
if let Authorized::No(handler) = auth { if let Authorized::No(handler) = auth {
debug!(target: "dapps", "Authorization denied.");
self.handler = handler; self.handler = handler;
return self.handler.on_request(req); return self.handler.on_request(req);
} }
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed"); let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
self.handler = match endpoint { self.handler = match endpoint {
// First check special endpoints // First check special endpoints
(ref path, ref endpoint) if self.special.contains_key(endpoint) => { (ref path, ref endpoint) if self.special.contains_key(endpoint) => {
trace!(target: "dapps", "Resolving to special endpoint.");
self.special.get(endpoint) self.special.get(endpoint)
.expect("special known to contain key; qed") .expect("special known to contain key; qed")
.to_async_handler(path.clone().unwrap_or_default(), control) .to_async_handler(path.clone().unwrap_or_default(), control)
}, },
// Then delegate to dapp // Then delegate to dapp
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => { (Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
trace!(target: "dapps", "Resolving to local/builtin dapp.");
self.endpoints.get(&path.app_id) self.endpoints.get(&path.app_id)
.expect("special known to contain key; qed") .expect("special known to contain key; qed")
.to_async_handler(path.clone(), control) .to_async_handler(path.clone(), control)
}, },
// Try to resolve and fetch the dapp // Try to resolve and fetch the dapp
(Some(ref path), _) if self.fetch.contains(&path.app_id) => { (Some(ref path), _) if self.fetch.contains(&path.app_id) => {
trace!(target: "dapps", "Resolving to fetchable content.");
self.fetch.to_async_handler(path.clone(), control) self.fetch.to_async_handler(path.clone(), control)
}, },
// 404 for non-existent content // 404 for non-existent content
(Some(ref path), _) if *req.method() == hyper::method::Method::Get => { (Some(_), _) if *req.method() == hyper::method::Method::Get => {
let address = apps::redirection_address(path.using_dapps_domains, self.main_page); trace!(target: "dapps", "Resolving to 404.");
Box::new(ContentHandler::error( Box::new(ContentHandler::error(
StatusCode::NotFound, StatusCode::NotFound,
"404 Not Found", "404 Not Found",
"Requested content was not found.", "Requested content was not found.",
Some(&format!("Go back to the <a href=\"{}\">Home Page</a>.", address)) None,
)) ))
}, },
// Redirect any GET request to home. // Redirect any other GET request to signer.
_ if *req.method() == hyper::method::Method::Get => { _ if *req.method() == hyper::method::Method::Get => {
let address = apps::redirection_address(false, self.main_page); if let Some(port) = self.signer_port {
Redirection::boxed(address.as_str()) trace!(target: "dapps", "Redirecting to signer interface.");
Redirection::boxed(&format!("http://{}", signer_address(port)))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.",
Some("You can still access dapps by writing a correct address, though. Re-enabled Signer to get your homepage back."),
))
}
}, },
// RPC by default // RPC by default
_ => { _ => {
trace!(target: "dapps", "Resolving to RPC call.");
self.special.get(&SpecialEndpoint::Rpc) self.special.get(&SpecialEndpoint::Rpc)
.expect("RPC endpoint always stored; qed") .expect("RPC endpoint always stored; qed")
.to_async_handler(EndpointPath::default(), control) .to_async_handler(EndpointPath::default(), control)
@ -141,7 +162,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
impl<A: Authorization> Router<A> { impl<A: Authorization> Router<A> {
pub fn new( pub fn new(
control: Control, control: Control,
main_page: &'static str, signer_port: Option<u16>,
content_fetcher: Arc<ContentFetcher>, content_fetcher: Arc<ContentFetcher>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -154,7 +175,7 @@ impl<A: Authorization> Router<A> {
.to_handler(EndpointPath::default()); .to_handler(EndpointPath::default());
Router { Router {
control: Some(control), control: Some(control),
main_page: main_page, signer_port: signer_port,
endpoints: endpoints, endpoints: endpoints,
fetch: content_fetcher, fetch: content_fetcher,
special: special, special: special,

View File

@ -24,7 +24,7 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
Box::new(RpcEndpoint { Box::new(RpcEndpoint {
handler: handler, handler: handler,
panic_handler: panic_handler, panic_handler: panic_handler,
cors_domain: Some(vec![AccessControlAllowOrigin::Null]), cors_domain: None,
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router. // NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
allowed_hosts: None, allowed_hosts: None,
}) })

View File

@ -34,7 +34,7 @@ fn should_return_error() {
// then // then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json"); assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#)); assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
@ -57,8 +57,8 @@ fn should_serve_apps() {
// then // then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json"); assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
assert!(response.body.contains("Parity Home Screen"), response.body); assert!(response.body.contains("Parity UI"), response.body);
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
@ -80,7 +80,7 @@ fn should_handle_ping() {
// then // then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json"); assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
assert_eq!(response.body, "0\n\n".to_owned()); assert_eq!(response.body, "0\n\n".to_owned());
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
@ -107,3 +107,54 @@ fn should_try_to_resolve_dapp() {
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
#[test]
fn should_return_signer_port_cors_headers() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: http://127.0.0.1:18180\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
response.headers_raw.contains("Access-Control-Allow-Origin: http://127.0.0.1:18180"),
"CORS header for signer missing: {:?}",
response.headers
);
}
#[test]
fn should_return_signer_port_cors_headers_for_home_parity() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: http://home.parity\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
response.headers_raw.contains("Access-Control-Allow-Origin: http://home.parity"),
"CORS header for home.parity missing: {:?}",
response.headers
);
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tests::helpers::{serve_with_auth, request, assert_security_headers}; use tests::helpers::{serve_with_auth, request, assert_security_headers_for_embed};
#[test] #[test]
fn should_require_authorization() { fn should_require_authorization() {
@ -66,7 +66,7 @@ fn should_allow_on_valid_auth() {
// when // when
let response = request(server, let response = request(server,
"\ "\
GET /home/ HTTP/1.1\r\n\ GET /ui/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\ Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\ Connection: close\r\n\
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
@ -76,5 +76,5 @@ fn should_allow_on_valid_auth() {
// then // then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_security_headers(&response.headers); assert_security_headers_for_embed(&response.headers);
} }

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tests::helpers::{serve_with_registrar, request, assert_security_headers}; use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers};
#[test] #[test]
fn should_resolve_dapp() { fn should_resolve_dapp() {
@ -37,3 +37,31 @@ fn should_resolve_dapp() {
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
#[test]
fn should_return_503_when_syncing_but_should_make_the_calls() {
// given
let (server, registrar) = serve_with_registrar_and_sync();
{
let mut responses = registrar.responses.lock();
let res1 = responses.get(0).unwrap().clone();
let res2 = responses.get(1).unwrap().clone();
// Registrar will be called twice - fill up the responses.
responses.push(res1);
responses.push(res2);
}
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.parity\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
assert_eq!(response.status, "HTTP/1.1 503 Service Unavailable".to_owned());
assert_eq!(registrar.calls.lock().len(), 4);
assert_security_headers(&response.headers);
}

View File

@ -18,6 +18,7 @@ use std::env;
use std::str; use std::str;
use std::sync::Arc; use std::sync::Arc;
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
use env_logger::LogBuilder;
use ServerBuilder; use ServerBuilder;
use Server; use Server;
@ -27,6 +28,7 @@ use devtools::http_client;
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2"; const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000"; const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
const SIGNER_PORT: u16 = 18180;
pub struct FakeRegistrar { pub struct FakeRegistrar {
pub calls: Arc<Mutex<Vec<(String, String)>>>, pub calls: Arc<Mutex<Vec<(String, String)>>>,
@ -58,11 +60,23 @@ impl ContractClient for FakeRegistrar {
} }
} }
pub fn init_server(hosts: Option<Vec<String>>) -> (Server, Arc<FakeRegistrar>) { fn init_logger() {
// Initialize 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.");
}
}
pub fn init_server(hosts: Option<Vec<String>>, is_syncing: bool) -> (Server, Arc<FakeRegistrar>) {
init_logger();
let registrar = Arc::new(FakeRegistrar::new()); let registrar = Arc::new(FakeRegistrar::new());
let mut dapps_path = env::temp_dir(); let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone()); let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone());
builder.with_sync_status(Arc::new(move || is_syncing));
builder.with_signer_port(Some(SIGNER_PORT));
( (
builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(), builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(),
registrar, registrar,
@ -70,23 +84,29 @@ pub fn init_server(hosts: Option<Vec<String>>) -> (Server, Arc<FakeRegistrar>) {
} }
pub fn serve_with_auth(user: &str, pass: &str) -> Server { pub fn serve_with_auth(user: &str, pass: &str) -> Server {
init_logger();
let registrar = Arc::new(FakeRegistrar::new()); let registrar = Arc::new(FakeRegistrar::new());
let mut dapps_path = env::temp_dir(); let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar); let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar);
builder.with_signer_port(Some(SIGNER_PORT));
builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap() builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
} }
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server { pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
init_server(hosts).0 init_server(hosts, false).0
} }
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) { pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
init_server(None) init_server(None, false)
}
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
init_server(None, true)
} }
pub fn serve() -> Server { pub fn serve() -> Server {
init_server(None).0 init_server(None, false).0
} }
pub fn request(server: Server, request: &str) -> http_client::Response { pub fn request(server: Server, request: &str) -> http_client::Response {
@ -94,5 +114,8 @@ pub fn request(server: Server, request: &str) -> http_client::Response {
} }
pub fn assert_security_headers(headers: &[String]) { pub fn assert_security_headers(headers: &[String]) {
http_client::assert_security_headers_present(headers) http_client::assert_security_headers_present(headers, None)
}
pub fn assert_security_headers_for_embed(headers: &[String]) {
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
} }

View File

@ -33,7 +33,7 @@ fn should_redirect_to_home() {
// then // then
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/"); assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
} }
#[test] #[test]
@ -53,7 +53,7 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
// then // then
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/"); assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
} }
#[test] #[test]
@ -73,7 +73,6 @@ fn should_display_404_on_invalid_dapp() {
// then // then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert!(response.body.contains("href=\"/home/"));
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
@ -94,7 +93,6 @@ fn should_display_404_on_invalid_dapp_with_domain() {
// then // then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert!(response.body.contains("href=\"http://home.parity/"));
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }
@ -161,7 +159,7 @@ fn should_serve_proxy_pac() {
// then // then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.body, "86\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned()); assert_eq!(response.body, "D5\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
assert_security_headers(&response.headers); assert_security_headers(&response.headers);
} }

View File

@ -45,7 +45,7 @@ fn should_allow_valid_host() {
// when // when
let response = request(server, let response = request(server,
"\ "\
GET /home/ HTTP/1.1\r\n\ GET /ui/ HTTP/1.1\r\n\
Host: localhost:8080\r\n\ Host: localhost:8080\r\n\
Connection: close\r\n\ Connection: close\r\n\
\r\n\ \r\n\
@ -66,7 +66,7 @@ fn should_serve_dapps_domains() {
let response = request(server, let response = request(server,
"\ "\
GET / HTTP/1.1\r\n\ GET / HTTP/1.1\r\n\
Host: home.parity\r\n\ Host: ui.parity\r\n\
Connection: close\r\n\ Connection: close\r\n\
\r\n\ \r\n\
{} {}
@ -98,3 +98,30 @@ fn should_allow_parity_utils_even_on_invalid_domain() {
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
} }
#[test]
fn should_not_return_cors_headers_for_rpc() {
// given
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
// when
let response = request(server,
"\
POST /rpc HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: null\r\n\
Content-Type: application/json\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
!response.headers_raw.contains("Access-Control-Allow-Origin"),
"CORS headers were not expected: {:?}",
response.headers
);
}

View File

@ -1,19 +1,18 @@
[package] [package]
description = "Parity built-in dapps." description = "Ethcore Parity UI"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "parity-ui" name = "parity-ui"
version = "1.4.0" version = "1.4.0"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io>"] authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[features]
default = ["with-syntex"]
use-precompiled-js = ["parity-dapps-glue/use-precompiled-js"]
with-syntex = ["parity-dapps-glue/with-syntex"]
[build-dependencies] [build-dependencies]
parity-dapps-glue = { path = "../js-glue" } rustc_version = "0.1"
[dependencies] [dependencies]
parity-dapps-glue = { path = "../js-glue" } parity-ui-dev = { path = "../../js", optional = true }
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", optional = true }
[features]
no-precompiled-js = ["parity-ui-dev"]
use-precompiled-js = ["parity-ui-precompiled"]

View File

@ -14,9 +14,20 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
#[cfg(feature = "with-syntex")]
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
#[cfg(not(feature = "with-syntex"))] #[cfg(feature = "parity-ui-dev")]
include!("lib.rs.in"); mod inner {
extern crate parity_ui_dev;
pub use self::parity_ui_dev::*;
}
#[cfg(feature = "parity-ui-precompiled")]
mod inner {
extern crate parity_ui_precompiled;
pub use self::parity_ui_precompiled::*;
}
pub use self::inner::*;

View File

@ -65,11 +65,18 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
} }
/// Check if all required security headers are present /// Check if all required security headers are present
pub fn assert_security_headers_present(headers: &[String]) { pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
assert!( if let Some(port) = port {
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(), assert!(
"X-Frame-Options missing: {:?}", headers headers.iter().find(|header| header.as_str() == &format!("X-Frame-Options: ALLOW-FROM http://127.0.0.1:{}", port)).is_some(),
); "X-Frame-Options: ALLOW-FROM missing: {:?}", headers
);
} else {
assert!(
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
);
}
assert!( assert!(
headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(), headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(),
"X-XSS-Protection missing: {:?}", headers "X-XSS-Protection missing: {:?}", headers

View File

@ -164,8 +164,7 @@
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@zero.parity.io:30303", "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303"
"enode://cc92c4c40d612a10c877ca023ef0496c843fbc92b6c6c0d55ce0b863d51d821c4bd70daebb54324a6086374e6dc05708fed39862b275f169cb678e655da9d07d@136.243.154.246:30303"
], ],
"accounts": { "accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@ -96,9 +96,9 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!() unimplemented!()
} }
fn get(&self, key: &H256) -> Option<&[u8]> { fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP { if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC); return Some(DBValue::from_slice(&NULL_RLP_STATIC));
} }
self.db.get(&combine_key(&self.address_hash, key)) self.db.get(&combine_key(&self.address_hash, key))
} }
@ -114,7 +114,7 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!() unimplemented!()
} }
fn emplace(&mut self, _key: H256, _value: Bytes) { fn emplace(&mut self, _key: H256, _value: DBValue) {
unimplemented!() unimplemented!()
} }
@ -122,7 +122,7 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!() unimplemented!()
} }
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> { fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
self.db.get_aux(hash) self.db.get_aux(hash)
} }
} }
@ -158,9 +158,9 @@ impl<'db> HashDB for AccountDBMut<'db>{
unimplemented!() unimplemented!()
} }
fn get(&self, key: &H256) -> Option<&[u8]> { fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP { if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC); return Some(DBValue::from_slice(&NULL_RLP_STATIC));
} }
self.db.get(&combine_key(&self.address_hash, key)) self.db.get(&combine_key(&self.address_hash, key))
} }
@ -178,16 +178,16 @@ impl<'db> HashDB for AccountDBMut<'db>{
} }
let k = value.sha3(); let k = value.sha3();
let ak = combine_key(&self.address_hash, &k); let ak = combine_key(&self.address_hash, &k);
self.db.emplace(ak, value.to_vec()); self.db.emplace(ak, DBValue::from_slice(value));
k k
} }
fn emplace(&mut self, key: H256, value: Bytes) { fn emplace(&mut self, key: H256, value: DBValue) {
if key == SHA3_NULL_RLP { if key == SHA3_NULL_RLP {
return; return;
} }
let key = combine_key(&self.address_hash, &key); let key = combine_key(&self.address_hash, &key);
self.db.emplace(key, value.to_vec()) self.db.emplace(key, value)
} }
fn remove(&mut self, key: &H256) { fn remove(&mut self, key: &H256) {
@ -202,7 +202,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
self.db.insert_aux(hash, value); self.db.insert_aux(hash, value);
} }
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> { fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
self.db.get_aux(hash) self.db.get_aux(hash)
} }
@ -218,9 +218,9 @@ impl<'db> HashDB for Wrapping<'db> {
unimplemented!() unimplemented!()
} }
fn get(&self, key: &H256) -> Option<&[u8]> { fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP { if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC); return Some(DBValue::from_slice(&NULL_RLP_STATIC));
} }
self.0.get(key) self.0.get(key)
} }
@ -236,7 +236,7 @@ impl<'db> HashDB for Wrapping<'db> {
unimplemented!() unimplemented!()
} }
fn emplace(&mut self, _key: H256, _value: Bytes) { fn emplace(&mut self, _key: H256, _value: DBValue) {
unimplemented!() unimplemented!()
} }
@ -252,9 +252,9 @@ impl<'db> HashDB for WrappingMut<'db>{
unimplemented!() unimplemented!()
} }
fn get(&self, key: &H256) -> Option<&[u8]> { fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP { if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC); return Some(DBValue::from_slice(&NULL_RLP_STATIC));
} }
self.0.get(key) self.0.get(key)
} }
@ -273,7 +273,7 @@ impl<'db> HashDB for WrappingMut<'db>{
self.0.insert(value) self.0.insert(value)
} }
fn emplace(&mut self, key: H256, value: Bytes) { fn emplace(&mut self, key: H256, value: DBValue) {
if key == SHA3_NULL_RLP { if key == SHA3_NULL_RLP {
return; return;
} }
@ -286,4 +286,4 @@ impl<'db> HashDB for WrappingMut<'db>{
} }
self.0.remove(key) self.0.remove(key)
} }
} }

View File

@ -265,6 +265,20 @@ impl AccountProvider {
Ok(()) Ok(())
} }
/// Returns `true` if the password for `account` is `password`. `false` if not.
pub fn test_password(&self, account: &Address, password: String) -> Result<bool, Error> {
match self.sstore.sign(&account, &password, &Default::default()) {
Ok(_) => Ok(true),
Err(SSError::InvalidPassword) => Ok(false),
Err(e) => Err(Error::SStore(e)),
}
}
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> {
self.sstore.change_password(&account, &password, &new_password).map_err(Error::SStore)
}
/// Helper method used for unlocking accounts. /// Helper method used for unlocking accounts.
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> { fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
// verify password by signing dump message // verify password by signing dump message

View File

@ -15,10 +15,14 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Evm input params. //! Evm input params.
use common::*; use util::{Address, Bytes, Uint, U256};
use util::hash::{H256, FixedHash};
use util::sha3::{Hashable, SHA3_EMPTY};
use ethjson; use ethjson;
use types::executed::CallType; use types::executed::CallType;
use std::sync::Arc;
/// Transaction value /// Transaction value
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ActionValue { pub enum ActionValue {

View File

@ -16,7 +16,7 @@
//! Ethcore basic typenames. //! Ethcore basic typenames.
use util::*; use util::hash::H2048;
/// Type for a 2048-bit log-bloom, as used by our blocks. /// Type for a 2048-bit log-bloom, as used by our blocks.
pub type LogBloom = H2048; pub type LogBloom = H2048;

View File

@ -587,10 +587,17 @@ pub fn enact_verified(
mod tests { mod tests {
use tests::helpers::*; use tests::helpers::*;
use super::*; use super::*;
use common::*;
use engines::Engine; use engines::Engine;
use env_info::LastHashes;
use error::Error;
use header::Header;
use factory::Factories; use factory::Factories;
use state_db::StateDB; use state_db::StateDB;
use views::BlockView;
use util::Address;
use util::hash::FixedHash;
use std::sync::Arc;
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]

View File

@ -394,6 +394,8 @@ impl BlockProvider for BlockChain {
} }
} }
/// An iterator which walks the blockchain towards the genesis.
#[derive(Clone)]
pub struct AncestryIter<'a> { pub struct AncestryIter<'a> {
current: H256, current: H256,
chain: &'a BlockChain, chain: &'a BlockChain,
@ -403,11 +405,10 @@ impl<'a> Iterator for AncestryIter<'a> {
type Item = H256; type Item = H256;
fn next(&mut self) -> Option<H256> { fn next(&mut self) -> Option<H256> {
if self.current.is_zero() { if self.current.is_zero() {
Option::None None
} else { } else {
let mut n = self.chain.block_details(&self.current).unwrap().parent; self.chain.block_details(&self.current)
mem::swap(&mut self.current, &mut n); .map(|details| mem::replace(&mut self.current, details.parent))
Some(n)
} }
} }
} }
@ -999,17 +1000,29 @@ impl BlockChain {
if !self.is_known(parent) { return None; } if !self.is_known(parent) { return None; }
let mut excluded = HashSet::new(); let mut excluded = HashSet::new();
for a in self.ancestry_iter(parent.clone()).unwrap().take(uncle_generations) { let ancestry = match self.ancestry_iter(parent.clone()) {
excluded.extend(self.uncle_hashes(&a).unwrap().into_iter()); Some(iter) => iter,
excluded.insert(a); None => return None,
};
for a in ancestry.clone().take(uncle_generations) {
if let Some(uncles) = self.uncle_hashes(&a) {
excluded.extend(uncles);
excluded.insert(a);
} else {
break
}
} }
let mut ret = Vec::new(); let mut ret = Vec::new();
for a in self.ancestry_iter(parent.clone()).unwrap().skip(1).take(uncle_generations) { for a in ancestry.skip(1).take(uncle_generations) {
ret.extend(self.block_details(&a).unwrap().children.iter() if let Some(details) = self.block_details(&a) {
.filter(|h| !excluded.contains(h)) ret.extend(details.children.iter().filter(|h| !excluded.contains(h)))
); } else {
break
}
} }
Some(ret) Some(ret)
} }

View File

@ -204,7 +204,7 @@ impl Client {
let engine = spec.engine.clone(); let engine = spec.engine.clone();
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone()); let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal());
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue); panic_handler.forward_from(&block_queue);

View File

@ -117,6 +117,8 @@ pub struct ClientConfig {
pub jump_table_size: usize, pub jump_table_size: usize,
/// State pruning history size. /// State pruning history size.
pub history: u64, pub history: u64,
/// Check seal valididity on block import
pub check_seal: bool,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -37,6 +37,7 @@ pub use block_import_error::BlockImportError;
pub use transaction_import::TransactionImportResult; pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError; pub use transaction_import::TransactionImportError;
pub use self::traits::{BlockChainClient, MiningBlockChainClient}; pub use self::traits::{BlockChainClient, MiningBlockChainClient};
pub use verification::VerifierType;
/// IPC interfaces /// IPC interfaces
#[cfg(feature="ipc")] #[cfg(feature="ipc")]

View File

@ -194,15 +194,20 @@ pub trait BlockChainClient : Sync + Send {
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> { fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
let mut h = self.chain_info().best_block_hash; let mut h = self.chain_info().best_block_hash;
let mut corpus = Vec::new(); let mut corpus = Vec::new();
for _ in 0..sample_size { while corpus.is_empty() {
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed"); for _ in 0..sample_size {
let block = BlockView::new(&block_bytes); let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let header = block.header_view(); let block = BlockView::new(&block_bytes);
if header.number() == 0 { let header = block.header_view();
break; if header.number() == 0 {
if corpus.is_empty() {
corpus.push(20_000_000_000u64.into()); // we have literally no information - it' as good a number as any.
}
break;
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
} }
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
} }
corpus.sort(); corpus.sort();
let n = corpus.len(); let n = corpus.len();

View File

@ -19,17 +19,22 @@
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::sync::Weak; use std::sync::Weak;
use std::time::{UNIX_EPOCH, Duration}; use std::time::{UNIX_EPOCH, Duration};
use common::*; use util::*;
use ethkey::verify_address; use ethkey::verify_address;
use rlp::{UntrustedRlp, View, encode, decode}; use rlp::{UntrustedRlp, View, encode, decode};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use block::*; use block::*;
use spec::CommonParams; use spec::CommonParams;
use engines::Engine; use engines::Engine;
use header::Header;
use error::{Error, BlockError};
use evm::Schedule; use evm::Schedule;
use ethjson; use ethjson;
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel}; use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
use service::ClientIoMessage; use service::ClientIoMessage;
use transaction::SignedTransaction;
use env_info::EnvInfo;
use builtin::Builtin;
/// `AuthorityRound` params. /// `AuthorityRound` params.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]

View File

@ -16,14 +16,20 @@
//! A blockchain engine that supports a basic, non-BFT proof-of-authority. //! A blockchain engine that supports a basic, non-BFT proof-of-authority.
use common::*;
use ethkey::{recover, public_to_address}; use ethkey::{recover, public_to_address};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use block::*; use block::*;
use builtin::Builtin;
use spec::CommonParams; use spec::CommonParams;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use error::{BlockError, Error};
use evm::Schedule; use evm::Schedule;
use ethjson; use ethjson;
use header::Header;
use transaction::SignedTransaction;
use util::*;
/// `BasicAuthority` params. /// `BasicAuthority` params.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -177,10 +183,13 @@ impl Engine for BasicAuthority {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use common::*; use util::*;
use block::*; use block::*;
use env_info::EnvInfo;
use error::{BlockError, Error};
use tests::helpers::*; use tests::helpers::*;
use account_provider::AccountProvider; use account_provider::AccountProvider;
use header::Header;
use spec::Spec; use spec::Spec;
/// Create a new test chain spec with `BasicAuthority` consensus engine. /// Create a new test chain spec with `BasicAuthority` consensus engine.

View File

@ -18,11 +18,11 @@ use std::collections::BTreeMap;
use util::Address; use util::Address;
use builtin::Builtin; use builtin::Builtin;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use spec::CommonParams; use spec::CommonParams;
use evm::Schedule; use evm::Schedule;
use env_info::EnvInfo;
use block::ExecutedBlock; use block::ExecutedBlock;
use common::Bytes; use util::Bytes;
use account_provider::AccountProvider; use account_provider::AccountProvider;
/// An engine which does not provide any consensus mechanism, just seals blocks internally. /// An engine which does not provide any consensus mechanism, just seals blocks internally.
@ -67,10 +67,11 @@ impl Engine for InstantSeal {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use common::*; use util::*;
use tests::helpers::*; use tests::helpers::*;
use account_provider::AccountProvider; use account_provider::AccountProvider;
use spec::Spec; use spec::Spec;
use header::Header;
use block::*; use block::*;
#[test] #[test]

View File

@ -26,13 +26,18 @@ pub use self::instant_seal::InstantSeal;
pub use self::basic_authority::BasicAuthority; pub use self::basic_authority::BasicAuthority;
pub use self::authority_round::AuthorityRound; pub use self::authority_round::AuthorityRound;
use common::*; use util::*;
use account_provider::AccountProvider; use account_provider::AccountProvider;
use block::ExecutedBlock; use block::ExecutedBlock;
use builtin::Builtin;
use env_info::EnvInfo;
use error::Error;
use spec::CommonParams; use spec::CommonParams;
use evm::Schedule; use evm::Schedule;
use io::IoChannel; use io::IoChannel;
use service::ClientIoMessage; use service::ClientIoMessage;
use header::Header;
use transaction::SignedTransaction;
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
/// Provides hooks into each of the major parts of block import. /// Provides hooks into each of the major parts of block import.

View File

@ -15,9 +15,14 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager, H256 as EH256}; use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager, H256 as EH256};
use common::*; use util::*;
use block::*; use block::*;
use builtin::Builtin;
use env_info::EnvInfo;
use error::{BlockError, Error};
use header::Header;
use spec::CommonParams; use spec::CommonParams;
use transaction::SignedTransaction;
use engines::Engine; use engines::Engine;
use evm::Schedule; use evm::Schedule;
use ethjson; use ethjson;
@ -187,8 +192,8 @@ impl Engine for Ethash {
// Commit state so that we can actually figure out the state root. // Commit state so that we can actually figure out the state root.
if let Err(e) = fields.state.commit() { if let Err(e) = fields.state.commit() {
warn!("Encountered error on state commit: {}", e); warn!("Encountered error on state commit: {}", e);
} }
} }
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
@ -371,9 +376,12 @@ impl Header {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use common::*; use util::*;
use block::*; use block::*;
use tests::helpers::*; use tests::helpers::*;
use env_info::EnvInfo;
use error::{BlockError, Error};
use header::Header;
use super::super::new_morden; use super::super::new_morden;
use super::Ethash; use super::Ethash;
use rlp; use rlp;

View File

@ -65,10 +65,11 @@ pub fn new_morden() -> Spec { load(include_bytes!("../../res/ethereum/morden.jso
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use common::*; use util::*;
use state::*; use state::*;
use super::*; use super::*;
use tests::helpers::*; use tests::helpers::*;
use views::BlockView;
#[test] #[test]
fn ensure_db_good() { fn ensure_db_good() {

View File

@ -24,7 +24,8 @@ extern crate test;
use self::test::{Bencher, black_box}; use self::test::{Bencher, black_box};
use common::*; use util::*;
use action_params::ActionParams;
use evm::{self, Factory, VMType}; use evm::{self, Factory, VMType};
use evm::tests::FakeExt; use evm::tests::FakeExt;

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*; use util::*;
use super::u256_to_address; use super::u256_to_address;
use evm::{self, CostType}; use evm::{self, CostType};
use evm::instructions::{self, Instruction, InstructionInfo}; use evm::instructions::{self, Instruction, InstructionInfo};
@ -64,14 +64,14 @@ impl<Gas: CostType> Gasometer<Gas> {
Some(cap_divisor) if self.current_gas >= needed => { Some(cap_divisor) if self.current_gas >= needed => {
let gas_remaining = self.current_gas - needed; let gas_remaining = self.current_gas - needed;
let max_gas_provided = gas_remaining - gas_remaining / Gas::from(cap_divisor); let max_gas_provided = gas_remaining - gas_remaining / Gas::from(cap_divisor);
if let Some(Ok(r)) = requested { if let Some(Ok(r)) = requested {
Ok(min(r, max_gas_provided)) Ok(min(r, max_gas_provided))
} else { } else {
Ok(max_gas_provided) Ok(max_gas_provided)
} }
}, },
_ => { _ => {
if let Some(r) = requested { if let Some(r) = requested {
r r
} else if self.current_gas >= needed { } else if self.current_gas >= needed {
Ok(self.current_gas - needed) Ok(self.current_gas - needed)
@ -84,10 +84,10 @@ impl<Gas: CostType> Gasometer<Gas> {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
/// Determine how much gas is used by the given instruction, given the machine's state. /// Determine how much gas is used by the given instruction, given the machine's state.
/// ///
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some` /// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case, /// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
/// it will be the amount of gas that the current context provides to the child context. /// it will be the amount of gas that the current context provides to the child context.
pub fn get_gas_cost_mem( pub fn get_gas_cost_mem(
&mut self, &mut self,
ext: &evm::Ext, ext: &evm::Ext,
@ -183,7 +183,7 @@ impl<Gas: CostType> Gasometer<Gas> {
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into())); gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
}; };
// TODO: refactor to avoid duplicate calculation here and later on. // TODO: refactor to avoid duplicate calculation here and later on.
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem)); let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into())); let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let requested = Gas::from_u256(*stack.peek(0)); let requested = Gas::from_u256(*stack.peek(0));
@ -199,7 +199,7 @@ impl<Gas: CostType> Gasometer<Gas> {
try!(mem_needed(stack.peek(2), stack.peek(3))) try!(mem_needed(stack.peek(2), stack.peek(3)))
); );
// TODO: refactor to avoid duplicate calculation here and later on. // TODO: refactor to avoid duplicate calculation here and later on.
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem)); let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into())); let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let requested = Gas::from_u256(*stack.peek(0)); let requested = Gas::from_u256(*stack.peek(0));
@ -212,9 +212,9 @@ impl<Gas: CostType> Gasometer<Gas> {
let mut gas = Gas::from(schedule.create_gas); let mut gas = Gas::from(schedule.create_gas);
let mem = try!(mem_needed(stack.peek(1), stack.peek(2))); let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
// TODO: refactor to avoid duplicate calculation here and later on. // TODO: refactor to avoid duplicate calculation here and later on.
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem)); let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into())); let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let provided = try!(self.gas_provided(schedule, cost_so_far, None)); let provided = try!(self.gas_provided(schedule, cost_so_far, None));
gas = overflowing!(gas.overflow_add(provided)); gas = overflowing!(gas.overflow_add(provided));

View File

@ -29,12 +29,14 @@ use self::memory::Memory;
pub use self::shared_cache::SharedCache; pub use self::shared_cache::SharedCache;
use std::marker::PhantomData; use std::marker::PhantomData;
use common::*; use action_params::{ActionParams, ActionValue};
use types::executed::CallType; use types::executed::CallType;
use super::instructions::{self, Instruction, InstructionInfo}; use evm::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
use bit_set::BitSet; use bit_set::BitSet;
use util::*;
type CodePosition = usize; type CodePosition = usize;
type ProgramCounter = usize; type ProgramCounter = usize;

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Just in time compiler execution environment. //! Just in time compiler execution environment.
use common::*; use util::*;
use evmjit; use evmjit;
use evm::{self, GasLeft}; use evm::{self, GasLeft};
use types::executed::CallType; use types::executed::CallType;

View File

@ -14,7 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*; use util::*;
use action_params::{ActionParams, ActionValue};
use env_info::EnvInfo;
use types::executed::CallType; use types::executed::CallType;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug; use std::fmt::Debug;

View File

@ -15,13 +15,17 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Transaction Execution environment. //! Transaction Execution environment.
use common::*; use util::*;
use action_params::{ActionParams, ActionValue};
use state::{State, Substate}; use state::{State, Substate};
use engines::Engine; use engines::Engine;
use types::executed::CallType; use types::executed::CallType;
use env_info::EnvInfo;
use error::ExecutionError;
use evm::{self, Ext, Factory, Finalize}; use evm::{self, Ext, Factory, Finalize};
use externalities::*; use externalities::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use transaction::{Action, SignedTransaction};
use crossbeam; use crossbeam;
pub use types::executed::{Executed, ExecutionResult}; pub use types::executed::{Executed, ExecutionResult};
@ -250,7 +254,7 @@ impl<'a> Executive<'a> {
vm_tracer: &mut V vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer { ) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.checkpoint();
// at first, transfer value to destination // at first, transfer value to destination
if let ActionValue::Transfer(val) = params.value { if let ActionValue::Transfer(val) = params.value {
@ -269,7 +273,7 @@ impl<'a> Executive<'a> {
let cost = self.engine.cost_of_builtin(&params.code_address, data); let cost = self.engine.cost_of_builtin(&params.code_address, data);
if cost <= params.gas { if cost <= params.gas {
self.engine.execute_builtin(&params.code_address, data, &mut output); self.engine.execute_builtin(&params.code_address, data, &mut output);
self.state.discard_snapshot(); self.state.discard_checkpoint();
// trace only top level calls to builtins to avoid DDoS attacks // trace only top level calls to builtins to avoid DDoS attacks
if self.depth == 0 { if self.depth == 0 {
@ -289,7 +293,7 @@ impl<'a> Executive<'a> {
Ok(params.gas - cost) Ok(params.gas - cost)
} else { } else {
// just drain the whole gas // just drain the whole gas
self.state.revert_to_snapshot(); self.state.revert_to_checkpoint();
tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into()); tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into());
@ -335,7 +339,7 @@ impl<'a> Executive<'a> {
res res
} else { } else {
// otherwise it's just a basic transaction, only do tracing, if necessary. // otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.discard_snapshot(); self.state.discard_checkpoint();
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]); tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
Ok(params.gas) Ok(params.gas)
@ -354,7 +358,7 @@ impl<'a> Executive<'a> {
vm_tracer: &mut V vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer { ) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.checkpoint();
// part of substate that may be reverted // part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(); let mut unconfirmed_substate = Substate::new();
@ -485,10 +489,10 @@ impl<'a> Executive<'a> {
| Err(evm::Error::BadInstruction {.. }) | Err(evm::Error::BadInstruction {.. })
| Err(evm::Error::StackUnderflow {..}) | Err(evm::Error::StackUnderflow {..})
| Err(evm::Error::OutOfStack {..}) => { | Err(evm::Error::OutOfStack {..}) => {
self.state.revert_to_snapshot(); self.state.revert_to_checkpoint();
}, },
Ok(_) | Err(evm::Error::Internal) => { Ok(_) | Err(evm::Error::Internal) => {
self.state.discard_snapshot(); self.state.discard_checkpoint();
substate.accrue(un_substate); substate.accrue(un_substate);
} }
} }
@ -500,13 +504,18 @@ impl<'a> Executive<'a> {
mod tests { mod tests {
use ethkey::{Generator, Random}; use ethkey::{Generator, Random};
use super::*; use super::*;
use common::*; use util::*;
use action_params::{ActionParams, ActionValue};
use env_info::EnvInfo;
use evm::{Factory, VMType}; use evm::{Factory, VMType};
use error::ExecutionError;
use state::Substate; use state::Substate;
use tests::helpers::*; use tests::helpers::*;
use trace::trace; use trace::trace;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer}; use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
use transaction::{Action, Transaction};
use types::executed::CallType; use types::executed::CallType;
#[test] #[test]

View File

@ -15,9 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Transaction Execution environment. //! Transaction Execution environment.
use common::*; use util::*;
use action_params::{ActionParams, ActionValue};
use state::{State, Substate}; use state::{State, Substate};
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use executive::*; use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use types::executed::CallType; use types::executed::CallType;
@ -253,6 +255,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
} }
fn log(&mut self, topics: Vec<H256>, data: &[u8]) { fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
use log_entry::LogEntry;
let address = self.origin_info.address.clone(); let address = self.origin_info.address.clone();
self.substate.logs.push(LogEntry { self.substate.logs.push(LogEntry {
address: address, address: address,
@ -303,8 +307,9 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use common::*; use util::*;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use evm::Ext; use evm::Ext;
use state::{State, Substate}; use state::{State, Substate};
use tests::helpers::*; use tests::helpers::*;

View File

@ -17,7 +17,7 @@
//! Block header. //! Block header.
use util::*; use util::*;
use basic_types::*; use basic_types::{LogBloom, Seal, ZERO_LOGBLOOM};
use time::get_time; use time::get_time;
use rlp::*; use rlp::*;

View File

@ -15,9 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use super::test_common::*; use super::test_common::*;
use action_params::ActionParams;
use state::{State, Substate}; use state::{State, Substate};
use executive::*; use executive::*;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use evm; use evm;
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*; use externalities::*;

View File

@ -37,6 +37,5 @@ declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"} declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"} declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"} declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
// TODO [ToDr] uncomment as soon as eip150 tests are merged to develop branch of ethereum/tests declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"} declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}

View File

@ -17,6 +17,7 @@
use super::test_common::*; use super::test_common::*;
use tests::helpers::*; use tests::helpers::*;
use pod_state::{self, PodState}; use pod_state::{self, PodState};
use log_entry::LogEntry;
use ethereum; use ethereum;
use ethjson; use ethjson;

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub use common::*; pub use util::*;
macro_rules! test { macro_rules! test {
($name: expr) => { ($name: expr) => {

View File

@ -18,6 +18,7 @@ use super::test_common::*;
use evm; use evm;
use ethjson; use ethjson;
use rlp::{UntrustedRlp, View}; use rlp::{UntrustedRlp, View};
use transaction::{Action, SignedTransaction};
fn do_json_test(json_data: &[u8]) -> Vec<String> { fn do_json_test(json_data: &[u8]) -> Vec<String> {
let tests = ethjson::transaction::Test::load(json_data).unwrap(); let tests = ethjson::transaction::Test::load(json_data).unwrap();

View File

@ -140,7 +140,6 @@ pub mod db;
mod cache_manager; mod cache_manager;
mod blooms; mod blooms;
mod common;
mod basic_types; mod basic_types;
mod env_info; mod env_info;
mod pod_account; mod pod_account;

View File

@ -154,7 +154,7 @@ impl OverlayRecentV7 {
// and commit the altered entries. // and commit the altered entries.
fn migrate_journal(&self, source: Arc<Database>, mut batch: Batch, dest: &mut Database) -> Result<(), Error> { fn migrate_journal(&self, source: Arc<Database>, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) { if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.to_owned(), dest)); try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().to_vec(), dest));
let mut era = decode::<u64>(&val); let mut era = decode::<u64>(&val);
loop { loop {

View File

@ -163,7 +163,7 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use common::*; use util::*;
use types::account_diff::*; use types::account_diff::*;
use super::{PodAccount, diff_pod}; use super::{PodAccount, diff_pod};

View File

@ -77,7 +77,7 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use common::*; use util::*;
use types::state_diff::*; use types::state_diff::*;
use types::account_diff::*; use types::account_diff::*;
use pod_account::PodAccount; use pod_account::PodAccount;

View File

@ -19,7 +19,7 @@
use account_db::{AccountDB, AccountDBMut}; use account_db::{AccountDB, AccountDBMut};
use snapshot::Error; use snapshot::Error;
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP}; use util::{U256, FixedHash, H256, Bytes, HashDB, DBValue, SHA3_EMPTY, SHA3_NULL_RLP};
use util::trie::{TrieDB, Trie}; use util::trie::{TrieDB, Trie};
use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View}; use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
@ -112,7 +112,7 @@ impl Account {
let mut stream = RlpStream::new_list(pairs.len()); let mut stream = RlpStream::new_list(pairs.len());
for (k, v) in pairs { for (k, v) in pairs {
stream.begin_list(2).append(&k).append(&v); stream.begin_list(2).append(&k).append(&&*v);
} }
let pairs_rlp = stream.out(); let pairs_rlp = stream.out();
@ -130,7 +130,7 @@ impl Account {
match acct_db.get(&self.code_hash) { match acct_db.get(&self.code_hash) {
Some(c) => { Some(c) => {
used_code.insert(self.code_hash.clone()); used_code.insert(self.code_hash.clone());
account_stream.append(&CodeState::Inline.raw()).append(&c); account_stream.append(&CodeState::Inline.raw()).append(&&*c);
} }
None => { None => {
warn!("code lookup failed during snapshot"); warn!("code lookup failed during snapshot");
@ -178,7 +178,7 @@ impl Account {
CodeState::Hash => { CodeState::Hash => {
let code_hash = try!(rlp.val_at(3)); let code_hash = try!(rlp.val_at(3));
if let Some(code) = code_map.get(&code_hash) { if let Some(code) = code_map.get(&code_hash) {
acct_db.emplace(code_hash.clone(), code.clone()); acct_db.emplace(code_hash.clone(), DBValue::from_slice(&code));
} }
(code_hash, None) (code_hash, None)
@ -226,7 +226,7 @@ mod tests {
use snapshot::tests::helpers::fill_storage; use snapshot::tests::helpers::fill_storage;
use util::sha3::{SHA3_EMPTY, SHA3_NULL_RLP}; use util::sha3::{SHA3_EMPTY, SHA3_NULL_RLP};
use util::{Address, FixedHash, H256, HashDB}; use util::{Address, FixedHash, H256, HashDB, DBValue};
use rlp::{UntrustedRlp, View}; use rlp::{UntrustedRlp, View};
use std::collections::{HashSet, HashMap}; use std::collections::{HashSet, HashMap};
@ -292,7 +292,7 @@ mod tests {
{ {
let mut acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr2); let mut acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr2);
acct_db.emplace(code_hash.clone(), b"this is definitely code".to_vec()); acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code"));
} }
let account1 = Account { let account1 = Account {

View File

@ -29,8 +29,7 @@ use engines::Engine;
use ids::BlockID; use ids::BlockID;
use views::BlockView; use views::BlockView;
use util::{Bytes, Hashable, HashDB, snappy, U256, Uint}; use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256, Uint};
use util::memorydb::MemoryDB;
use util::Mutex; use util::Mutex;
use util::hash::{FixedHash, H256}; use util::hash::{FixedHash, H256};
use util::journaldb::{self, Algorithm, JournalDB}; use util::journaldb::{self, Algorithm, JournalDB};
@ -38,6 +37,7 @@ use util::kvdb::Database;
use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut}; use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut};
use util::sha3::SHA3_NULL_RLP; use util::sha3::SHA3_NULL_RLP;
use rlp::{RlpStream, Stream, UntrustedRlp, View}; use rlp::{RlpStream, Stream, UntrustedRlp, View};
use bloom_journal::Bloom;
use self::account::Account; use self::account::Account;
use self::block::AbridgedBlock; use self::block::AbridgedBlock;
@ -46,7 +46,7 @@ use self::io::SnapshotWriter;
use super::state_db::StateDB; use super::state_db::StateDB;
use super::state::Account as StateAccount; use super::state::Account as StateAccount;
use crossbeam::{scope, ScopedJoinHandle}; use crossbeam::scope;
use rand::{Rng, OsRng}; use rand::{Rng, OsRng};
pub use self::error::Error; pub use self::error::Error;
@ -136,7 +136,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
let writer = Mutex::new(writer); let writer = Mutex::new(writer);
let (state_hashes, block_hashes) = try!(scope(|scope| { let (state_hashes, block_hashes) = try!(scope(|scope| {
let block_guard = scope.spawn(|| chunk_blocks(chain, (number, block_at), &writer, p)); let block_guard = scope.spawn(|| chunk_blocks(chain, block_at, &writer, p));
let state_res = chunk_state(state_db, state_root, &writer, p); let state_res = chunk_state(state_db, state_root, &writer, p);
state_res.and_then(|state_hashes| { state_res.and_then(|state_hashes| {
@ -176,10 +176,15 @@ struct BlockChunker<'a> {
impl<'a> BlockChunker<'a> { impl<'a> BlockChunker<'a> {
// Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash. // Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash.
// Loops until we reach the first desired block, and writes out the remainder. // Loops until we reach the first desired block, and writes out the remainder.
fn chunk_all(&mut self, first_hash: H256) -> Result<(), Error> { fn chunk_all(&mut self) -> Result<(), Error> {
let mut loaded_size = 0; let mut loaded_size = 0;
let mut last = self.current_hash;
let genesis_hash = self.chain.genesis_hash();
for _ in 0..SNAPSHOT_BLOCKS {
if self.current_hash == genesis_hash { break }
while self.current_hash != first_hash {
let (block, receipts) = try!(self.chain.block(&self.current_hash) let (block, receipts) = try!(self.chain.block(&self.current_hash)
.and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r))) .and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r)))
.ok_or(Error::BlockNotFound(self.current_hash))); .ok_or(Error::BlockNotFound(self.current_hash)));
@ -197,21 +202,21 @@ impl<'a> BlockChunker<'a> {
// cut off the chunk if too large. // cut off the chunk if too large.
if new_loaded_size > PREFERRED_CHUNK_SIZE { if new_loaded_size > PREFERRED_CHUNK_SIZE && self.rlps.len() > 0 {
try!(self.write_chunk()); try!(self.write_chunk(last));
loaded_size = pair.len(); loaded_size = pair.len();
} else { } else {
loaded_size = new_loaded_size; loaded_size = new_loaded_size;
} }
self.rlps.push_front(pair); self.rlps.push_front(pair);
last = self.current_hash;
self.current_hash = view.header_view().parent_hash(); self.current_hash = view.header_view().parent_hash();
} }
if loaded_size != 0 { if loaded_size != 0 {
// we don't store the first block, so once we get to this point, try!(self.write_chunk(last));
// the "first" block will be first_number + 1.
try!(self.write_chunk());
} }
Ok(()) Ok(())
@ -219,23 +224,24 @@ impl<'a> BlockChunker<'a> {
// write out the data in the buffers to a chunk on disk // write out the data in the buffers to a chunk on disk
// //
// we preface each chunk with the parent of the first block's details. // we preface each chunk with the parent of the first block's details,
fn write_chunk(&mut self) -> Result<(), Error> { // obtained from the details of the last block written.
// since the block we're inspecting now doesn't go into the fn write_chunk(&mut self, last: H256) -> Result<(), Error> {
// chunk if it's too large, the current hash is the parent hash
// for the first block in that chunk.
let parent_hash = self.current_hash;
trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len()); trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len());
let (parent_number, parent_details) = try!(self.chain.block_number(&parent_hash)
.and_then(|n| self.chain.block_details(&parent_hash).map(|d| (n, d)))
.ok_or(Error::BlockNotFound(parent_hash)));
let parent_total_difficulty = parent_details.total_difficulty; let (last_header, last_details) = try!(self.chain.block_header(&last)
.and_then(|n| self.chain.block_details(&last).map(|d| (n, d)))
.ok_or(Error::BlockNotFound(last)));
let parent_number = last_header.number() - 1;
let parent_hash = last_header.parent_hash();
let parent_total_difficulty = last_details.total_difficulty - *last_header.difficulty();
trace!(target: "snapshot", "parent last written block: {}", parent_hash);
let num_entries = self.rlps.len(); let num_entries = self.rlps.len();
let mut rlp_stream = RlpStream::new_list(3 + num_entries); let mut rlp_stream = RlpStream::new_list(3 + num_entries);
rlp_stream.append(&parent_number).append(&parent_hash).append(&parent_total_difficulty); rlp_stream.append(&parent_number).append(parent_hash).append(&parent_total_difficulty);
for pair in self.rlps.drain(..) { for pair in self.rlps.drain(..) {
rlp_stream.append_raw(&pair, 1); rlp_stream.append_raw(&pair, 1);
@ -264,17 +270,7 @@ impl<'a> BlockChunker<'a> {
/// The path parameter is the directory to store the block chunks in. /// The path parameter is the directory to store the block chunks in.
/// This function assumes the directory exists already. /// This function assumes the directory exists already.
/// Returns a list of chunk hashes, with the first having the blocks furthest from the genesis. /// Returns a list of chunk hashes, with the first having the blocks furthest from the genesis.
pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> { pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_hash: H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
let (start_number, start_hash) = start_block_info;
let first_hash = if start_number < SNAPSHOT_BLOCKS {
// use the genesis hash.
chain.genesis_hash()
} else {
let first_num = start_number - SNAPSHOT_BLOCKS;
try!(chain.block_hash(first_num).ok_or(Error::IncompleteChain))
};
let mut chunker = BlockChunker { let mut chunker = BlockChunker {
chain: chain, chain: chain,
rlps: VecDeque::new(), rlps: VecDeque::new(),
@ -285,7 +281,7 @@ pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), wr
progress: progress, progress: progress,
}; };
try!(chunker.chunk_all(first_hash)); try!(chunker.chunk_all());
Ok(chunker.hashes) Ok(chunker.hashes)
} }
@ -372,7 +368,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
// account_key here is the address' hash. // account_key here is the address' hash.
for item in try!(account_trie.iter()) { for item in try!(account_trie.iter()) {
let (account_key, account_data) = try!(item); let (account_key, account_data) = try!(item);
let account = Account::from_thin_rlp(account_data); let account = Account::from_thin_rlp(&*account_data);
let account_key_hash = H256::from_slice(&account_key); let account_key_hash = H256::from_slice(&account_key);
let account_db = AccountDB::from_hash(db, account_key_hash); let account_db = AccountDB::from_hash(db, account_key_hash);
@ -394,6 +390,7 @@ pub struct StateRebuilder {
state_root: H256, state_root: H256,
code_map: HashMap<H256, Bytes>, // maps code hashes to code itself. code_map: HashMap<H256, Bytes>, // maps code hashes to code itself.
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code. missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
bloom: Bloom,
} }
impl StateRebuilder { impl StateRebuilder {
@ -404,6 +401,7 @@ impl StateRebuilder {
state_root: SHA3_NULL_RLP, state_root: SHA3_NULL_RLP,
code_map: HashMap::new(), code_map: HashMap::new(),
missing_code: HashMap::new(), missing_code: HashMap::new(),
bloom: StateDB::load_bloom(&*db),
} }
} }
@ -422,43 +420,19 @@ impl StateRebuilder {
// new code contained within this chunk. // new code contained within this chunk.
let mut chunk_code = HashMap::new(); let mut chunk_code = HashMap::new();
// build account tries in parallel. for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
// Todo [rob] keep a thread pool around so we don't do this per-chunk. let code_map = &self.code_map;
try!(scope(|scope| { let status = try!(rebuild_accounts(self.db.as_hashdb_mut(), account_chunk, out_pairs_chunk, code_map));
let mut handles = Vec::new(); chunk_code.extend(status.new_code);
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) { for (addr_hash, code_hash) in status.missing_code {
let code_map = &self.code_map; self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
let handle: ScopedJoinHandle<Result<_, ::error::Error>> = scope.spawn(move || {
let mut db = MemoryDB::new();
let status = try!(rebuild_accounts(&mut db, account_chunk, out_pairs_chunk, code_map));
trace!(target: "snapshot", "thread rebuilt {} account tries", account_chunk.len());
Ok((db, status))
});
handles.push(handle);
} }
}
// consolidate all edits into the main overlay.
for handle in handles {
let (thread_db, status): (MemoryDB, _) = try!(handle.join());
self.db.consolidate(thread_db);
chunk_code.extend(status.new_code);
for (addr_hash, code_hash) in status.missing_code {
self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
}
}
Ok::<_, ::error::Error>(())
}));
// patch up all missing code. must be done after collecting all new missing code entries. // patch up all missing code. must be done after collecting all new missing code entries.
for (code_hash, code) in chunk_code { for (code_hash, code) in chunk_code {
for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) { for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) {
let mut db = AccountDBMut::from_hash(self.db.as_hashdb_mut(), addr_hash); let mut db = AccountDBMut::from_hash(self.db.as_hashdb_mut(), addr_hash);
db.emplace(code_hash, code.clone()); db.emplace(code_hash, DBValue::from_slice(&code));
} }
self.code_map.insert(code_hash, code); self.code_map.insert(code_hash, code);
@ -466,9 +440,6 @@ impl StateRebuilder {
let backing = self.db.backing().clone(); let backing = self.db.backing().clone();
// bloom has to be updated
let mut bloom = StateDB::load_bloom(&backing);
// batch trie writes // batch trie writes
{ {
let mut account_trie = if self.state_root != SHA3_NULL_RLP { let mut account_trie = if self.state_root != SHA3_NULL_RLP {
@ -479,17 +450,17 @@ impl StateRebuilder {
for (hash, thin_rlp) in pairs { for (hash, thin_rlp) in pairs {
if &thin_rlp[..] != &empty_rlp[..] { if &thin_rlp[..] != &empty_rlp[..] {
bloom.set(&*hash); self.bloom.set(&*hash);
} }
try!(account_trie.insert(&hash, &thin_rlp)); try!(account_trie.insert(&hash, &thin_rlp));
} }
} }
let bloom_journal = bloom.drain_journal(); let bloom_journal = self.bloom.drain_journal();
let mut batch = backing.transaction(); let mut batch = backing.transaction();
try!(StateDB::commit_bloom(&mut batch, bloom_journal)); try!(StateDB::commit_bloom(&mut batch, bloom_journal));
try!(self.db.inject(&mut batch)); try!(self.db.inject(&mut batch));
try!(backing.write(batch).map_err(::util::UtilError::SimpleString)); backing.write_buffered(batch);
trace!(target: "snapshot", "current state root: {:?}", self.state_root); trace!(target: "snapshot", "current state root: {:?}", self.state_root);
Ok(()) Ok(())
} }
@ -596,7 +567,7 @@ impl BlockRebuilder {
let rlp = UntrustedRlp::new(chunk); let rlp = UntrustedRlp::new(chunk);
let item_count = rlp.item_count(); let item_count = rlp.item_count();
trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 2); trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3);
// todo: assert here that these values are consistent with chunks being in order. // todo: assert here that these values are consistent with chunks being in order.
let mut cur_number = try!(rlp.val_at::<u64>(0)) + 1; let mut cur_number = try!(rlp.val_at::<u64>(0)) + 1;
@ -632,7 +603,7 @@ impl BlockRebuilder {
} else { } else {
self.chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, is_best, false); self.chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, is_best, false);
} }
self.db.write(batch).expect("Error writing to the DB"); self.db.write_buffered(batch);
self.chain.commit(); self.chain.commit();
parent_hash = BlockView::new(&block_bytes).hash(); parent_hash = BlockView::new(&block_bytes).hash();

View File

@ -74,6 +74,7 @@ struct Restoration {
snappy_buffer: Bytes, snappy_buffer: Bytes,
final_state_root: H256, final_state_root: H256,
guard: Guard, guard: Guard,
db: Arc<Database>,
} }
struct RestorationParams<'a> { struct RestorationParams<'a> {
@ -105,12 +106,13 @@ impl Restoration {
manifest: manifest, manifest: manifest,
state_chunks_left: state_chunks, state_chunks_left: state_chunks,
block_chunks_left: block_chunks, block_chunks_left: block_chunks,
state: StateRebuilder::new(raw_db, params.pruning), state: StateRebuilder::new(raw_db.clone(), params.pruning),
blocks: blocks, blocks: blocks,
writer: params.writer, writer: params.writer,
snappy_buffer: Vec::new(), snappy_buffer: Vec::new(),
final_state_root: root, final_state_root: root,
guard: params.guard, guard: params.guard,
db: raw_db,
}) })
} }
@ -467,39 +469,47 @@ impl Service {
/// Feed a chunk of either kind. no-op if no restoration or status is wrong. /// Feed a chunk of either kind. no-op if no restoration or status is wrong.
fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> { fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> {
// TODO: be able to process block chunks and state chunks at same time? // TODO: be able to process block chunks and state chunks at same time?
let mut restoration = self.restoration.lock(); let (result, db) = {
let mut restoration = self.restoration.lock();
match self.status() { match self.status() {
RestorationStatus::Inactive | RestorationStatus::Failed => Ok(()), RestorationStatus::Inactive | RestorationStatus::Failed => return Ok(()),
RestorationStatus::Ongoing { .. } => { RestorationStatus::Ongoing { .. } => {
let res = { let (res, db) = {
let rest = match *restoration { let rest = match *restoration {
Some(ref mut r) => r, Some(ref mut r) => r,
None => return Ok(()), None => return Ok(()),
};
match is_state {
true => rest.feed_state(hash, chunk),
false => rest.feed_blocks(hash, chunk, &*self.engine),
}.map(|_| rest.is_done())
};
match res {
Ok(is_done) => {
match is_state {
true => self.state_chunks.fetch_add(1, Ordering::SeqCst),
false => self.block_chunks.fetch_add(1, Ordering::SeqCst),
}; };
match is_done { (match is_state {
true => self.finalize_restoration(&mut *restoration), true => rest.feed_state(hash, chunk),
false => Ok(()) false => rest.feed_blocks(hash, chunk, &*self.engine),
}.map(|_| rest.is_done()), rest.db.clone())
};
let res = match res {
Ok(is_done) => {
match is_state {
true => self.state_chunks.fetch_add(1, Ordering::SeqCst),
false => self.block_chunks.fetch_add(1, Ordering::SeqCst),
};
match is_done {
true => {
try!(db.flush().map_err(::util::UtilError::SimpleString));
drop(db);
return self.finalize_restoration(&mut *restoration);
},
false => Ok(())
}
} }
} other => other.map(drop),
other => other.map(drop), };
(res, db)
} }
} }
} };
result.and_then(|_| db.flush().map_err(|e| ::util::UtilError::SimpleString(e).into()))
} }
/// Feed a state chunk to be processed synchronously. /// Feed a state chunk to be processed synchronously.
@ -549,8 +559,9 @@ impl SnapshotService for Service {
} }
fn begin_restore(&self, manifest: ManifestData) { fn begin_restore(&self, manifest: ManifestData) {
self.io_channel.send(ClientIoMessage::BeginRestoration(manifest)) if let Err(e) = self.io_channel.send(ClientIoMessage::BeginRestoration(manifest)) {
.expect("snapshot service and io service are kept alive by client service; qed"); trace!("Error sending snapshot service message: {:?}", e);
}
} }
fn abort_restore(&self) { fn abort_restore(&self) {
@ -559,13 +570,15 @@ impl SnapshotService for Service {
} }
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) { fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk)) if let Err(e) = self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk)) {
.expect("snapshot service and io service are kept alive by client service; qed"); trace!("Error sending snapshot service message: {:?}", e);
}
} }
fn restore_block_chunk(&self, hash: H256, chunk: Bytes) { fn restore_block_chunk(&self, hash: H256, chunk: Bytes) {
self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk)) if let Err(e) = self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk)) {
.expect("snapshot service and io service are kept alive by client service; qed"); trace!("Error sending snapshot service message: {:?}", e);
}
} }
} }

View File

@ -57,7 +57,7 @@ fn chunk_and_restore(amount: u64) {
// snapshot it. // snapshot it.
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
let block_hashes = chunk_blocks(&bc, (amount, best_hash), &writer, &Progress::default()).unwrap(); let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap();
writer.into_inner().finish(::snapshot::ManifestData { writer.into_inner().finish(::snapshot::ManifestData {
state_hashes: Vec::new(), state_hashes: Vec::new(),
block_hashes: block_hashes, block_hashes: block_hashes,

View File

@ -21,6 +21,7 @@ use account_db::AccountDBMut;
use rand::Rng; use rand::Rng;
use snapshot::account::Account; use snapshot::account::Account;
use util::DBValue;
use util::hash::{FixedHash, H256}; use util::hash::{FixedHash, H256};
use util::hashdb::HashDB; use util::hashdb::HashDB;
use util::trie::{Alphabet, StandardMap, SecTrieDBMut, TrieMut, ValueMode}; use util::trie::{Alphabet, StandardMap, SecTrieDBMut, TrieMut, ValueMode};
@ -66,7 +67,7 @@ impl StateProducer {
let mut account = Account::from_thin_rlp(&*account_data); let mut account = Account::from_thin_rlp(&*account_data);
let acct_db = AccountDBMut::from_hash(db, *address_hash); let acct_db = AccountDBMut::from_hash(db, *address_hash);
fill_storage(acct_db, account.storage_root_mut(), &mut self.storage_seed); fill_storage(acct_db, account.storage_root_mut(), &mut self.storage_seed);
*account_data = account.to_thin_rlp(); *account_data = DBValue::from_vec(account.to_thin_rlp());
} }
// sweep again to alter account trie. // sweep again to alter account trie.

View File

@ -16,10 +16,12 @@
//! Parameters for a block chain. //! Parameters for a block chain.
use common::*; use util::*;
use builtin::Builtin;
use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound}; use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound};
use pod_state::*; use pod_state::*;
use account_db::*; use account_db::*;
use header::{BlockNumber, Header};
use state_db::StateDB; use state_db::StateDB;
use super::genesis::Genesis; use super::genesis::Genesis;
use super::seal::Generic as GenericSeal; use super::seal::Generic as GenericSeal;

View File

@ -172,7 +172,7 @@ impl Account {
using it will not fail."); using it will not fail.");
let item: U256 = match db.get(key){ let item: U256 = match db.get(key){
Ok(x) => x.map_or_else(U256::zero, decode), Ok(x) => x.map_or_else(U256::zero, |v| decode(&*v)),
Err(e) => panic!("Encountered potential DB corruption: {}", e), Err(e) => panic!("Encountered potential DB corruption: {}", e),
}; };
let value: H256 = item.into(); let value: H256 = item.into();
@ -253,8 +253,8 @@ impl Account {
self.is_cached() || self.is_cached() ||
match db.get(&self.code_hash) { match db.get(&self.code_hash) {
Some(x) => { Some(x) => {
self.code_cache = Arc::new(x.to_vec());
self.code_size = Some(x.len()); self.code_size = Some(x.len());
self.code_cache = Arc::new(x.to_vec());
true true
}, },
_ => { _ => {
@ -351,7 +351,7 @@ impl Account {
self.code_filth = Filth::Clean; self.code_filth = Filth::Clean;
}, },
(true, false) => { (true, false) => {
db.emplace(self.code_hash.clone(), (*self.code_cache).clone()); db.emplace(self.code_hash.clone(), DBValue::from_slice(&*self.code_cache));
self.code_size = Some(self.code_cache.len()); self.code_size = Some(self.code_cache.len());
self.code_filth = Filth::Clean; self.code_filth = Filth::Clean;
}, },

View File

@ -16,14 +16,18 @@
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use common::*; use util::*;
use receipt::Receipt;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use error::Error;
use executive::{Executive, TransactOptions}; use executive::{Executive, TransactOptions};
use factory::Factories; use factory::Factories;
use trace::FlatTrace; use trace::FlatTrace;
use pod_account::*; use pod_account::*;
use pod_state::{self, PodState}; use pod_state::{self, PodState};
use types::state_diff::StateDiff; use types::state_diff::StateDiff;
use transaction::SignedTransaction;
use state_db::StateDB; use state_db::StateDB;
mod account; mod account;
@ -167,24 +171,24 @@ impl AccountEntry {
/// Upon destruction all the local cache data propagated into the global cache. /// Upon destruction all the local cache data propagated into the global cache.
/// Propagated items might be rejected if current state is non-canonical. /// Propagated items might be rejected if current state is non-canonical.
/// ///
/// State snapshotting. /// State checkpointing.
/// ///
/// A new snapshot can be created with `snapshot()`. Snapshots can be /// A new checkpoint can be created with `checkpoint()`. checkpoints can be
/// created in a hierarchy. /// created in a hierarchy.
/// When a snapshot is active all changes are applied directly into /// When a checkpoint is active all changes are applied directly into
/// `cache` and the original value is copied into an active snapshot. /// `cache` and the original value is copied into an active checkpoint.
/// Reverting a snapshot with `revert_to_snapshot` involves copying /// Reverting a checkpoint with `revert_to_checkpoint` involves copying
/// original values from the latest snapshot back into `cache`. The code /// original values from the latest checkpoint back into `cache`. The code
/// takes care not to overwrite cached storage while doing that. /// takes care not to overwrite cached storage while doing that.
/// Snapshot can be discateded with `discard_snapshot`. All of the orignal /// checkpoint can be discateded with `discard_checkpoint`. All of the orignal
/// backed-up values are moved into a parent snapshot (if any). /// backed-up values are moved into a parent checkpoint (if any).
/// ///
pub struct State { pub struct State {
db: StateDB, db: StateDB,
root: H256, root: H256,
cache: RefCell<HashMap<Address, AccountEntry>>, cache: RefCell<HashMap<Address, AccountEntry>>,
// The original account is preserved in // The original account is preserved in
snapshots: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>, checkpoints: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
account_start_nonce: U256, account_start_nonce: U256,
factories: Factories, factories: Factories,
} }
@ -213,7 +217,7 @@ impl State {
db: db, db: db,
root: root, root: root,
cache: RefCell::new(HashMap::new()), cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()), checkpoints: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce, account_start_nonce: account_start_nonce,
factories: factories, factories: factories,
} }
@ -229,7 +233,7 @@ impl State {
db: db, db: db,
root: root, root: root,
cache: RefCell::new(HashMap::new()), cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()), checkpoints: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce, account_start_nonce: account_start_nonce,
factories: factories factories: factories
}; };
@ -237,21 +241,21 @@ impl State {
Ok(state) Ok(state)
} }
/// Create a recoverable snaphot of this state. /// Create a recoverable checkpoint of this state.
pub fn snapshot(&mut self) { pub fn checkpoint(&mut self) {
self.snapshots.get_mut().push(HashMap::new()); self.checkpoints.get_mut().push(HashMap::new());
} }
/// Merge last snapshot with previous. /// Merge last checkpoint with previous.
pub fn discard_snapshot(&mut self) { pub fn discard_checkpoint(&mut self) {
// merge with previous snapshot // merge with previous checkpoint
let last = self.snapshots.get_mut().pop(); let last = self.checkpoints.get_mut().pop();
if let Some(mut snapshot) = last { if let Some(mut checkpoint) = last {
if let Some(ref mut prev) = self.snapshots.get_mut().last_mut() { if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() {
if prev.is_empty() { if prev.is_empty() {
**prev = snapshot; **prev = checkpoint;
} else { } else {
for (k, v) in snapshot.drain() { for (k, v) in checkpoint.drain() {
prev.entry(k).or_insert(v); prev.entry(k).or_insert(v);
} }
} }
@ -259,15 +263,15 @@ impl State {
} }
} }
/// Revert to the last snapshot and discard it. /// Revert to the last checkpoint and discard it.
pub fn revert_to_snapshot(&mut self) { pub fn revert_to_checkpoint(&mut self) {
if let Some(mut snapshot) = self.snapshots.get_mut().pop() { if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() {
for (k, v) in snapshot.drain() { for (k, v) in checkpoint.drain() {
match v { match v {
Some(v) => { Some(v) => {
match self.cache.get_mut().entry(k) { match self.cache.get_mut().entry(k) {
Entry::Occupied(mut e) => { Entry::Occupied(mut e) => {
// Merge snapshotted changes back into the main account // Merge checkpointed changes back into the main account
// storage preserving the cache. // storage preserving the cache.
e.get_mut().overwrite_with(v); e.get_mut().overwrite_with(v);
}, },
@ -293,14 +297,14 @@ impl State {
fn insert_cache(&self, address: &Address, account: AccountEntry) { fn insert_cache(&self, address: &Address, account: AccountEntry) {
// Dirty account which is not in the cache means this is a new account. // Dirty account which is not in the cache means this is a new account.
// It goes directly into the snapshot as there's nothing to rever to. // It goes directly into the checkpoint as there's nothing to rever to.
// //
// In all other cases account is read as clean first, and after that made // In all other cases account is read as clean first, and after that made
// dirty in and added to the snapshot with `note_cache`. // dirty in and added to the checkpoint with `note_cache`.
if account.is_dirty() { if account.is_dirty() {
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() { if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() {
if !snapshot.contains_key(address) { if !checkpoint.contains_key(address) {
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account)); checkpoint.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
return; return;
} }
} }
@ -309,9 +313,9 @@ impl State {
} }
fn note_cache(&self, address: &Address) { fn note_cache(&self, address: &Address) {
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() { if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() {
if !snapshot.contains_key(address) { if !checkpoint.contains_key(address) {
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); checkpoint.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_dirty));
} }
} }
} }
@ -404,7 +408,7 @@ impl State {
// account is not found in the global cache, get from the DB and insert into local // account is not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(address) { let maybe_acc = match db.get(address) {
Ok(acc) => acc.map(Account::from_rlp), Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
Err(e) => panic!("Potential DB corruption encountered: {}", e), Err(e) => panic!("Potential DB corruption encountered: {}", e),
}; };
let r = maybe_acc.as_ref().map_or(H256::new(), |a| { let r = maybe_acc.as_ref().map_or(H256::new(), |a| {
@ -548,7 +552,7 @@ impl State {
/// Commits our cached account changes into the trie. /// Commits our cached account changes into the trie.
pub fn commit(&mut self) -> Result<(), Error> { pub fn commit(&mut self) -> Result<(), Error> {
assert!(self.snapshots.borrow().is_empty()); assert!(self.checkpoints.borrow().is_empty());
Self::commit_into(&self.factories, &mut self.db, &mut self.root, &mut *self.cache.borrow_mut()) Self::commit_into(&self.factories, &mut self.db, &mut self.root, &mut *self.cache.borrow_mut())
} }
@ -561,7 +565,7 @@ impl State {
#[cfg(feature = "json-tests")] #[cfg(feature = "json-tests")]
/// Populate the state from `accounts`. /// Populate the state from `accounts`.
pub fn populate_from(&mut self, accounts: PodState) { pub fn populate_from(&mut self, accounts: PodState) {
assert!(self.snapshots.borrow().is_empty()); assert!(self.checkpoints.borrow().is_empty());
for (add, acc) in accounts.drain().into_iter() { for (add, acc) in accounts.drain().into_iter() {
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
} }
@ -569,7 +573,7 @@ impl State {
/// Populate a PodAccount map from this state. /// Populate a PodAccount map from this state.
pub fn to_pod(&self) -> PodState { pub fn to_pod(&self) -> PodState {
assert!(self.snapshots.borrow().is_empty()); assert!(self.checkpoints.borrow().is_empty());
// TODO: handle database rather than just the cache. // TODO: handle database rather than just the cache.
// will need fat db. // will need fat db.
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
@ -644,7 +648,7 @@ impl State {
// not found in the global cache, get from the DB and insert into local // not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let mut maybe_acc = match db.get(a) { let mut maybe_acc = match db.get(a) {
Ok(acc) => acc.map(Account::from_rlp), Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
Err(e) => panic!("Potential DB corruption encountered: {}", e), Err(e) => panic!("Potential DB corruption encountered: {}", e),
}; };
if let Some(ref mut account) = maybe_acc.as_mut() { if let Some(ref mut account) = maybe_acc.as_mut() {
@ -676,7 +680,7 @@ impl State {
let maybe_acc = if self.db.check_account_bloom(a) { let maybe_acc = if self.db.check_account_bloom(a) {
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(a) { let maybe_acc = match db.get(a) {
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(acc))), Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
Ok(None) => AccountEntry::new_clean(None), Ok(None) => AccountEntry::new_clean(None),
Err(e) => panic!("Potential DB corruption encountered: {}", e), Err(e) => panic!("Potential DB corruption encountered: {}", e),
}; };
@ -739,7 +743,7 @@ impl Clone for State {
db: self.db.boxed_clone(), db: self.db.boxed_clone(),
root: self.root.clone(), root: self.root.clone(),
cache: RefCell::new(cache), cache: RefCell::new(cache),
snapshots: RefCell::new(Vec::new()), checkpoints: RefCell::new(Vec::new()),
account_start_nonce: self.account_start_nonce.clone(), account_start_nonce: self.account_start_nonce.clone(),
factories: self.factories.clone(), factories: self.factories.clone(),
} }
@ -756,7 +760,7 @@ use super::*;
use util::{U256, H256, FixedHash, Address, Hashable}; use util::{U256, H256, FixedHash, Address, Hashable};
use tests::helpers::*; use tests::helpers::*;
use devtools::*; use devtools::*;
use env_info::*; use env_info::EnvInfo;
use spec::*; use spec::*;
use transaction::*; use transaction::*;
use util::log::init_log; use util::log::init_log;
@ -1777,34 +1781,34 @@ fn ensure_cached() {
} }
#[test] #[test]
fn snapshot_basic() { fn checkpoint_basic() {
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
let a = Address::zero(); let a = Address::zero();
state.snapshot(); state.checkpoint();
state.add_balance(&a, &U256::from(69u64)); state.add_balance(&a, &U256::from(69u64));
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.discard_snapshot(); state.discard_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.snapshot(); state.checkpoint();
state.add_balance(&a, &U256::from(1u64)); state.add_balance(&a, &U256::from(1u64));
assert_eq!(state.balance(&a), U256::from(70u64)); assert_eq!(state.balance(&a), U256::from(70u64));
state.revert_to_snapshot(); state.revert_to_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
} }
#[test] #[test]
fn snapshot_nested() { fn checkpoint_nested() {
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
let a = Address::zero(); let a = Address::zero();
state.snapshot(); state.checkpoint();
state.snapshot(); state.checkpoint();
state.add_balance(&a, &U256::from(69u64)); state.add_balance(&a, &U256::from(69u64));
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.discard_snapshot(); state.discard_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.revert_to_snapshot(); state.revert_to_checkpoint();
assert_eq!(state.balance(&a), U256::from(0)); assert_eq!(state.balance(&a), U256::from(0));
} }

View File

@ -20,11 +20,12 @@ use ethereum;
use block::IsBlock; use block::IsBlock;
use tests::helpers::*; use tests::helpers::*;
use types::filter::Filter; use types::filter::Filter;
use common::*; use util::*;
use devtools::*; use devtools::*;
use miner::Miner; use miner::Miner;
use rlp::{Rlp, View}; use rlp::{Rlp, View};
use spec::Spec; use spec::Spec;
use views::BlockView;
#[test] #[test]
fn imports_from_empty() { fn imports_from_empty() {

View File

@ -17,18 +17,22 @@
use ethkey::KeyPair; use ethkey::KeyPair;
use io::*; use io::*;
use client::{BlockChainClient, Client, ClientConfig}; use client::{BlockChainClient, Client, ClientConfig};
use common::*; use util::*;
use spec::*; use spec::*;
use state_db::StateDB; use state_db::StateDB;
use block::{OpenBlock, Drain}; use block::{OpenBlock, Drain};
use blockchain::{BlockChain, Config as BlockChainConfig}; use blockchain::{BlockChain, Config as BlockChainConfig};
use builtin::Builtin;
use evm::Schedule; use evm::Schedule;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo;
use ethereum; use ethereum;
use devtools::*; use devtools::*;
use miner::Miner; use miner::Miner;
use header::Header;
use transaction::{Action, SignedTransaction, Transaction};
use rlp::{self, RlpStream, Stream}; use rlp::{self, RlpStream, Stream};
use db::COL_STATE; use views::BlockView;
#[cfg(feature = "json-tests")] #[cfg(feature = "json-tests")]
pub enum ChainEra { pub enum ChainEra {
@ -346,7 +350,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
pub fn get_temp_state_db_in(path: &Path) -> StateDB { pub fn get_temp_state_db_in(path: &Path) -> StateDB {
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests.")); let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, COL_STATE); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, ::db::COL_STATE);
StateDB::new(journal_db, 5 * 1024 * 1024) StateDB::new(journal_db, 5 * 1024 * 1024)
} }

View File

@ -31,6 +31,8 @@ pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, Queu
pub enum VerifierType { pub enum VerifierType {
/// Verifies block normally. /// Verifies block normally.
Canon, Canon,
/// Verifies block normallly, but skips seal verification.
CanonNoSeal,
/// Does not verify block at all. /// Does not verify block at all.
/// Used in tests. /// Used in tests.
Noop, Noop,
@ -44,7 +46,17 @@ impl Default for VerifierType {
pub fn new(v: VerifierType) -> Box<Verifier> { pub fn new(v: VerifierType) -> Box<Verifier> {
match v { match v {
VerifierType::Canon => Box::new(CanonVerifier), VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier),
VerifierType::Noop => Box::new(NoopVerifier), VerifierType::Noop => Box::new(NoopVerifier),
} }
} }
impl VerifierType {
/// Check if seal verification is enabled for this verifier type.
pub fn verifying_seal(&self) -> bool {
match *self {
VerifierType::Canon => true,
VerifierType::Noop | VerifierType::CanonNoSeal => false,
}
}
}

View File

@ -57,7 +57,7 @@ pub trait Kind: 'static + Sized + Send + Sync {
fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error>; fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error>;
/// Attempt to verify the `Unverified` item using the given engine. /// Attempt to verify the `Unverified` item using the given engine.
fn verify(unverified: Self::Unverified, engine: &Engine) -> Result<Self::Verified, Error>; fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error>;
} }
/// The blocks verification module. /// The blocks verification module.
@ -89,9 +89,9 @@ pub mod blocks {
} }
} }
fn verify(un: Self::Unverified, engine: &Engine) -> Result<Self::Verified, Error> { fn verify(un: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> {
let hash = un.hash(); let hash = un.hash();
match verify_block_unordered(un.header, un.bytes, engine) { match verify_block_unordered(un.header, un.bytes, engine, check_seal) {
Ok(verified) => Ok(verified), Ok(verified) => Ok(verified),
Err(e) => { Err(e) => {
warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e); warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e);
@ -176,8 +176,11 @@ pub mod headers {
verify_header_params(&input, engine).map(|_| input) verify_header_params(&input, engine).map(|_| input)
} }
fn verify(unverified: Self::Unverified, engine: &Engine) -> Result<Self::Verified, Error> { fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> {
engine.verify_block_unordered(&unverified, None).map(|_| unverified) match check_seal {
true => engine.verify_block_unordered(&unverified, None).map(|_| unverified),
false => Ok(unverified),
}
} }
} }
} }

View File

@ -159,7 +159,7 @@ struct Verification<K: Kind> {
impl<K: Kind> VerificationQueue<K> { impl<K: Kind> VerificationQueue<K> {
/// Creates a new queue instance. /// Creates a new queue instance.
pub fn new(config: Config, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>) -> Self { pub fn new(config: Config, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>, check_seal: bool) -> Self {
let verification = Arc::new(Verification { let verification = Arc::new(Verification {
unverified: Mutex::new(VecDeque::new()), unverified: Mutex::new(VecDeque::new()),
verifying: Mutex::new(VecDeque::new()), verifying: Mutex::new(VecDeque::new()),
@ -198,7 +198,7 @@ impl<K: Kind> VerificationQueue<K> {
.name(format!("Verifier #{}", i)) .name(format!("Verifier #{}", i))
.spawn(move || { .spawn(move || {
panic_handler.catch_panic(move || { panic_handler.catch_panic(move || {
VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty) VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty, check_seal)
}).unwrap() }).unwrap()
}) })
.expect("Error starting block verification thread") .expect("Error starting block verification thread")
@ -219,7 +219,7 @@ impl<K: Kind> VerificationQueue<K> {
} }
} }
fn verify(verification: Arc<Verification<K>>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>) { fn verify(verification: Arc<Verification<K>>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>, check_seal: bool) {
while !deleting.load(AtomicOrdering::Acquire) { while !deleting.load(AtomicOrdering::Acquire) {
{ {
let mut more_to_verify = verification.more_to_verify.lock().unwrap(); let mut more_to_verify = verification.more_to_verify.lock().unwrap();
@ -253,7 +253,7 @@ impl<K: Kind> VerificationQueue<K> {
}; };
let hash = item.hash(); let hash = item.hash();
let is_ready = match K::verify(item, &*engine) { let is_ready = match K::verify(item, &*engine, check_seal) {
Ok(verified) => { Ok(verified) => {
let mut verifying = verification.verifying.lock(); let mut verifying = verification.verifying.lock();
let mut idx = None; let mut idx = None;
@ -529,7 +529,7 @@ mod tests {
fn get_test_queue() -> BlockQueue { fn get_test_queue() -> BlockQueue {
let spec = get_test_spec(); let spec = get_test_spec();
let engine = spec.engine; let engine = spec.engine;
BlockQueue::new(Config::default(), engine, IoChannel::disconnected()) BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true)
} }
#[test] #[test]
@ -537,7 +537,7 @@ mod tests {
// TODO better test // TODO better test
let spec = Spec::new_test(); let spec = Spec::new_test();
let engine = spec.engine; let engine = spec.engine;
let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected()); let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true);
} }
#[test] #[test]
@ -601,7 +601,7 @@ mod tests {
let engine = spec.engine; let engine = spec.engine;
let mut config = Config::default(); let mut config = Config::default();
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000 config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
let queue = BlockQueue::new(config, engine, IoChannel::disconnected()); let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true);
assert!(!queue.queue_info().is_full()); assert!(!queue.queue_info().is_full());
let mut blocks = get_good_dummy_block_seq(50); let mut blocks = get_good_dummy_block_seq(50);
for b in blocks.drain(..) { for b in blocks.drain(..) {

View File

@ -21,10 +21,14 @@
/// 2. Signatures verification done in the queue. /// 2. Signatures verification done in the queue.
/// 3. Final verification against the blockchain done before enactment. /// 3. Final verification against the blockchain done before enactment.
use common::*; use util::*;
use engines::Engine; use engines::Engine;
use error::{BlockError, Error};
use blockchain::*; use blockchain::*;
use header::{BlockNumber, Header};
use rlp::{UntrustedRlp, View}; use rlp::{UntrustedRlp, View};
use transaction::SignedTransaction;
use views::BlockView;
/// Preprocessed block data gathered in `verify_block_unordered` call /// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreverifiedBlock { pub struct PreverifiedBlock {
@ -66,10 +70,12 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block /// Still operates on a individual block
/// Returns a `PreverifiedBlock` structure populated with transactions /// Returns a `PreverifiedBlock` structure populated with transactions
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreverifiedBlock, Error> { pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, check_seal: bool) -> Result<PreverifiedBlock, Error> {
try!(engine.verify_block_unordered(&header, Some(&bytes))); if check_seal {
for u in try!(UntrustedRlp::new(&bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) { try!(engine.verify_block_unordered(&header, Some(&bytes)));
try!(engine.verify_block_unordered(&try!(u), None)); for u in try!(UntrustedRlp::new(&bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
try!(engine.verify_block_unordered(&try!(u), None));
}
} }
// Verify transactions. // Verify transactions.
let mut transactions = Vec::new(); let mut transactions = Vec::new();

View File

@ -166,7 +166,9 @@ pub mod ecies {
/// Encrypt a message with a public key /// Encrypt a message with a public key
pub fn encrypt(public: &Public, shared_mac: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> { pub fn encrypt(public: &Public, shared_mac: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> {
let r = Random.generate().unwrap(); let r = Random.generate()
.expect("context known to have key-generation capabilities; qed");
let z = try!(ecdh::agree(r.secret(), public)); let z = try!(ecdh::agree(r.secret(), public));
let mut key = [0u8; 32]; let mut key = [0u8; 32];
let mut mkey = [0u8; 32]; let mut mkey = [0u8; 32];
@ -201,7 +203,9 @@ pub mod ecies {
/// Encrypt a message with a public key /// Encrypt a message with a public key
pub fn encrypt_single_message(public: &Public, plain: &[u8]) -> Result<Vec<u8>, Error> { pub fn encrypt_single_message(public: &Public, plain: &[u8]) -> Result<Vec<u8>, Error> {
let r = Random.generate().unwrap(); let r = Random.generate()
.expect("context known to have key-generation capabilities");
let z = try!(ecdh::agree(r.secret(), public)); let z = try!(ecdh::agree(r.secret(), public));
let mut key = [0u8; 32]; let mut key = [0u8; 32];
let mut mkey = [0u8; 32]; let mut mkey = [0u8; 32];

View File

@ -18,6 +18,7 @@ docopt = { version = "0.6", optional = true }
time = "0.1.34" time = "0.1.34"
lazy_static = "0.2" lazy_static = "0.2"
itertools = "0.4" itertools = "0.4"
parking_lot = "0.3"
ethcrypto = { path = "../ethcrypto" } ethcrypto = { path = "../ethcrypto" }
[build-dependencies] [build-dependencies]

View File

@ -28,7 +28,9 @@ const IGNORED_FILES: &'static [&'static str] = &["thumbs.db", "address_book.json
fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> { fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> {
use std::ffi; use std::ffi;
use libc; use libc;
let cstr = ffi::CString::new(file_path.to_str().unwrap()).unwrap();
let cstr = try!(ffi::CString::new(&*file_path.to_string_lossy())
.map_err(|_| -1));
match unsafe { libc::chmod(cstr.as_ptr(), libc::S_IWUSR | libc::S_IRUSR) } { match unsafe { libc::chmod(cstr.as_ptr(), libc::S_IWUSR | libc::S_IRUSR) } {
0 => Ok(()), 0 => Ok(()),
x => Err(x), x => Err(x),
@ -63,15 +65,15 @@ impl DiskDirectory {
let paths = try!(fs::read_dir(&self.path)) let paths = try!(fs::read_dir(&self.path))
.flat_map(Result::ok) .flat_map(Result::ok)
.filter(|entry| { .filter(|entry| {
let metadata = entry.metadata(); let metadata = entry.metadata().ok();
let file_name = entry.file_name(); let file_name = entry.file_name();
let name = file_name.to_str().unwrap(); let name = file_name.to_string_lossy();
// filter directories // filter directories
metadata.is_ok() && !metadata.unwrap().is_dir() && metadata.map_or(false, |m| !m.is_dir()) &&
// hidden files // hidden files
!name.starts_with(".") && !name.starts_with(".") &&
// other ignored files // other ignored files
!IGNORED_FILES.contains(&name) !IGNORED_FILES.contains(&&*name)
}) })
.map(|entry| entry.path()) .map(|entry| entry.path())
.collect::<Vec<PathBuf>>(); .collect::<Vec<PathBuf>>();

View File

@ -15,7 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::RwLock;
use std::mem; use std::mem;
use ethkey::KeyPair; use ethkey::KeyPair;
use crypto::KEY_ITERATIONS; use crypto::KEY_ITERATIONS;
@ -26,6 +25,7 @@ use account::SafeAccount;
use {Error, SecretStore}; use {Error, SecretStore};
use json; use json;
use json::UUID; use json::UUID;
use parking_lot::RwLock;
use presale::PresaleWallet; use presale::PresaleWallet;
use import; use import;
@ -56,13 +56,13 @@ impl EthStore {
let account = try!(self.dir.insert(account.clone())); let account = try!(self.dir.insert(account.clone()));
// update cache // update cache
let mut cache = self.cache.write().unwrap(); let mut cache = self.cache.write();
cache.insert(account.address.clone(), account); cache.insert(account.address.clone(), account);
Ok(()) Ok(())
} }
fn reload_accounts(&self) -> Result<(), Error> { fn reload_accounts(&self) -> Result<(), Error> {
let mut cache = self.cache.write().unwrap(); let mut cache = self.cache.write();
let accounts = try!(self.dir.load()); let accounts = try!(self.dir.load());
let new_accounts: BTreeMap<_, _> = accounts.into_iter().map(|account| (account.address.clone(), account)).collect(); let new_accounts: BTreeMap<_, _> = accounts.into_iter().map(|account| (account.address.clone(), account)).collect();
mem::replace(&mut *cache, new_accounts); mem::replace(&mut *cache, new_accounts);
@ -71,13 +71,13 @@ impl EthStore {
fn get(&self, address: &Address) -> Result<SafeAccount, Error> { fn get(&self, address: &Address) -> Result<SafeAccount, Error> {
{ {
let cache = self.cache.read().unwrap(); let cache = self.cache.read();
if let Some(account) = cache.get(address) { if let Some(account) = cache.get(address) {
return Ok(account.clone()) return Ok(account.clone())
} }
} }
try!(self.reload_accounts()); try!(self.reload_accounts());
let cache = self.cache.read().unwrap(); let cache = self.cache.read();
cache.get(address).cloned().ok_or(Error::InvalidAccount) cache.get(address).cloned().ok_or(Error::InvalidAccount)
} }
} }
@ -111,7 +111,7 @@ impl SecretStore for EthStore {
fn accounts(&self) -> Result<Vec<Address>, Error> { fn accounts(&self) -> Result<Vec<Address>, Error> {
try!(self.reload_accounts()); try!(self.reload_accounts());
Ok(self.cache.read().unwrap().keys().cloned().collect()) Ok(self.cache.read().keys().cloned().collect())
} }
fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> { fn change_password(&self, address: &Address, old_password: &str, new_password: &str) -> Result<(), Error> {
@ -131,7 +131,7 @@ impl SecretStore for EthStore {
if can_remove { if can_remove {
try!(self.dir.remove(address)); try!(self.dir.remove(address));
let mut cache = self.cache.write().unwrap(); let mut cache = self.cache.write();
cache.remove(address); cache.remove(address);
Ok(()) Ok(())
} else { } else {

View File

@ -26,12 +26,15 @@ extern crate serde_json;
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate crypto as rcrypto; extern crate crypto as rcrypto;
extern crate tiny_keccak; extern crate tiny_keccak;
#[macro_use] extern crate parking_lot;
extern crate lazy_static;
// reexport it nicely // reexport it nicely
extern crate ethkey as _ethkey; extern crate ethkey as _ethkey;
extern crate ethcrypto as crypto; extern crate ethcrypto as crypto;
#[macro_use]
extern crate lazy_static;
pub mod dir; pub mod dir;
pub mod ethkey; pub mod ethkey;

View File

@ -33,7 +33,8 @@ impl From<json::PresaleWallet> for PresaleWallet {
impl PresaleWallet { impl PresaleWallet {
pub fn open<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> { pub fn open<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
let file = try!(fs::File::open(path)); let file = try!(fs::File::open(path));
let presale = json::PresaleWallet::load(file).unwrap(); let presale = try!(json::PresaleWallet::load(file)
.map_err(|e| Error::InvalidKeyFile(format!("{}", e))));
Ok(PresaleWallet::from(presale)) Ok(PresaleWallet::from(presale))
} }

2
js/.gitignore vendored
View File

@ -1,5 +1,7 @@
node_modules node_modules
npm-debug.log npm-debug.log
build
.build .build
.coverage .coverage
.dist
.happypack .happypack

19
js/Cargo.precompiled.toml Normal file
View File

@ -0,0 +1,19 @@
[package]
description = "Parity built-in dapps."
name = "parity-ui-precompiled"
version = "1.4.0"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[features]
default = ["with-syntex", "use-precompiled-js"]
use-precompiled-js = ["parity-dapps-glue/use-precompiled-js"]
with-syntex = ["parity-dapps-glue/with-syntex"]
[build-dependencies]
parity-dapps-glue = "1.4"
[dependencies]
parity-dapps-glue = "1.4"

18
js/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
description = "Parity built-in dapps."
name = "parity-ui-dev"
version = "1.4.0"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[features]
default = ["with-syntex"]
with-syntex = ["parity-dapps-glue/with-syntex"]
[build-dependencies]
parity-dapps-glue = "1.4"
[dependencies]
parity-dapps-glue = "1.4"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

@ -1 +0,0 @@
Subproject commit f94a8eddb8789410dda0db03d4f1d6ae42b31208

View File

@ -24,17 +24,38 @@ var express = require('express');
var proxy = require('http-proxy-middleware'); var proxy = require('http-proxy-middleware');
var app = express(); var app = express();
var wsProxy = proxy('ws://127.0.0.1:8180', { changeOrigin: true });
app.use(express.static('build')); app.use(express.static('.build'));
app.use('/api/*', proxy({ app.use('/api/*', proxy({
target: 'http://127.0.0.1:8080', target: 'http://127.0.0.1:8080',
changeOrigin: true changeOrigin: true
})); }));
app.use('/app/*', proxy({
target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: {
'^/app': ''
}
}));
app.use('/parity-utils/*', proxy({
target: 'http://127.0.0.1:3000',
changeOrigin: true,
pathRewrite: {
'^/parity-utils': ''
}
}));
app.use('/rpc/*', proxy({ app.use('/rpc/*', proxy({
target: 'http://127.0.0.1:8080', target: 'http://127.0.0.1:8080',
changeOrigin: true changeOrigin: true
})); }));
app.listen(3000); app.use(wsProxy);
var server = app.listen(3000);
server.on('upgrade', wsProxy.upgrade);

View File

@ -17,6 +17,6 @@
extern crate parity_dapps_glue; extern crate parity_dapps_glue;
fn main() { fn main() {
parity_dapps_glue::js::build(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js")); parity_dapps_glue::js::build(env!("CARGO_MANIFEST_DIR"), "build");
parity_dapps_glue::generate(); parity_dapps_glue::generate();
} }

View File

@ -23,17 +23,22 @@
"Promise" "Promise"
], ],
"scripts": { "scripts": {
"build": "npm run build:dll && npm run build:app", "build": "npm run build:dll && npm run build:app && npm run build:lib",
"build:app": "webpack --progress", "build:app": "webpack --progress",
"build:dll": "webpack --config webpack.vendor.js --progress", "build:lib": "webpack --config webpack.libraries --progress",
"ci:build": "npm run ci:build:dll && npm run ci:build:app", "build:dll": "webpack --config webpack.vendor --progress",
"ci:build": "npm run ci:build:dll && npm run ci:build:app && npm run ci:build:lib",
"ci:build:app": "NODE_ENV=production webpack", "ci:build:app": "NODE_ENV=production webpack",
"ci:build:dll": "NODE_ENV=production webpack --config webpack.vendor.js", "ci:build:lib": "NODE_ENV=production webpack --config webpack.libraries",
"ci:build:dll": "NODE_ENV=production webpack --config webpack.vendor",
"start": "npm install && npm run build:dll && npm run start:app",
"start:app": "webpack-dev-server -d --history-api-fallback --open --hot --inline --progress --colors --port 3000",
"clean": "rm -rf ./build ./coverage", "clean": "rm -rf ./build ./coverage",
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
"lint": "eslint --ignore-path .gitignore ./src/", "lint": "eslint --ignore-path .gitignore ./src/",
"start": "npm install && npm run build:dll && npm run start:app",
"start:app": "webpack-dev-server -d --history-api-fallback --open --hot --inline --progress --colors --port 3000",
"test": "mocha 'src/**/*.spec.js'", "test": "mocha 'src/**/*.spec.js'",
"test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", "test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'",
"test:e2e": "mocha 'src/**/*.e2e.js'" "test:e2e": "mocha 'src/**/*.e2e.js'"
@ -109,6 +114,7 @@
"blockies": "0.0.2", "blockies": "0.0.2",
"bytes": "^2.4.0", "bytes": "^2.4.0",
"es6-promise": "^3.2.1", "es6-promise": "^3.2.1",
"file-saver": "^1.3.3",
"format-json": "^1.0.3", "format-json": "^1.0.3",
"format-number": "^2.0.1", "format-number": "^2.0.1",
"geopattern": "^1.2.3", "geopattern": "^1.2.3",

View File

@ -6,7 +6,15 @@ cd ..
# run build (production) and store the exit code # run build (production) and store the exit code
EXITCODE=0 EXITCODE=0
npm run ci:build || EXITCODE=1 BUILDDIR=./.dist
rm -rf $BUILDDIR
mkdir -p $BUILDDIR/src
BUILD_DEST=$BUILDDIR/build npm run ci:build || EXITCODE=1
# Copy rust files
cp Cargo.precompiled.toml $BUILDDIR/Cargo.toml
cp build.rs $BUILDDIR
cp src/lib.rs* $BUILDDIR/src
# back to root # back to root
popd popd

Some files were not shown because too many files have changed in this diff Show More