Merge remote-tracking branch 'parity/master' into bft
This commit is contained in:
commit
ea960f1c27
204
.gitlab-ci.yml
204
.gitlab-ci.yml
@ -1,11 +1,11 @@
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
RUST_BACKTRACE: "1"
|
||||
RUSTFLAGS: "-D warnings"
|
||||
RUSTFLAGS: ""
|
||||
CARGOFLAGS: ""
|
||||
cache:
|
||||
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
||||
untracked: true
|
||||
@ -18,13 +18,20 @@ linux-stable:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- cargo build --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- 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_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/checksum --body checksum
|
||||
- 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:
|
||||
- rust
|
||||
- rust-stable
|
||||
@ -41,13 +48,20 @@ linux-stable-14.04:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- cargo build --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- 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_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/checksum --body checksum
|
||||
- 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:
|
||||
- rust
|
||||
- rust-14.04
|
||||
@ -64,7 +78,7 @@ linux-beta:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- cargo build --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
tags:
|
||||
- rust
|
||||
@ -83,7 +97,7 @@ linux-nightly:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- cargo build --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
tags:
|
||||
- rust
|
||||
@ -104,13 +118,13 @@ linux-centos:
|
||||
script:
|
||||
- export CXX="g++"
|
||||
- export CC="gcc"
|
||||
- cargo build --release --verbose
|
||||
- cargo build --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- rust
|
||||
- rust-centos
|
||||
@ -127,18 +141,29 @@ linux-armv7:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- export CXX=arm-linux-gnueabihf-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
||||
- cargo build --target armv7-unknown-linux-gnueabihf --release $CARGOFLAGS
|
||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> checksum
|
||||
- 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_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/checksum --body checksum
|
||||
- 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:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -156,18 +181,29 @@ linux-arm:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- export CXX=arm-linux-gnueabihf-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
||||
- cargo build --target arm-unknown-linux-gnueabihf --release $CARGOFLAGS
|
||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> checksum
|
||||
- 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_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/checksum --body checksum
|
||||
- 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:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -180,23 +216,26 @@ linux-armv6:
|
||||
stage: build
|
||||
image: ethcore/rust-armv6:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- export CC=arm-linux-gnueabi-gcc
|
||||
- export CXX=arm-linux-gnueabi-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
||||
- cargo build --target arm-unknown-linux-gnueabi --release $CARGOFLAGS
|
||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> checksum
|
||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity --body target/arm-unknown-linux-gnueabi/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -214,18 +253,29 @@ linux-aarch64:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- export CC=aarch64-linux-gnu-gcc
|
||||
- export CXX=aarch64-linux-gnu-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
||||
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
||||
- cargo build --target aarch64-unknown-linux-gnu --release $CARGOFLAGS
|
||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> checksum
|
||||
- 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_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/checksum --body checksum
|
||||
- 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:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -242,12 +292,13 @@ darwin:
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- md5sum target/release/parity >> checksum
|
||||
- cargo build --release $CARGOFLAGS
|
||||
- rm -rf parity.md5
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- osx
|
||||
artifacts:
|
||||
@ -267,28 +318,105 @@ windows:
|
||||
- set RUST_BACKTRACE=1
|
||||
- set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo build --release --verbose
|
||||
- cmd md5sum target\release\parity >> checksum
|
||||
- cargo build --release %CARGOFLAGS%
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||
- cd nsis
|
||||
- makensis.exe installer.nsi
|
||||
- copy installer.exe InstallParity.exe
|
||||
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
|
||||
- md5sums InstallParity.exe > InstallParity.exe.md5
|
||||
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||
- md5sums win-installer.zip > win-installer.zip.md5
|
||||
- cd ..\target\release\
|
||||
- md5sums parity.exe parity.pdb > parity.md5
|
||||
- md5sums parity.exe > parity.exe.md5
|
||||
- zip parity.zip parity.exe parity.pdb parity.md5
|
||||
- md5sums parity.zip > parity.zip.md5
|
||||
- cd ..\..
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
- aws configure set aws_secret_access_key %s3_secret%
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.pdb
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
||||
tags:
|
||||
- rust-windows
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity.exe
|
||||
- target/release/parity.pdb
|
||||
- nsis/InstallParity.exe
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
#test-darwin:
|
||||
# stage: build
|
||||
# before_script:
|
||||
# - git submodule update --init --recursive
|
||||
# script:
|
||||
# - export RUST_BACKTRACE=1
|
||||
# - ./test.sh $CARGOFLAGS --no-release
|
||||
# tags:
|
||||
# - osx
|
||||
#test-windows:
|
||||
# stage: build
|
||||
# before_script:
|
||||
# - git submodule update --init --recursive
|
||||
# script:
|
||||
# - set RUST_BACKTRACE=1
|
||||
# - cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
|
||||
# tags:
|
||||
# - rust-windows
|
||||
# allow_failure: true
|
||||
test-linux:
|
||||
stage: test
|
||||
stage: build
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
script:
|
||||
- export RUST_BACKTRACE=1
|
||||
- ./test.sh --verbose
|
||||
- ./test.sh $CARGOFLAGS --no-release
|
||||
tags:
|
||||
- rust-test
|
||||
dependencies:
|
||||
- linux-stable
|
||||
js-release:
|
||||
stage: build
|
||||
image: ethcore/javascript:latest
|
||||
only:
|
||||
- master
|
||||
before_script:
|
||||
- ./js/scripts/install-deps.sh
|
||||
script:
|
||||
- ./js/scripts/build.sh
|
||||
- ./js/scripts/release.sh
|
||||
tags:
|
||||
- javascript
|
||||
js-lint:
|
||||
stage: build
|
||||
image: ethcore/javascript:latest
|
||||
before_script:
|
||||
- ./js/scripts/install-deps.sh
|
||||
script:
|
||||
- ./js/scripts/lint.sh
|
||||
tags:
|
||||
- javascript-test
|
||||
js-test:
|
||||
stage: build
|
||||
image: ethcore/javascript:latest
|
||||
before_script:
|
||||
- ./js/scripts/install-deps.sh
|
||||
script:
|
||||
- ./js/scripts/test.sh
|
||||
tags:
|
||||
- javascript-test
|
||||
js-pack:
|
||||
stage: build
|
||||
image: ethcore/javascript:latest
|
||||
before_script:
|
||||
- ./js/scripts/install-deps.sh
|
||||
script:
|
||||
- ./js/scripts/build.sh
|
||||
tags:
|
||||
- javascript-test
|
||||
|
@ -32,6 +32,7 @@ env:
|
||||
- RUN_DOCS="false"
|
||||
- TEST_OPTIONS=""
|
||||
- RUSTFLAGS="-D warnings"
|
||||
- TRAVIS_NODE_VERSION="6"
|
||||
# GH_TOKEN for documentation
|
||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov"
|
||||
@ -41,6 +42,7 @@ cache:
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/target
|
||||
- $TRAVIS_BUILD_DIR/kcov-master
|
||||
- $TRAVIS_BUILD_DIR/js/node_modules
|
||||
- $HOME/.cargo
|
||||
|
||||
addons:
|
||||
@ -64,9 +66,14 @@ install:
|
||||
make && make install DESTDIR=../tmp &&
|
||||
cd
|
||||
)
|
||||
- nvm install $TRAVIS_NODE_VERSION && nvm use $TRAVIS_NODE_VERSION && ./js/scripts/install-deps.sh
|
||||
|
||||
script:
|
||||
- if [ "$RUN_TESTS" = "true" ]; then ./test.sh $TEST_OPTIONS --verbose; fi
|
||||
- if [ "$RUN_TESTS" = "true" ]; then
|
||||
./js/scripts/lint.sh &&
|
||||
./js/scripts/test.sh &&
|
||||
./test.sh $TEST_OPTIONS --verbose;
|
||||
fi
|
||||
- if [ "$RUN_COVERAGE" = "true" ]; then ./scripts/cov.sh "$KCOV_CMD"; fi
|
||||
|
||||
after_success: |
|
||||
|
219
Cargo.lock
generated
219
Cargo.lock
generated
@ -3,7 +3,7 @@ name = "parity"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (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.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
|
||||
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -145,15 +145,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.0.90"
|
||||
version = "0.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.0.90"
|
||||
version = "0.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -222,8 +222,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "elastic-array"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ethcore/elastic-array#70e4012e691b732c7c4cb04e9232799e6aa268bc"
|
||||
dependencies = [
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
@ -264,7 +267,7 @@ name = "ethash"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha3 0.1.0",
|
||||
]
|
||||
@ -276,7 +279,7 @@ dependencies = [
|
||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 0.5.3 (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.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.4.0",
|
||||
@ -295,7 +298,7 @@ dependencies = [
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -304,6 +307,7 @@ dependencies = [
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -319,12 +323,16 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ethcore-bloom-journal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-dapps"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.96 (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)",
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-rpc 1.4.0",
|
||||
@ -332,14 +340,13 @@ dependencies = [
|
||||
"fetch 0.1.0",
|
||||
"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-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
"parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
"parity-dapps-wallet 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-ui 1.4.0",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -363,8 +370,8 @@ version = "1.4.0"
|
||||
dependencies = [
|
||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.0 (git+https://github.com/carllerche/mio)",
|
||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -444,6 +451,7 @@ name = "ethcore-network"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-io 1.4.0",
|
||||
"ethcore-util 1.4.0",
|
||||
@ -452,8 +460,8 @@ dependencies = [
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.0 (git+https://github.com/carllerche/mio)",
|
||||
"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)",
|
||||
"rlp 0.1.0",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -467,7 +475,7 @@ dependencies = [
|
||||
name = "ethcore-rpc"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.4.0",
|
||||
"ethcore 1.4.0",
|
||||
"ethcore-devtools 1.4.0",
|
||||
@ -482,7 +490,7 @@ dependencies = [
|
||||
"fetch 0.1.0",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -497,7 +505,7 @@ dependencies = [
|
||||
name = "ethcore-signer"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-io 1.4.0",
|
||||
@ -505,10 +513,11 @@ dependencies = [
|
||||
"ethcore-util 1.4.0",
|
||||
"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)",
|
||||
"parity-dapps-signer 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-ui 1.4.0",
|
||||
"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)",
|
||||
"ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -535,8 +544,8 @@ version = "1.4.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"clippy 0.0.96 (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)",
|
||||
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
|
||||
"ethcore-bigint 0.1.1",
|
||||
@ -547,8 +556,10 @@ dependencies = [
|
||||
"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)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.0 (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)",
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -608,6 +619,7 @@ dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
@ -622,7 +634,7 @@ dependencies = [
|
||||
name = "ethsync"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.4.0",
|
||||
"ethcore-io 1.4.0",
|
||||
@ -633,7 +645,7 @@ dependencies = [
|
||||
"ethcore-util 1.4.0",
|
||||
"heapsize 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)",
|
||||
"parking_lot 0.2.6 (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)",
|
||||
"rlp 0.1.0",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -807,7 +819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "json-ipc-server"
|
||||
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 = [
|
||||
"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)",
|
||||
@ -839,7 +851,7 @@ version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -847,8 +859,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "6.1.0"
|
||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#2766c6708f66f6f4e667211461d220b49c0d9fdf"
|
||||
version = "6.1.1"
|
||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5"
|
||||
dependencies = [
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -875,6 +887,11 @@ name = "lazy_static"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.15"
|
||||
@ -882,7 +899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.0.9"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -897,10 +914,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
version = "0.0.7"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -979,7 +996,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.0-dev"
|
||||
source = "git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d#62ec763c9cc34d8a452ed0392c575c50ddd5fc8d"
|
||||
source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -991,6 +1008,22 @@ dependencies = [
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/carllerche/mio#9f17b70d6fecbf912168267ea74cf536f2cba705"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.1.3"
|
||||
@ -1165,9 +1198,14 @@ version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps"
|
||||
name = "owning_ref"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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)",
|
||||
@ -1179,40 +1217,32 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-home"
|
||||
name = "parity-ui"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
||||
dependencies = [
|
||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
"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-dapps-signer"
|
||||
name = "parity-ui-dev"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
||||
dependencies = [
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-status"
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#10a57a7df153360b4abeddff99e5b8f34a85ff53"
|
||||
dependencies = [
|
||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-wallet"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
||||
dependencies = [
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1221,6 +1251,27 @@ dependencies = [
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.7.14"
|
||||
@ -1397,7 +1448,7 @@ dependencies = [
|
||||
name = "rlp"
|
||||
version = "0.1.0"
|
||||
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",
|
||||
"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)",
|
||||
@ -1406,7 +1457,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
@ -1415,7 +1466,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocksdb-sys"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1544,6 +1595,11 @@ dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.1.3"
|
||||
@ -1559,6 +1615,11 @@ name = "slab"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.1.8"
|
||||
@ -1850,13 +1911,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ws"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#afbff59776ce16ccec5ee9e218b8891830ee6fdf"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#0cd6c5e3e9d5e61a37d53eb8dcbad523dcc69314"
|
||||
dependencies = [
|
||||
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)",
|
||||
"mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
|
||||
@ -1916,8 +1977,8 @@ dependencies = [
|
||||
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
||||
"checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "<none>"
|
||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||
"checksum clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "d19bda68c3db98e3a780342f6101b44312fef20a5f13ce756d1202a35922b01b"
|
||||
"checksum clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4ed67c69b9bb35169be2538691d290a3aa0cbfd4b9f0bfb7c221fc1d399a96"
|
||||
"checksum clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "6eacf01b0aad84a0817703498f72d252df7c0faf6a5b86d0be4265f1829e459f"
|
||||
"checksum clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "a49960c9aab544ce86b004dcb61620e8b898fea5fc0f697a028f460f48221ed6"
|
||||
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
||||
"checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc"
|
||||
"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "<none>"
|
||||
@ -1925,7 +1986,7 @@ dependencies = [
|
||||
"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 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 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"
|
||||
@ -1946,15 +2007,16 @@ dependencies = [
|
||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
||||
"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "<none>"
|
||||
"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414"
|
||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||
"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
||||
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
||||
"checksum linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "83f7ff3baae999fdf921cccf54b61842bb3b26868d50d02dff48052ebec8dd79"
|
||||
"checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48"
|
||||
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||
"checksum lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "42d50dcb5d9f145df83b1043207e1ac0c37c9c779c4e128ca4655abc3f3cbf8c"
|
||||
"checksum lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656fa4dfcb02bcf1063c592ba3ff6a5303ee1f2afe98c8a889e8b1a77c6dfdb7"
|
||||
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
||||
@ -1962,7 +2024,8 @@ dependencies = [
|
||||
"checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54"
|
||||
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
|
||||
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
||||
"checksum mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)" = "<none>"
|
||||
"checksum mio 0.6.0 (git+https://github.com/carllerche/mio)" = "<none>"
|
||||
"checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "<none>"
|
||||
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
||||
"checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8"
|
||||
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||
@ -1982,12 +2045,12 @@ dependencies = [
|
||||
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
||||
"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 parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0fd1be2c3cf5fef20a6d18fec252c4f3c87c14fc3039002eb7d4ed91e436826"
|
||||
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
|
||||
"checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab"
|
||||
"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.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 phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
||||
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
||||
"checksum phf_generator 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "db005608fd99800c8c74106a7c894cf582055b689aa14a79462cefdcb7dc1cc3"
|
||||
@ -2024,9 +2087,11 @@ dependencies = [
|
||||
"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21"
|
||||
"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc"
|
||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"
|
||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
||||
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||
@ -2064,7 +2129,7 @@ dependencies = [
|
||||
"checksum webpki 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "813503a5985585e0812d430cd1328ee322f47f66629c8ed4ecab939cf9e92f91"
|
||||
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||
"checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"
|
||||
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
|
||||
|
32
Cargo.toml
32
Cargo.toml
@ -26,7 +26,11 @@ lazy_static = "0.2"
|
||||
regex = "0.1"
|
||||
isatty = "0.1"
|
||||
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" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
fdlimit = { path = "util/fdlimit" }
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
@ -40,12 +44,9 @@ ethcore-ipc = { path = "ipc/rpc" }
|
||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
rlp = { path = "util/rlp" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.90", optional = true}
|
||||
ethcore-stratum = { path = "stratum" }
|
||||
serde = "0.8.0"
|
||||
serde_json = "0.8.0"
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.96", optional = true}
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.2"
|
||||
@ -53,19 +54,26 @@ winapi = "0.2"
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
daemonize = "0.2"
|
||||
|
||||
[dependencies.hyper]
|
||||
version = "0.9"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["ui", "use-precompiled-js", "ipc"]
|
||||
ui = ["dapps", "ethcore-signer/ui"]
|
||||
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
||||
default = ["ui-precompiled"]
|
||||
|
||||
ui = [
|
||||
"dapps",
|
||||
"ethcore-dapps/ui",
|
||||
"ethcore-signer/ui",
|
||||
]
|
||||
ui-precompiled = [
|
||||
"dapps",
|
||||
"ethcore-signer/ui-precompiled",
|
||||
"ethcore-dapps/ui-precompiled",
|
||||
]
|
||||
|
||||
dapps = ["ethcore-dapps"]
|
||||
ipc = ["ethcore/ipc", "ethsync/ipc"]
|
||||
jit = ["ethcore/jit"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
test-heavy = ["ethcore/test-heavy"]
|
||||
stratum = ["ipc"]
|
||||
ethkey-cli = ["ethcore/ethkey-cli"]
|
||||
ethstore-cli = ["ethcore/ethstore-cli"]
|
||||
|
@ -1,6 +1,8 @@
|
||||
# [Parity](https://ethcore.io/parity.html)
|
||||
### Fast, light, and robust Ethereum implementation
|
||||
|
||||
[![Join the chat at https://gitter.im/ethcore/parity.js](https://badges.gitter.im/ethcore/parity.js.svg)](https://gitter.im/ethcore/parity.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
|
||||
|
||||
[Internal Documentation][doc-url]
|
||||
|
@ -11,6 +11,7 @@ build = "build.rs"
|
||||
[dependencies]
|
||||
rand = "0.3.14"
|
||||
log = "0.3"
|
||||
env_logger = "0.3"
|
||||
jsonrpc-core = "3.0"
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
@ -19,33 +20,28 @@ url = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
serde = "0.8"
|
||||
serde_json = "0.8"
|
||||
serde_macros = { version = "0.8", optional = true }
|
||||
zip = { version = "0.1", default-features = false }
|
||||
ethabi = "0.2.2"
|
||||
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-rpc = { path = "../rpc" }
|
||||
ethcore-util = { path = "../util" }
|
||||
fetch = { path = "../util/fetch" }
|
||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||
# List of apps
|
||||
parity-dapps-status = { 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" }
|
||||
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true }
|
||||
parity-ui = { path = "./ui" }
|
||||
|
||||
mime_guess = { version = "1.6.1" }
|
||||
clippy = { version = "0.0.90", optional = true}
|
||||
clippy = { version = "0.0.96", optional = true}
|
||||
|
||||
[build-dependencies]
|
||||
serde_codegen = { version = "0.8", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["serde_codegen", "extra-dapps"]
|
||||
extra-dapps = ["parity-dapps-wallet"]
|
||||
default = ["serde_codegen"]
|
||||
nightly = ["serde_macros"]
|
||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||
|
||||
use-precompiled-js = [
|
||||
"parity-dapps-status/use-precompiled-js",
|
||||
"parity-dapps-home/use-precompiled-js",
|
||||
"parity-dapps-wallet/use-precompiled-js"
|
||||
]
|
||||
ui = ["parity-ui/no-precompiled-js"]
|
||||
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
||||
|
31
dapps/js-glue/Cargo.toml
Normal file
31
dapps/js-glue/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
description = "Base Package for all Parity built-in dapps"
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.4.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
quasi_codegen = { version = "0.11", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
|
||||
[dependencies]
|
||||
glob = { version = "0.2.11" }
|
||||
mime_guess = { version = "1.6.1" }
|
||||
aster = { version = "0.17", default-features = false }
|
||||
quasi = { version = "0.11", default-features = false }
|
||||
quasi_macros = { version = "0.11", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
syntex_syntax = { version = "0.33", optional = true }
|
||||
clippy = { version = "0.0.90", optional = true }
|
||||
|
||||
[features]
|
||||
dev = ["clippy"]
|
||||
default = ["with-syntex"]
|
||||
nightly = ["quasi_macros"]
|
||||
nightly-testing = ["clippy"]
|
||||
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||
use-precompiled-js = []
|
||||
|
||||
|
65
dapps/js-glue/README.md
Normal file
65
dapps/js-glue/README.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Parity Dapps (JS-glue)
|
||||
|
||||
Code generator to simplify creating a built-in Parity Dapp
|
||||
|
||||
# How to create new builtin Dapp.
|
||||
1. Clone this repository.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/ethcore/parity.git
|
||||
```
|
||||
|
||||
1. Create a new directory for your Dapp. (`./myapp`)
|
||||
|
||||
```bash
|
||||
$ mkdir -p ./parity/dapps/myapp/src/web
|
||||
```
|
||||
|
||||
1. Copy your frontend files to `./dapps/myapp/src/web` (bundled ones)
|
||||
|
||||
```bash
|
||||
$ cp -r ./myapp-src/* ./parity/dapps/myapp/src/web
|
||||
```
|
||||
|
||||
1. Instead of creating `web3` in your app. Load (as the first script tag in `head`):
|
||||
|
||||
```html
|
||||
<script src="/parity-utils/inject.js"></script>
|
||||
```
|
||||
|
||||
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
||||
|
||||
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/ethcore/parity-ui/blob/master/status/Cargo.toml).
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/ethcore/parity-ui.git
|
||||
$ cd ./parity-ui/
|
||||
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||
$ cp ./home/src/lib.rs ../parity/dapps/myapp/src/lib.rs
|
||||
$ cp ./home/src/lib.rs.in ../parity/dapps/myapp/src/lib.rs.in
|
||||
# And edit the details of your app
|
||||
$ vim ../parity/dapps/myapp/Cargo.toml # Edit the details
|
||||
$ vim ./parity/dapps/myapp/src/lib.rs.in # Edit the details
|
||||
```
|
||||
# How to include your Dapp into `Parity`?
|
||||
1. Edit `dapps/Cargo.toml` and add dependency to your application (it can be optional)
|
||||
|
||||
```toml
|
||||
# Use git repo and version
|
||||
parity-dapps-myapp = { path="./myapp" }
|
||||
```
|
||||
|
||||
1. Edit `dapps/src/apps.rs` and add your application to `all_pages` (if it's optional you need to specify two functions - see `parity-dapps-wallet` example)
|
||||
|
||||
1. Compile parity.
|
||||
|
||||
```bash
|
||||
$ cargo build --release # While inside `parity`
|
||||
```
|
||||
|
||||
1. Commit the results.
|
||||
|
||||
```bash
|
||||
$ git add myapp && git commit -am "My first Parity Dapp".
|
||||
```
|
45
dapps/js-glue/build.rs
Normal file
45
dapps/js-glue/build.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
mod inner {
|
||||
extern crate syntex;
|
||||
extern crate quasi_codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let mut registry = syntex::Registry::new();
|
||||
quasi_codegen::register(&mut registry);
|
||||
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
mod inner {
|
||||
pub fn main() {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
inner::main();
|
||||
}
|
66
dapps/js-glue/src/build.rs
Normal file
66
dapps/js-glue/src/build.rs
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub mod inner {
|
||||
use syntex;
|
||||
use codegen;
|
||||
use syntax::{ast, fold};
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
||||
/// Helper folder that strips the serde attributes after the extensions have been expanded.
|
||||
struct StripAttributeFolder;
|
||||
|
||||
impl fold::Folder for StripAttributeFolder {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref n, _) if n == &"webapp" => { return None; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(attr)
|
||||
}
|
||||
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
fold::noop_fold_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
||||
}
|
||||
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
reg.add_attr("feature(custom_derive)");
|
||||
reg.add_attr("feature(custom_attribute)");
|
||||
|
||||
reg.add_decorator("derive_WebAppFiles", codegen::expand_webapp_implementation);
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
pub fn generate() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
pub mod inner {
|
||||
use codegen;
|
||||
|
||||
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_syntax_extension(
|
||||
syntax::parse::token::intern("derive_WebAppFiles"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(codegen::expand_webapp_implementation)));
|
||||
|
||||
reg.register_attribute("webapp".to_owned(), AttributeType::Normal);
|
||||
}
|
||||
|
||||
pub fn generate() {}
|
||||
}
|
189
dapps/js-glue/src/codegen.rs
Normal file
189
dapps/js-glue/src/codegen.rs
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate aster;
|
||||
extern crate glob;
|
||||
extern crate mime_guess;
|
||||
|
||||
use self::mime_guess::guess_mime_type;
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use std::ops::Deref;
|
||||
|
||||
use syntax::ast::{MetaItem, Item};
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::print::pprust::{lit_to_string};
|
||||
use syntax::parse::token::{InternedString};
|
||||
|
||||
|
||||
pub fn expand_webapp_implementation(
|
||||
cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
meta_item: &MetaItem,
|
||||
annotatable: &Annotatable,
|
||||
push: &mut FnMut(Annotatable)
|
||||
) {
|
||||
let item = match *annotatable {
|
||||
Annotatable::Item(ref item) => item,
|
||||
_ => {
|
||||
cx.span_err(meta_item.span, "`#[derive(WebAppFiles)]` may only be applied to struct implementations");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let builder = aster::AstBuilder::new().span(span);
|
||||
implement_webapp(cx, &builder, &item, push);
|
||||
}
|
||||
|
||||
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
||||
let static_files_dir = extract_path(cx, item);
|
||||
|
||||
let src = Path::new("src");
|
||||
let static_files = {
|
||||
let mut buf = src.to_path_buf();
|
||||
buf.push(static_files_dir.deref());
|
||||
buf
|
||||
};
|
||||
|
||||
let search_location = {
|
||||
let mut buf = static_files.to_path_buf();
|
||||
buf.push("**");
|
||||
buf.push("*");
|
||||
buf
|
||||
};
|
||||
|
||||
let files = glob::glob(search_location.to_str().expect("Valid UTF8 path"))
|
||||
.expect("The sources directory is missing.")
|
||||
.collect::<Result<Vec<PathBuf>, glob::GlobError>>()
|
||||
.expect("There should be no error when reading a list of files.");
|
||||
|
||||
let statements = files
|
||||
.iter()
|
||||
.filter(|path_buf| path_buf.is_file())
|
||||
.map(|path_buf| {
|
||||
let path = path_buf.as_path();
|
||||
let filename = path.file_name().and_then(|s| s.to_str()).expect("Only UTF8 paths.");
|
||||
let mime_type = guess_mime_type(filename).to_string();
|
||||
let file_path = as_uri(path.strip_prefix(&static_files).ok().expect("Prefix is always there, cause it's absolute path;qed"));
|
||||
let file_path_in_source = path.to_str().expect("Only UTF8 paths.");
|
||||
|
||||
let path_lit = builder.expr().str(file_path.as_str());
|
||||
let mime_lit = builder.expr().str(mime_type.as_str());
|
||||
let web_path_lit = builder.expr().str(file_path_in_source);
|
||||
let separator_lit = builder.expr().str(path::MAIN_SEPARATOR.to_string().as_str());
|
||||
let concat_id = builder.id("concat!");
|
||||
let env_id = builder.id("env!");
|
||||
let macro_id = builder.id("include_bytes!");
|
||||
|
||||
let content = quote_expr!(
|
||||
cx,
|
||||
$macro_id($concat_id($env_id("CARGO_MANIFEST_DIR"), $separator_lit, $web_path_lit))
|
||||
);
|
||||
quote_stmt!(
|
||||
cx,
|
||||
files.insert($path_lit, File { path: $path_lit, content_type: $mime_lit, content: $content });
|
||||
).expect("The statement is always ok, because it just uses literals.")
|
||||
}).collect::<Vec<ast::Stmt>>();
|
||||
|
||||
let type_name = item.ident;
|
||||
|
||||
let files_impl = quote_item!(cx,
|
||||
impl $type_name {
|
||||
fn files() -> ::std::collections::HashMap<&'static str, File> {
|
||||
let mut files = ::std::collections::HashMap::new();
|
||||
$statements
|
||||
files
|
||||
}
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
push(Annotatable::Item(files_impl));
|
||||
}
|
||||
|
||||
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||
for meta_items in item.attrs().iter().filter_map(webapp_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
match meta_item.node {
|
||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"path" => {
|
||||
if let Some(s) = get_str_from_lit(cx, name, lit) {
|
||||
return s.deref().to_owned();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
"web".to_owned()
|
||||
}
|
||||
|
||||
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<InternedString> {
|
||||
match lit.node {
|
||||
ast::LitKind::Str(ref s, _) => Some(s.clone()),
|
||||
_ => {
|
||||
cx.span_err(
|
||||
lit.span,
|
||||
&format!("webapp annotation `{}` must be a string, not `{}`",
|
||||
name,
|
||||
lit_to_string(lit)
|
||||
)
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn webapp_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref name, ref items) if name == &"webapp" => {
|
||||
attr::mark_used(&attr);
|
||||
Some(items)
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_uri(path: &Path) -> String {
|
||||
let mut s = String::new();
|
||||
for component in path.iter() {
|
||||
s.push_str(component.to_str().expect("Only UTF-8 filenames are supported."));
|
||||
s.push('/');
|
||||
}
|
||||
s[0..s.len()-1].into()
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_convert_path_separators_on_all_platforms() {
|
||||
// given
|
||||
let p = {
|
||||
let mut p = PathBuf::new();
|
||||
p.push("web");
|
||||
p.push("src");
|
||||
p.push("index.html");
|
||||
p
|
||||
};
|
||||
|
||||
// when
|
||||
let path = as_uri(&p);
|
||||
|
||||
// then
|
||||
assert_eq!(path, "web/src/index.html".to_owned());
|
||||
}
|
89
dapps/js-glue/src/js.rs
Normal file
89
dapps/js-glue/src/js.rs
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg_attr(feature="use-precompiled-js", allow(dead_code))]
|
||||
#![cfg_attr(feature="use-precompiled-js", allow(unused_imports))]
|
||||
|
||||
use std::fmt;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod platform {
|
||||
use std::process::Command;
|
||||
|
||||
pub static NPM_CMD: &'static str = "npm";
|
||||
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod platform {
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub static NPM_CMD: &'static str = "npm.cmd";
|
||||
// NOTE [ToDr] For some reason on windows
|
||||
// We cannot have any file descriptors open when running a child process
|
||||
// during build phase.
|
||||
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||
cmd.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
}
|
||||
}
|
||||
|
||||
fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
|
||||
panic!("Error: {}: {:?}", s, e);
|
||||
}
|
||||
|
||||
#[cfg(feature = "use-precompiled-js")]
|
||||
pub fn test(_path: &str) {
|
||||
}
|
||||
#[cfg(feature = "use-precompiled-js")]
|
||||
pub fn build(_path: &str, _dest: &str) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "use-precompiled-js"))]
|
||||
pub fn build(path: &str, dest: &str) {
|
||||
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||
.arg("install")
|
||||
.arg("--no-progress")
|
||||
.current_dir(path)
|
||||
.status()
|
||||
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
|
||||
assert!(child.success(), "There was an error installing dependencies.");
|
||||
|
||||
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||
.arg("run")
|
||||
.arg("build")
|
||||
.env("NODE_ENV", "production")
|
||||
.env("BUILD_DEST", dest)
|
||||
.current_dir(path)
|
||||
.status()
|
||||
.unwrap_or_else(|e| die("Building JS code", e));
|
||||
assert!(child.success(), "There was an error build JS code.");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "use-precompiled-js"))]
|
||||
pub fn test(path: &str) {
|
||||
let child = Command::new(platform::NPM_CMD)
|
||||
.arg("run")
|
||||
.arg("test")
|
||||
.current_dir(path)
|
||||
.status()
|
||||
.unwrap_or_else(|e| die("Running test command", e));
|
||||
assert!(child.success(), "There was an error while running JS tests.");
|
||||
}
|
39
dapps/js-glue/src/lib.rs
Normal file
39
dapps/js-glue/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
||||
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
extern crate syntex;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
#[macro_use]
|
||||
extern crate syntex_syntax as syntax;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
extern crate rustc_plugin;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
include!("lib.rs.in");
|
46
dapps/js-glue/src/lib.rs.in
Normal file
46
dapps/js-glue/src/lib.rs.in
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate quasi;
|
||||
|
||||
mod codegen;
|
||||
mod build;
|
||||
pub mod js;
|
||||
pub use build::inner::generate;
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct File {
|
||||
pub path: &'static str,
|
||||
pub content: &'static [u8],
|
||||
// TODO: use strongly-typed MIME.
|
||||
pub content_type: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Info {
|
||||
pub name: &'static str,
|
||||
pub version: &'static str,
|
||||
pub author: &'static str,
|
||||
pub description: &'static str,
|
||||
pub icon_url: &'static str,
|
||||
}
|
||||
|
||||
pub trait WebApp : Default + Send + Sync {
|
||||
fn file(&self, path: &str) -> Option<&File>;
|
||||
fn info(&self) -> Info;
|
||||
}
|
@ -15,24 +15,31 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use unicase::UniCase;
|
||||
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::response::{as_json, as_json_error, ping_response};
|
||||
use api::response;
|
||||
use apps::fetcher::ContentFetcher;
|
||||
|
||||
use handlers::extract_url;
|
||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||
use apps::fetcher::ContentFetcher;
|
||||
use jsonrpc_http_server::cors;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RestApi {
|
||||
local_domain: String,
|
||||
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
|
||||
endpoints: Arc<Endpoints>,
|
||||
fetcher: Arc<ContentFetcher>,
|
||||
}
|
||||
|
||||
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 {
|
||||
local_domain: local_domain,
|
||||
cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
||||
endpoints: endpoints,
|
||||
fetcher: fetcher,
|
||||
})
|
||||
@ -53,6 +60,7 @@ impl Endpoint for RestApi {
|
||||
|
||||
struct RestApiRouter {
|
||||
api: RestApi,
|
||||
origin: Option<String>,
|
||||
path: Option<EndpointPath>,
|
||||
control: Option<Control>,
|
||||
handler: Box<Handler>,
|
||||
@ -62,9 +70,10 @@ impl RestApiRouter {
|
||||
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
||||
RestApiRouter {
|
||||
path: Some(path),
|
||||
origin: None,
|
||||
control: Some(control),
|
||||
api: api,
|
||||
handler: as_json_error(&ApiError {
|
||||
handler: response::as_json_error(&ApiError {
|
||||
code: "404".into(),
|
||||
title: "Not Found".into(),
|
||||
detail: "Resource you requested has not been found.".into(),
|
||||
@ -80,11 +89,40 @@ impl RestApiRouter {
|
||||
_ => 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 {
|
||||
|
||||
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);
|
||||
if url.is_none() {
|
||||
// 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());
|
||||
// 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
|
||||
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 {
|
||||
"apps" => Some(as_json(&self.api.list_apps())),
|
||||
"ping" => Some(ping_response(&self.api.local_domain)),
|
||||
"apps" => Some(response::as_json(&self.api.list_apps())),
|
||||
"ping" => Some(response::ping()),
|
||||
"content" => self.resolve_content(hash, path, control),
|
||||
_ => None
|
||||
});
|
||||
@ -121,6 +159,7 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
*res.headers_mut() = self.response_headers();
|
||||
self.handler.on_response(res)
|
||||
}
|
||||
|
||||
|
0
dapps/src/api/cors.rs
Normal file
0
dapps/src/api/cors.rs
Normal file
@ -19,18 +19,22 @@ use serde_json;
|
||||
use endpoint::Handler;
|
||||
use handlers::{ContentHandler, EchoHandler};
|
||||
|
||||
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
||||
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||
pub fn empty() -> Box<Handler> {
|
||||
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
||||
}
|
||||
|
||||
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
|
||||
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
|
||||
let json = serde_json::to_string(val)
|
||||
.expect("serialization to string is infallible; qed");
|
||||
Box::new(ContentHandler::ok(json, mime!(Application/Json)))
|
||||
}
|
||||
|
||||
pub fn ping_response(local_domain: &str) -> Box<Handler> {
|
||||
Box::new(EchoHandler::cors(vec![
|
||||
format!("http://{}", local_domain),
|
||||
// Allow CORS calls also for localhost
|
||||
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
|
||||
]))
|
||||
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
|
||||
let json = serde_json::to_string(val)
|
||||
.expect("serialization to string is infallible; qed");
|
||||
Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
|
||||
}
|
||||
|
||||
pub fn ping() -> Box<Handler> {
|
||||
Box::new(EchoHandler::default())
|
||||
}
|
||||
|
@ -47,15 +47,17 @@ impl ContentCache {
|
||||
}
|
||||
|
||||
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
||||
let mut len = self.cache.len();
|
||||
let len = self.cache.len();
|
||||
|
||||
if len <= expected_size {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut removed = Vec::with_capacity(len - expected_size);
|
||||
while len > expected_size {
|
||||
let entry = self.cache.pop_front().unwrap();
|
||||
|
||||
while self.cache.len() > expected_size {
|
||||
let entry = self.cache.pop_front().expect("expected_size bounded at 0, len is greater; qed");
|
||||
|
||||
match entry.1 {
|
||||
ContentStatus::Fetching(ref fetch) => {
|
||||
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
||||
@ -64,16 +66,15 @@ impl ContentCache {
|
||||
},
|
||||
ContentStatus::Ready(ref endpoint) => {
|
||||
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
||||
// Remove path
|
||||
let res = fs::remove_dir_all(&endpoint.path());
|
||||
// Remove path (dir or file)
|
||||
let res = fs::remove_dir_all(&endpoint.path()).or_else(|_| fs::remove_file(&endpoint.path()));
|
||||
if let Err(e) = res {
|
||||
warn!(target: "dapps", "Unable to remove dapp: {:?}", e);
|
||||
warn!(target: "dapps", "Unable to remove dapp/content from cache: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removed.push(entry);
|
||||
len -= 1;
|
||||
}
|
||||
removed
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ pub struct ContentFetcher<R: URLHint = URLHintContract> {
|
||||
resolver: R,
|
||||
cache: Arc<Mutex<ContentCache>>,
|
||||
sync: Arc<SyncStatus>,
|
||||
embeddable_at: Option<u16>,
|
||||
}
|
||||
|
||||
impl<R: URLHint> Drop for ContentFetcher<R> {
|
||||
@ -57,7 +58,7 @@ impl<R: URLHint> Drop for 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();
|
||||
dapps_path.push(random_filename());
|
||||
|
||||
@ -66,9 +67,19 @@ impl<R: URLHint> ContentFetcher<R> {
|
||||
resolver: resolver,
|
||||
sync: sync_status,
|
||||
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)]
|
||||
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
||||
self.cache.lock().insert(content_id.to_owned(), status);
|
||||
@ -84,12 +95,10 @@ impl<R: URLHint> ContentFetcher<R> {
|
||||
}
|
||||
// fallback to resolver
|
||||
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_syncing() {
|
||||
return true;
|
||||
}
|
||||
// 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 {
|
||||
false
|
||||
}
|
||||
@ -99,28 +108,19 @@ impl<R: URLHint> ContentFetcher<R> {
|
||||
let mut cache = self.cache.lock();
|
||||
let content_id = path.app_id.clone();
|
||||
|
||||
if self.sync.is_major_syncing() {
|
||||
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 status = cache.get(&content_id);
|
||||
match status {
|
||||
// Just server dapp
|
||||
// Just serve the content
|
||||
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
||||
(None, endpoint.to_async_handler(path, control))
|
||||
},
|
||||
// App is already being fetched
|
||||
// Content is already being fetched
|
||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) => {
|
||||
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
||||
(None, fetch_control.to_handler(control))
|
||||
},
|
||||
// We need to start fetching app
|
||||
// We need to start fetching the content
|
||||
None => {
|
||||
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.");
|
||||
@ -141,6 +141,10 @@ impl<R: URLHint> ContentFetcher<R> {
|
||||
};
|
||||
|
||||
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)) => {
|
||||
let (handler, fetch_control) = ContentFetcherHandler::new(
|
||||
dapp.url(),
|
||||
@ -150,6 +154,7 @@ impl<R: URLHint> ContentFetcher<R> {
|
||||
id: content_id.clone(),
|
||||
dapps_path: self.dapps_path.clone(),
|
||||
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>)
|
||||
},
|
||||
None if self.sync.is_major_importing() => {
|
||||
(None, Self::still_syncing())
|
||||
},
|
||||
None => {
|
||||
// This may happen when sync status changes in between
|
||||
// `contains` and `to_handler`
|
||||
@ -271,6 +279,7 @@ struct DappInstaller {
|
||||
id: String,
|
||||
dapps_path: PathBuf,
|
||||
on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>,
|
||||
embeddable_at: Option<u16>,
|
||||
}
|
||||
|
||||
impl DappInstaller {
|
||||
@ -363,7 +372,7 @@ impl ContentValidator for DappInstaller {
|
||||
try!(manifest_file.write_all(manifest_str.as_bytes()));
|
||||
|
||||
// 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
|
||||
Ok((manifest.id.clone(), app))
|
||||
@ -396,14 +405,14 @@ mod tests {
|
||||
fn should_true_if_contains_the_app() {
|
||||
// given
|
||||
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 {
|
||||
name: "fake".into(),
|
||||
description: "".into(),
|
||||
version: "".into(),
|
||||
author: "".into(),
|
||||
icon_url: "".into(),
|
||||
});
|
||||
}, None);
|
||||
|
||||
// when
|
||||
fetcher.set_status("test", ContentStatus::Ready(handler));
|
||||
|
@ -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();
|
||||
for dapp in local_dapps(dapps_path) {
|
||||
pages.insert(
|
||||
dapp.id,
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info))
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, signer_port))
|
||||
);
|
||||
}
|
||||
pages
|
||||
|
@ -25,17 +25,14 @@ pub mod urlhint;
|
||||
pub mod fetcher;
|
||||
pub mod manifest;
|
||||
|
||||
extern crate parity_dapps_status;
|
||||
extern crate parity_dapps_home;
|
||||
extern crate parity_ui;
|
||||
|
||||
pub const HOME_PAGE: &'static str = "home";
|
||||
pub const DAPPS_DOMAIN : &'static str = ".parity";
|
||||
pub const RPC_PATH : &'static str = "rpc";
|
||||
pub const API_PATH : &'static str = "api";
|
||||
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 {
|
||||
if using_dapps_domains {
|
||||
format!("http://{}{}/", app_id, DAPPS_DOMAIN)
|
||||
@ -45,36 +42,29 @@ pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String {
|
||||
}
|
||||
|
||||
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) -> Endpoints {
|
||||
pub fn all_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path);
|
||||
// Home page needs to be safe embed
|
||||
// because we use Cross-Origin LocalStorage.
|
||||
// TODO [ToDr] Account naming should be moved to parity.
|
||||
pages.insert("home".into(), Box::new(
|
||||
PageEndpoint::new_safe_to_embed(parity_dapps_home::App::default())
|
||||
));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed());
|
||||
insert::<parity_dapps_status::App>(&mut pages, "parity");
|
||||
insert::<parity_dapps_status::App>(&mut pages, "status");
|
||||
let mut pages = fs::local_endpoints(dapps_path, signer_port);
|
||||
|
||||
// Optional dapps
|
||||
wallet_page(&mut pages);
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_port));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(signer_port));
|
||||
|
||||
pages
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-dapps-wallet")]
|
||||
fn wallet_page(pages: &mut Endpoints) {
|
||||
extern crate parity_dapps_wallet;
|
||||
insert::<parity_dapps_wallet::App>(pages, "wallet");
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
|
||||
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()),
|
||||
}));
|
||||
}
|
||||
#[cfg(not(feature = "parity-dapps-wallet"))]
|
||||
fn wallet_page(_pages: &mut Endpoints) {}
|
||||
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) {
|
||||
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default())));
|
||||
enum Embeddable {
|
||||
Yes(Option<u16>),
|
||||
#[allow(dead_code)]
|
||||
No,
|
||||
}
|
||||
|
@ -156,9 +156,9 @@ impl URLHintContract {
|
||||
}
|
||||
|
||||
let mut it = vec.into_iter();
|
||||
let account_slash_repo = it.next().unwrap();
|
||||
let commit = it.next().unwrap();
|
||||
let owner = it.next().unwrap();
|
||||
let account_slash_repo = it.next().expect("element 0 of 3-len vector known to exist; qed");
|
||||
let commit = it.next().expect("element 1 of 3-len vector known to exist; qed");
|
||||
let owner = it.next().expect("element 2 of 3-len vector known to exist qed");
|
||||
|
||||
match (account_slash_repo, commit, owner) {
|
||||
(Token::String(account_slash_repo), Token::FixedBytes(commit), Token::Address(owner)) => {
|
||||
|
@ -19,57 +19,60 @@
|
||||
use std::io::Write;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use util::version;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ContentHandler {
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: String,
|
||||
mimetype: Mime,
|
||||
write_pos: usize,
|
||||
safe_to_embed_at_port: Option<u16>,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn ok(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
code: StatusCode::Ok,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
pub fn ok(content: String, mimetype: Mime) -> Self {
|
||||
Self::new(StatusCode::Ok, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn not_found(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
code: StatusCode::NotFound,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
pub fn not_found(content: String, mimetype: Mime) -> Self {
|
||||
Self::new(StatusCode::NotFound, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn html(code: StatusCode, content: String) -> Self {
|
||||
Self::new(code, content, "text/html".into())
|
||||
pub fn html(code: StatusCode, content: String, embeddable_at: Option<u16>) -> Self {
|
||||
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at)
|
||||
}
|
||||
|
||||
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self {
|
||||
Self::error_embeddable(code, title, message, details, None)
|
||||
}
|
||||
|
||||
pub fn error_embeddable(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_at: Option<u16>) -> Self {
|
||||
Self::html(code, format!(
|
||||
include_str!("../error_tpl.html"),
|
||||
title=title,
|
||||
message=message,
|
||||
details=details.unwrap_or_else(|| ""),
|
||||
version=version(),
|
||||
))
|
||||
), embeddable_at)
|
||||
}
|
||||
|
||||
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
|
||||
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
|
||||
Self::new_embeddable(code, content, mimetype, None)
|
||||
}
|
||||
|
||||
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_at: Option<u16>) -> Self {
|
||||
ContentHandler {
|
||||
code: code,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0,
|
||||
safe_to_embed_at_port: embeddable_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,7 +88,8 @@ impl server::Handler<HttpStream> for ContentHandler {
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.code);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port.clone());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
|
@ -17,82 +17,25 @@
|
||||
//! Echo Handler
|
||||
|
||||
use std::io::Read;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::method::Method;
|
||||
use hyper::{server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use unicase::UniCase;
|
||||
use super::ContentHandler;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
/// 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,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EchoHandler {
|
||||
safe_origins: Vec<String>,
|
||||
content: String,
|
||||
cors: Cors,
|
||||
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 {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
let origin = request.headers().get_raw("origin")
|
||||
.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(), "text/plain".into()));
|
||||
Next::write()
|
||||
} else {
|
||||
Next::read()
|
||||
}
|
||||
fn on_request(&mut self, _: server::Request<HttpStream>) -> Next {
|
||||
Next::read()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
match decoder.read_to_string(&mut self.content) {
|
||||
Ok(0) => {
|
||||
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
|
||||
self.handler = Some(ContentHandler::ok(self.content.clone(), mime!(Application/Json)));
|
||||
Next::write()
|
||||
},
|
||||
Ok(_) => Next::read(),
|
||||
@ -104,45 +47,14 @@ impl server::Handler<HttpStream> for EchoHandler {
|
||||
}
|
||||
|
||||
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().unwrap().on_response(res)
|
||||
self.handler.as_mut()
|
||||
.expect("handler always set in on_request, which is before now; qed")
|
||||
.on_response(res)
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
self.handler.as_mut().unwrap().on_response_writable(encoder)
|
||||
self.handler.as_mut()
|
||||
.expect("handler always set in on_request, which is before now; qed")
|
||||
.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);
|
||||
}
|
||||
|
@ -30,7 +30,26 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl};
|
||||
|
||||
use url::Url;
|
||||
use hyper::{server, header, net, uri};
|
||||
use signer_address;
|
||||
|
||||
/// Adds security-related headers to the Response.
|
||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option<u16>) {
|
||||
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
||||
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||
|
||||
// Embedding header:
|
||||
if let Some(port) = embeddable_at {
|
||||
headers.set_raw(
|
||||
"X-Frame-Options",
|
||||
vec![format!("ALLOW-FROM http://{}", signer_address(port)).into_bytes()]
|
||||
);
|
||||
} else {
|
||||
// TODO [ToDr] Should we be more strict here (DENY?)?
|
||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts URL part from the Request.
|
||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||
match *req.uri() {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
|
@ -43,10 +43,8 @@
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate url as url_lib;
|
||||
extern crate hyper;
|
||||
extern crate url as url_lib;
|
||||
extern crate unicase;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
@ -57,13 +55,21 @@ extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
extern crate mime_guess;
|
||||
extern crate rustc_serialize;
|
||||
extern crate parity_dapps;
|
||||
extern crate ethcore_rpc;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate linked_hash_map;
|
||||
extern crate fetch;
|
||||
extern crate parity_dapps_glue as parity_dapps;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate mime;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ethcore_devtools as devtools;
|
||||
#[cfg(test)]
|
||||
extern crate env_logger;
|
||||
|
||||
|
||||
mod endpoint;
|
||||
mod apps;
|
||||
@ -87,16 +93,16 @@ use jsonrpc_core::{IoHandler, IoDelegate};
|
||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||
use ethcore_rpc::Extendable;
|
||||
|
||||
static DAPPS_DOMAIN : &'static str = ".parity";
|
||||
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
|
||||
|
||||
/// Indicates sync status
|
||||
pub trait SyncStatus: Send + Sync {
|
||||
/// Returns true if there is a major sync happening.
|
||||
fn is_major_syncing(&self) -> bool;
|
||||
fn is_major_importing(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
||||
fn is_major_syncing(&self) -> bool { self() }
|
||||
fn is_major_importing(&self) -> bool { self() }
|
||||
}
|
||||
|
||||
/// Webapps HTTP+RPC server build.
|
||||
@ -105,6 +111,7 @@ pub struct ServerBuilder {
|
||||
handler: Arc<IoHandler>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
signer_port: Option<u16>,
|
||||
}
|
||||
|
||||
impl Extendable for ServerBuilder {
|
||||
@ -121,6 +128,7 @@ impl ServerBuilder {
|
||||
handler: Arc::new(IoHandler::new()),
|
||||
registrar: registrar,
|
||||
sync_status: Arc::new(|| false),
|
||||
signer_port: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,6 +137,11 @@ impl ServerBuilder {
|
||||
self.sync_status = status;
|
||||
}
|
||||
|
||||
/// Change default signer port.
|
||||
pub fn with_signer_port(&mut self, signer_port: Option<u16>) {
|
||||
self.signer_port = signer_port;
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||
@ -138,6 +151,7 @@ impl ServerBuilder {
|
||||
NoAuth,
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
self.signer_port.clone(),
|
||||
self.registrar.clone(),
|
||||
self.sync_status.clone(),
|
||||
)
|
||||
@ -152,6 +166,7 @@ impl ServerBuilder {
|
||||
HttpBasicAuth::single_user(username, password),
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
self.signer_port.clone(),
|
||||
self.registrar.clone(),
|
||||
self.sync_status.clone(),
|
||||
)
|
||||
@ -180,26 +195,40 @@ impl Server {
|
||||
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>(
|
||||
addr: &SocketAddr,
|
||||
hosts: Option<Vec<String>>,
|
||||
authorization: A,
|
||||
handler: Arc<IoHandler>,
|
||||
dapps_path: String,
|
||||
signer_port: Option<u16>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
) -> Result<Server, ServerError> {
|
||||
let panic_handler = Arc::new(Mutex::new(None));
|
||||
let authorization = Arc::new(authorization);
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status));
|
||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
||||
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.clone()));
|
||||
let cors_domains = Self::cors_domains(signer_port);
|
||||
|
||||
let special = Arc::new({
|
||||
let mut special = HashMap::new();
|
||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||
special.insert(
|
||||
router::SpecialEndpoint::Api,
|
||||
api::RestApi::new(format!("{}", addr), endpoints.clone(), content_fetcher.clone())
|
||||
api::RestApi::new(cors_domains, endpoints.clone(), content_fetcher.clone())
|
||||
);
|
||||
special
|
||||
});
|
||||
@ -208,7 +237,7 @@ impl Server {
|
||||
try!(hyper::Server::http(addr))
|
||||
.handle(move |ctrl| router::Router::new(
|
||||
ctrl,
|
||||
apps::main_page(),
|
||||
signer_port.clone(),
|
||||
content_fetcher.clone(),
|
||||
endpoints.clone(),
|
||||
special.clone(),
|
||||
@ -272,6 +301,10 @@ pub fn random_filename() -> String {
|
||||
rng.gen_ascii_chars().take(12).collect()
|
||||
}
|
||||
|
||||
fn signer_address(port: u16) -> String {
|
||||
format!("127.0.0.1:{}", port)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod util_tests {
|
||||
use super::Server;
|
||||
@ -291,4 +324,17 @@ mod util_tests {
|
||||
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()]));
|
||||
}
|
||||
|
||||
#[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()]);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
|
||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||
pub prefix: Option<String>,
|
||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||
safe_to_embed: bool,
|
||||
safe_to_embed_at_port: Option<u16>,
|
||||
info: EndpointInfo,
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
safe_to_embed: false,
|
||||
safe_to_embed_at_port: None,
|
||||
info: EndpointInfo::from(info),
|
||||
}
|
||||
}
|
||||
@ -49,7 +49,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: Some(prefix),
|
||||
safe_to_embed: false,
|
||||
safe_to_embed_at_port: None,
|
||||
info: EndpointInfo::from(info),
|
||||
}
|
||||
}
|
||||
@ -57,12 +57,12 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||
/// even from different origin. It might be dangerous (clickjacking).
|
||||
/// Use wisely!
|
||||
pub fn new_safe_to_embed(app: T) -> Self {
|
||||
pub fn new_safe_to_embed(app: T, port: Option<u16>) -> Self {
|
||||
let info = app.info();
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
safe_to_embed: true,
|
||||
safe_to_embed_at_port: port,
|
||||
info: EndpointInfo::from(info),
|
||||
}
|
||||
}
|
||||
@ -79,8 +79,8 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
||||
app: BuiltinDapp::new(self.app.clone()),
|
||||
prefix: self.prefix.clone(),
|
||||
path: path,
|
||||
file: Default::default(),
|
||||
safe_to_embed: self.safe_to_embed,
|
||||
file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()),
|
||||
safe_to_embed_at_port: self.safe_to_embed_at_port.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{Decoder, Encoder, Next};
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::ContentHandler;
|
||||
use handlers::{ContentHandler, add_security_headers};
|
||||
|
||||
/// Represents a file that can be sent to client.
|
||||
/// Implementation should keep track of bytes already sent internally.
|
||||
@ -57,13 +57,14 @@ pub enum ServedFile<T: Dapp> {
|
||||
Error(ContentHandler),
|
||||
}
|
||||
|
||||
impl<T: Dapp> Default for ServedFile<T> {
|
||||
fn default() -> Self {
|
||||
ServedFile::Error(ContentHandler::error(
|
||||
impl<T: Dapp> ServedFile<T> {
|
||||
pub fn new(embeddable_at: Option<u16>) -> Self {
|
||||
ServedFile::Error(ContentHandler::error_embeddable(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Requested dapp resource was not found.",
|
||||
None
|
||||
None,
|
||||
embeddable_at,
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -81,7 +82,7 @@ pub struct PageHandler<T: Dapp> {
|
||||
/// Requested path.
|
||||
pub path: EndpointPath,
|
||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||
pub safe_to_embed: bool,
|
||||
pub safe_to_embed_at_port: Option<u16>,
|
||||
}
|
||||
|
||||
impl<T: Dapp> PageHandler<T> {
|
||||
@ -115,7 +116,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
||||
self.app.file(&self.extract_path(url.path()))
|
||||
},
|
||||
_ => None,
|
||||
}.map_or_else(|| ServedFile::default(), |f| ServedFile::File(f));
|
||||
}.map_or_else(|| ServedFile::new(self.safe_to_embed_at_port.clone()), |f| ServedFile::File(f));
|
||||
Next::write()
|
||||
}
|
||||
|
||||
@ -127,10 +128,14 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
||||
match self.file {
|
||||
ServedFile::File(ref f) => {
|
||||
res.set_status(StatusCode::Ok);
|
||||
res.headers_mut().set(header::ContentType(f.content_type().parse().unwrap()));
|
||||
if !self.safe_to_embed {
|
||||
res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||
|
||||
match f.content_type().parse() {
|
||||
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
||||
Err(()) => debug!(target: "page_handler", "invalid MIME type: {}", f.content_type()),
|
||||
}
|
||||
|
||||
// Security headers:
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port);
|
||||
Next::write()
|
||||
},
|
||||
ServedFile::Error(ref mut handler) => {
|
||||
@ -212,8 +217,8 @@ fn should_extract_path_with_appid() {
|
||||
port: 8080,
|
||||
using_dapps_domains: true,
|
||||
},
|
||||
file: Default::default(),
|
||||
safe_to_embed: true,
|
||||
file: ServedFile::new(None),
|
||||
safe_to_embed_at_port: None,
|
||||
};
|
||||
|
||||
// when
|
||||
|
@ -26,14 +26,16 @@ pub struct LocalPageEndpoint {
|
||||
path: PathBuf,
|
||||
mime: Option<String>,
|
||||
info: Option<EndpointInfo>,
|
||||
embeddable_at: Option<u16>,
|
||||
}
|
||||
|
||||
impl LocalPageEndpoint {
|
||||
pub fn new(path: PathBuf, info: EndpointInfo) -> Self {
|
||||
pub fn new(path: PathBuf, info: EndpointInfo, embeddable_at: Option<u16>) -> Self {
|
||||
LocalPageEndpoint {
|
||||
path: path,
|
||||
mime: None,
|
||||
info: Some(info),
|
||||
embeddable_at: embeddable_at,
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +44,7 @@ impl LocalPageEndpoint {
|
||||
path: path,
|
||||
mime: Some(mime),
|
||||
info: None,
|
||||
embeddable_at: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,16 +64,16 @@ impl Endpoint for LocalPageEndpoint {
|
||||
app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() },
|
||||
prefix: None,
|
||||
path: path,
|
||||
file: Default::default(),
|
||||
safe_to_embed: false,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_at_port: self.embeddable_at,
|
||||
})
|
||||
} else {
|
||||
Box::new(handler::PageHandler {
|
||||
app: LocalDapp { path: self.path.clone() },
|
||||
prefix: None,
|
||||
path: path,
|
||||
file: Default::default(),
|
||||
safe_to_embed: false,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_at_port: self.embeddable_at,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -18,31 +18,46 @@
|
||||
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
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 {
|
||||
pub fn boxed() -> Box<Endpoint> {
|
||||
Box::new(ProxyPac)
|
||||
pub fn boxed(signer_port: Option<u16>) -> Box<Endpoint> {
|
||||
Box::new(ProxyPac {
|
||||
signer_port: signer_port
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for ProxyPac {
|
||||
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!(
|
||||
r#"
|
||||
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";
|
||||
}}
|
||||
"#,
|
||||
DAPPS_DOMAIN, path.host, path.port);
|
||||
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
|
||||
HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
|
||||
|
||||
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
// 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::net::HttpStream;
|
||||
|
||||
|
@ -20,13 +20,13 @@
|
||||
pub mod auth;
|
||||
mod host_validation;
|
||||
|
||||
use DAPPS_DOMAIN;
|
||||
use signer_address;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use url::{Url, Host};
|
||||
use hyper::{self, server, Next, Encoder, Decoder, Control, StatusCode};
|
||||
use hyper::net::HttpStream;
|
||||
use apps;
|
||||
use apps::{self, DAPPS_DOMAIN};
|
||||
use apps::fetcher::ContentFetcher;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||
use handlers::{Redirection, extract_url, ContentHandler};
|
||||
@ -43,7 +43,7 @@ pub enum SpecialEndpoint {
|
||||
|
||||
pub struct Router<A: Authorization + 'static> {
|
||||
control: Option<Control>,
|
||||
main_page: &'static str,
|
||||
signer_port: Option<u16>,
|
||||
endpoints: Arc<Endpoints>,
|
||||
fetch: Arc<ContentFetcher>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
@ -61,54 +61,85 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
let endpoint = extract_endpoint(&url);
|
||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||
|
||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
|
||||
|
||||
// Validate Host header
|
||||
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());
|
||||
if !is_valid {
|
||||
debug!(target: "dapps", "Rejecting invalid host header.");
|
||||
self.handler = host_validation::host_invalid_response();
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
}
|
||||
|
||||
trace!(target: "dapps", "Checking authorization.");
|
||||
// Check authorization
|
||||
let auth = self.authorization.is_authorized(&req);
|
||||
if let Authorized::No(handler) = auth {
|
||||
debug!(target: "dapps", "Authorization denied.");
|
||||
self.handler = handler;
|
||||
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");
|
||||
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
||||
self.handler = match endpoint {
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||
self.special.get(endpoint).unwrap().to_async_handler(path.clone().unwrap_or_default(), control)
|
||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||
self.special.get(endpoint)
|
||||
.expect("special known to contain key; qed")
|
||||
.to_async_handler(path.clone().unwrap_or_default(), control)
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||
self.endpoints.get(&path.app_id).unwrap().to_async_handler(path.clone(), control)
|
||||
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||
self.endpoints.get(&path.app_id)
|
||||
.expect("special known to contain key; qed")
|
||||
.to_async_handler(path.clone(), control)
|
||||
},
|
||||
// Try to resolve and fetch the dapp
|
||||
(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)
|
||||
},
|
||||
// NOTE [todr] /home is redirected to home page since some users may have the redirection cached
|
||||
// (in the past we used 301 instead of 302)
|
||||
// It should be safe to remove it in (near) future.
|
||||
//
|
||||
// 404 for non-existent content
|
||||
(Some(ref path), _) if *req.method() == hyper::method::Method::Get => {
|
||||
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
||||
(Some(ref path), _) if *req.method() == hyper::Method::Get && path.app_id != "home" => {
|
||||
trace!(target: "dapps", "Resolving to 404.");
|
||||
Box::new(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 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.
|
||||
_ if *req.method() == hyper::method::Method::Get => {
|
||||
let address = apps::redirection_address(false, self.main_page);
|
||||
Redirection::boxed(address.as_str())
|
||||
// Redirect any other GET request to signer.
|
||||
_ if *req.method() == hyper::Method::Get => {
|
||||
if let Some(port) = self.signer_port {
|
||||
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
|
||||
_ => {
|
||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_async_handler(EndpointPath::default(), control)
|
||||
trace!(target: "dapps", "Resolving to RPC call.");
|
||||
self.special.get(&SpecialEndpoint::Rpc)
|
||||
.expect("RPC endpoint always stored; qed")
|
||||
.to_async_handler(EndpointPath::default(), control)
|
||||
}
|
||||
};
|
||||
|
||||
@ -135,7 +166,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
impl<A: Authorization> Router<A> {
|
||||
pub fn new(
|
||||
control: Control,
|
||||
main_page: &'static str,
|
||||
signer_port: Option<u16>,
|
||||
content_fetcher: Arc<ContentFetcher>,
|
||||
endpoints: Arc<Endpoints>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
@ -143,10 +174,12 @@ impl<A: Authorization> Router<A> {
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
|
||||
let handler = special.get(&SpecialEndpoint::Utils).unwrap().to_handler(EndpointPath::default());
|
||||
let handler = special.get(&SpecialEndpoint::Utils)
|
||||
.expect("Utils endpoint always stored; qed")
|
||||
.to_handler(EndpointPath::default());
|
||||
Router {
|
||||
control: Some(control),
|
||||
main_page: main_page,
|
||||
signer_port: signer_port,
|
||||
endpoints: endpoints,
|
||||
fetch: content_fetcher,
|
||||
special: special,
|
||||
|
@ -24,7 +24,7 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
|
||||
Box::new(RpcEndpoint {
|
||||
handler: 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.
|
||||
allowed_hosts: None,
|
||||
})
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve, serve_with_registrar, request};
|
||||
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_return_error() {
|
||||
@ -34,8 +34,9 @@ fn should_return_error() {
|
||||
|
||||
// then
|
||||
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_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -56,8 +57,9 @@ fn should_serve_apps() {
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||
assert!(response.body.contains("Parity Home Screen"), response.body);
|
||||
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
||||
assert!(response.body.contains("Parity UI"), response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -78,8 +80,9 @@ fn should_handle_ping() {
|
||||
|
||||
// then
|
||||
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_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
|
||||
@ -101,5 +104,57 @@ fn should_try_to_resolve_dapp() {
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
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
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve_with_auth, request};
|
||||
use tests::helpers::{serve_with_auth, request, assert_security_headers_for_embed};
|
||||
|
||||
#[test]
|
||||
fn should_require_authorization() {
|
||||
@ -66,7 +66,7 @@ fn should_allow_on_valid_auth() {
|
||||
// when
|
||||
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\
|
||||
Connection: close\r\n\
|
||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
||||
@ -76,4 +76,5 @@ fn should_allow_on_valid_auth() {
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve_with_registrar, request};
|
||||
use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_resolve_dapp() {
|
||||
@ -34,5 +34,34 @@ fn should_resolve_dapp() {
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
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);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use std::env;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use env_logger::LogBuilder;
|
||||
|
||||
use ServerBuilder;
|
||||
use Server;
|
||||
@ -27,6 +28,7 @@ use devtools::http_client;
|
||||
|
||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||
const SIGNER_PORT: u16 = 18180;
|
||||
|
||||
pub struct FakeRegistrar {
|
||||
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 mut dapps_path = env::temp_dir();
|
||||
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(),
|
||||
registrar,
|
||||
@ -70,25 +84,38 @@ pub fn init_server(hosts: Option<Vec<String>>) -> (Server, Arc<FakeRegistrar>) {
|
||||
}
|
||||
|
||||
pub fn serve_with_auth(user: &str, pass: &str) -> Server {
|
||||
init_logger();
|
||||
let registrar = Arc::new(FakeRegistrar::new());
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
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()
|
||||
}
|
||||
|
||||
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>) {
|
||||
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 {
|
||||
init_server(None).0
|
||||
init_server(None, false).0
|
||||
}
|
||||
|
||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||
http_client::request(server.addr(), request)
|
||||
}
|
||||
|
||||
pub fn assert_security_headers(headers: &[String]) {
|
||||
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))
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve, request};
|
||||
use tests::helpers::{serve, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_redirect_to_home() {
|
||||
@ -33,7 +33,7 @@ fn should_redirect_to_home() {
|
||||
|
||||
// then
|
||||
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]
|
||||
@ -53,7 +53,27 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
|
||||
|
||||
// then
|
||||
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]
|
||||
fn should_redirect_to_home_for_users_with_cached_redirection() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /home/ HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -73,7 +93,7 @@ fn should_display_404_on_invalid_dapp() {
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||
assert!(response.body.contains("href=\"/home/"));
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -93,7 +113,7 @@ fn should_display_404_on_invalid_dapp_with_domain() {
|
||||
|
||||
// then
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -159,7 +179,8 @@ fn should_serve_proxy_pac() {
|
||||
|
||||
// then
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -181,5 +202,6 @@ fn should_serve_utils() {
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
assert_eq!(response.body.contains("function(){"), true);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ fn should_allow_valid_host() {
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /home/ HTTP/1.1\r\n\
|
||||
GET /ui/ HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
@ -66,7 +66,7 @@ fn should_serve_dapps_domains() {
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: home.parity\r\n\
|
||||
Host: ui.parity\r\n\
|
||||
Connection: close\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());
|
||||
}
|
||||
|
||||
#[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
|
||||
);
|
||||
}
|
||||
|
||||
|
18
dapps/ui/Cargo.toml
Normal file
18
dapps/ui/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
description = "Ethcore Parity UI"
|
||||
homepage = "http://ethcore.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-ui"
|
||||
version = "1.4.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.1"
|
||||
|
||||
[dependencies]
|
||||
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"]
|
33
dapps/ui/src/lib.rs
Normal file
33
dapps/ui/src/lib.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#[cfg(feature = "parity-ui-dev")]
|
||||
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::*;
|
@ -11,7 +11,7 @@ build = "build.rs"
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
clippy = { version = "0.0.90", optional = true}
|
||||
clippy = { version = "0.0.96", optional = true}
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
||||
|
@ -64,3 +64,25 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all required security headers are present
|
||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||
if let Some(port) = port {
|
||||
assert!(
|
||||
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!(
|
||||
headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(),
|
||||
"X-XSS-Protection missing: {:?}", headers
|
||||
);
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
||||
"X-Content-Type-Options missing: {:?}", headers
|
||||
);
|
||||
}
|
||||
|
39
docker/ubuntu-stable/Dockerfile
Normal file
39
docker/ubuntu-stable/Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
g++ \
|
||||
curl \
|
||||
git \
|
||||
file \
|
||||
binutils
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
# rustup directory
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout stable && \
|
||||
git pull && \
|
||||
cargo build --release --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
|
||||
RUN file /build/parity/target/release/parity
|
||||
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/build/parity/target/release/parity"]
|
@ -9,4 +9,4 @@ authors = ["arkpar <arkadiy@ethcore.io"]
|
||||
log = "0.3"
|
||||
sha3 = { path = "../util/sha3" }
|
||||
primal = "0.2.3"
|
||||
parking_lot = "0.2.6"
|
||||
parking_lot = "0.3"
|
||||
|
@ -69,14 +69,19 @@ impl EthashManager {
|
||||
Some(ref e) if *e == epoch => lights.recent.clone(),
|
||||
_ => match lights.prev_epoch.clone() {
|
||||
Some(e) if e == epoch => {
|
||||
// swap
|
||||
let t = lights.prev_epoch;
|
||||
lights.prev_epoch = lights.recent_epoch;
|
||||
lights.recent_epoch = t;
|
||||
let t = lights.prev.clone();
|
||||
lights.prev = lights.recent.clone();
|
||||
lights.recent = t;
|
||||
lights.recent.clone()
|
||||
// don't swap if recent is newer.
|
||||
if lights.recent_epoch > lights.prev_epoch {
|
||||
None
|
||||
} else {
|
||||
// swap
|
||||
let t = lights.prev_epoch;
|
||||
lights.prev_epoch = lights.recent_epoch;
|
||||
lights.recent_epoch = t;
|
||||
let t = lights.prev.clone();
|
||||
lights.prev = lights.recent.clone();
|
||||
lights.recent = t;
|
||||
lights.recent.clone()
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
|
@ -24,8 +24,11 @@ rayon = "0.4.2"
|
||||
semver = "0.2"
|
||||
bit-set = "0.4"
|
||||
time = "0.1"
|
||||
rand = "0.3"
|
||||
byteorder = "0.5"
|
||||
transient-hashmap = "0.1"
|
||||
evmjit = { path = "../evmjit", optional = true }
|
||||
clippy = { version = "0.0.90", optional = true}
|
||||
clippy = { version = "0.0.96", optional = true}
|
||||
ethash = { path = "../ethash" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-io = { path = "../util/io" }
|
||||
@ -36,10 +39,8 @@ ethstore = { path = "../ethstore" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
rlp = { path = "../util/rlp" }
|
||||
rand = "0.3"
|
||||
lru-cache = "0.0.7"
|
||||
lru-cache = "0.1.0"
|
||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
||||
byteorder = "0.5"
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/ethcore/hyper"
|
||||
|
@ -10,7 +10,8 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
"homesteadTransition": "0x118c30",
|
||||
"eip150Transition": "0x2625a0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -38,10 +39,18 @@
|
||||
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://08c7ee6a4f861ff0664a49532bcc86de1363acd608999d1b76609bb9bc278649906f069057630fd9493924a368b5d1dc9b8f8bf13ac26df72512f6d1fabd8c95@45.32.7.81:30303",
|
||||
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
|
||||
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
|
||||
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
|
||||
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303"
|
||||
"enode://ca5ae4eca09ba6787e29cf6d86f7634d07aae6b9e6317a59aff675851c0bf445068173208cf8ef7f5cd783d8e29b85b2fa3fa358124cf0546823149724f9bde1@138.68.1.16:30303",
|
||||
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303",
|
||||
"enode://fa20444ef991596ce99b81652ac4e61de1eddc4ff21d3cd42762abd7ed47e7cf044d3c9ccddaf6035d39725e4eb372806787829ccb9a08ec7cb71883cb8c3abd@50.149.116.182:30303",
|
||||
"enode://4bd6a4df3612c718333eb5ea7f817923a8cdf1bed89cee70d1710b45a0b6b77b2819846440555e451a9b602ad2efa2d2facd4620650249d8468008946887820a@71.178.232.20:30304",
|
||||
"enode://921cf8e4c345fe8db913c53964f9cadc667644e7f20195a0b7d877bd689a5934e146ff2c2259f1bae6817b6585153a007ceb67d260b720fa3e6fc4350df25c7f@51.255.49.170:30303",
|
||||
"enode://ffea3b01c000cdd89e1e9229fea3e80e95b646f9b2aa55071fc865e2f19543c9b06045cc2e69453e6b78100a119e66be1b5ad50b36f2ffd27293caa28efdd1b2@128.199.93.177:3030",
|
||||
"enode://ee3da491ce6a155eb132708eb0e8d04b0637926ec0ae1b79e63fc97cb9fc3818f49250a0ae0d7f79ed62b66ec677f408c4e01741504dc7a051e274f1e803d454@91.121.65.179:40404",
|
||||
"enode://48e063a6cf5f335b1ef2ed98126bf522cf254396f850c7d442fe2edbbc23398787e14cd4de7968a00175a82762de9cbe9e1407d8ccbcaeca5004d65f8398d759@159.203.255.59:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
43
ethcore/res/ethereum/eip150_test.json
Normal file
43
ethcore/res/ethereum/eip150_test.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
@ -11,10 +11,11 @@
|
||||
"durationLimit": "0x3C",
|
||||
"blockReward": "0x6f05b59d3b200000",
|
||||
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
||||
"frontierCompatibilityModeLimit": "0x30d40",
|
||||
"homesteadTransition": "0x30d40",
|
||||
"difficultyHardforkTransition": "0x59d9",
|
||||
"difficultyHardforkBoundDivisor": "0x0200",
|
||||
"bombDefuseTransition": "0x30d40"
|
||||
"bombDefuseTransition": "0x30d40",
|
||||
"eip150Transition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8,11 +8,11 @@
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"registrar" : "0x3bb2bb5c6c9c9b7f4ef430b47dc7e026310042ea",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
"daoHardforkAccounts": [
|
||||
"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728",
|
||||
"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425",
|
||||
"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f",
|
||||
@ -129,7 +129,8 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0x259518"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -157,13 +158,21 @@
|
||||
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@136.243.154.245:30303",
|
||||
"enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303",
|
||||
"enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303",
|
||||
"enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303",
|
||||
"enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303",
|
||||
"enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303",
|
||||
"enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303",
|
||||
"enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303",
|
||||
"enode://84f5d5957b4880a8b0545e32e05472318898ad9fc8ebe1d56c90c12334a98e12351eccfdf3a2bf72432ac38b57e9d348400d17caa083879ade3822390f89773f@10.1.52.78:30303",
|
||||
"enode://f90dc9b9bf7b8db97726b7849e175f1eb2707f3d8f281c929336e398dd89b0409fc6aeceb89e846278e9d3ecc3857cebfbe6758ff352ece6fe5d42921ee761db@10.1.173.87:30303",
|
||||
"enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@10.3.149.199:30303",
|
||||
"enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303",
|
||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
|
||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||
"enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io:30303"
|
||||
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
@ -9,7 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
@ -129,7 +129,8 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,7 +9,8 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
"homesteadTransition": "0x7fffffffffffffff",
|
||||
"eip150Transition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,7 +9,8 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x0"
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,7 +9,8 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
||||
"frontierCompatibilityModeLimit": "0x789b0"
|
||||
"homesteadTransition": "0x789b0",
|
||||
"eip150Transition": "0x1b34d8"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -17,7 +18,9 @@
|
||||
"accountStartNonce": "0x0100000",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
"networkID" : "0x2",
|
||||
"forkBlock": "0x1b34d8",
|
||||
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -8,7 +8,9 @@
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"homesteadTransition": "0x7fffffffffffffff",
|
||||
"eip150Transition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit ac5475d676536cb945f98e9ff98384c01abd0599
|
||||
Subproject commit 97066e40ccd061f727deb5cd860e4d9135aa2551
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "DAO hard-fork consensus test",
|
||||
"name": "EIP150.1b hard-fork consensus test",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
@ -9,7 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x5",
|
||||
"homesteadTransition": "0x5",
|
||||
"daoHardforkTransition": "0x8",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
@ -129,7 +129,8 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0xa"
|
||||
}
|
||||
}
|
||||
},
|
@ -96,9 +96,9 @@ impl<'db> HashDB for AccountDB<'db>{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
||||
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||
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))
|
||||
}
|
||||
@ -114,7 +114,7 @@ impl<'db> HashDB for AccountDB<'db>{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn emplace(&mut self, _key: H256, _value: Bytes) {
|
||||
fn emplace(&mut self, _key: H256, _value: DBValue) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ impl<'db> HashDB for AccountDB<'db>{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
|
||||
fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
|
||||
self.db.get_aux(hash)
|
||||
}
|
||||
}
|
||||
@ -158,9 +158,9 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
||||
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||
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))
|
||||
}
|
||||
@ -178,16 +178,16 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
}
|
||||
let k = value.sha3();
|
||||
let ak = combine_key(&self.address_hash, &k);
|
||||
self.db.emplace(ak, value.to_vec());
|
||||
self.db.emplace(ak, DBValue::from_slice(value));
|
||||
k
|
||||
}
|
||||
|
||||
fn emplace(&mut self, key: H256, value: Bytes) {
|
||||
fn emplace(&mut self, key: H256, value: DBValue) {
|
||||
if key == SHA3_NULL_RLP {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
@ -202,7 +202,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
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)
|
||||
}
|
||||
|
||||
@ -218,9 +218,9 @@ impl<'db> HashDB for Wrapping<'db> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
||||
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return Some(&NULL_RLP_STATIC);
|
||||
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
|
||||
}
|
||||
self.0.get(key)
|
||||
}
|
||||
@ -236,7 +236,7 @@ impl<'db> HashDB for Wrapping<'db> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn emplace(&mut self, _key: H256, _value: Bytes) {
|
||||
fn emplace(&mut self, _key: H256, _value: DBValue) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@ -252,9 +252,9 @@ impl<'db> HashDB for WrappingMut<'db>{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
||||
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return Some(&NULL_RLP_STATIC);
|
||||
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
|
||||
}
|
||||
self.0.get(key)
|
||||
}
|
||||
@ -273,7 +273,7 @@ impl<'db> HashDB for WrappingMut<'db>{
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
@ -286,4 +286,4 @@ impl<'db> HashDB for WrappingMut<'db>{
|
||||
}
|
||||
self.0.remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ use std::time::{Instant, Duration};
|
||||
use util::{Mutex, RwLock};
|
||||
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
||||
use ethstore::dir::{KeyDirectory};
|
||||
use ethstore::ethkey::{Address, Message, Secret, Random, Generator};
|
||||
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
||||
use ethjson::misc::AccountMeta;
|
||||
pub use ethstore::ethkey::Signature;
|
||||
|
||||
@ -36,7 +36,7 @@ enum Unlock {
|
||||
/// Use with caution.
|
||||
Perm,
|
||||
/// Account unlocked with a timeout
|
||||
Timed((Instant, u32)),
|
||||
Timed(Instant),
|
||||
}
|
||||
|
||||
/// Data associated with account.
|
||||
@ -176,15 +176,23 @@ impl AccountProvider {
|
||||
AccountProvider {
|
||||
unlocked: Mutex::new(HashMap::new()),
|
||||
address_book: Mutex::new(AddressBook::new(Default::default())),
|
||||
sstore: Box::new(EthStore::open(Box::new(NullDir::default())).unwrap())
|
||||
sstore: Box::new(EthStore::open(Box::new(NullDir::default()))
|
||||
.expect("NullDir load always succeeds; qed"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new random account.
|
||||
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
self.new_account_and_public(password).map(|d| d.0)
|
||||
}
|
||||
|
||||
/// Creates new random account and returns address and public key
|
||||
pub fn new_account_and_public(&self, password: &str) -> Result<(Address, Public), Error> {
|
||||
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||
let public = acc.public().clone();
|
||||
let secret = acc.secret().clone();
|
||||
let address = try!(self.sstore.insert_account(secret, password));
|
||||
Ok(address)
|
||||
Ok((address, public))
|
||||
}
|
||||
|
||||
/// Inserts new account into underlying store.
|
||||
@ -257,6 +265,20 @@ impl AccountProvider {
|
||||
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.
|
||||
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
||||
// verify password by signing dump message
|
||||
@ -280,6 +302,21 @@ impl AccountProvider {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn password(&self, account: &Address) -> Result<String, Error> {
|
||||
let mut unlocked = self.unlocked.lock();
|
||||
let data = try!(unlocked.get(account).ok_or(Error::NotUnlocked)).clone();
|
||||
if let Unlock::Temp = data.unlock {
|
||||
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
if let Unlock::Timed(ref end) = data.unlock {
|
||||
if Instant::now() > *end {
|
||||
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||
return Err(Error::NotUnlocked);
|
||||
}
|
||||
}
|
||||
Ok(data.password.clone())
|
||||
}
|
||||
|
||||
/// Unlocks account permanently.
|
||||
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::Perm)
|
||||
@ -292,7 +329,7 @@ impl AccountProvider {
|
||||
|
||||
/// Unlocks account temporarily with a timeout.
|
||||
pub fn unlock_account_timed(&self, account: Address, password: String, duration_ms: u32) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::Timed((Instant::now(), duration_ms)))
|
||||
self.unlock_account(account, password, Unlock::Timed(Instant::now() + Duration::from_millis(duration_ms as u64)))
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked
|
||||
@ -301,51 +338,16 @@ impl AccountProvider {
|
||||
unlocked.get(&account).is_some()
|
||||
}
|
||||
|
||||
/// Signs the message. Account must be unlocked.
|
||||
pub fn sign(&self, account: Address, message: Message) -> Result<Signature, Error> {
|
||||
let data = {
|
||||
let mut unlocked = self.unlocked.lock();
|
||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
||||
if let Unlock::Temp = data.unlock {
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
return Err(Error::NotUnlocked);
|
||||
}
|
||||
}
|
||||
data
|
||||
};
|
||||
|
||||
let signature = try!(self.sstore.sign(&account, &data.password, &message));
|
||||
Ok(signature)
|
||||
/// Signs the message. If password is not provided the account must be unlocked.
|
||||
pub fn sign(&self, account: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||
Ok(try!(self.sstore.sign(&account, &password, &message)))
|
||||
}
|
||||
|
||||
/// Decrypts a message. Account must be unlocked.
|
||||
pub fn decrypt(&self, account: Address, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let data = {
|
||||
let mut unlocked = self.unlocked.lock();
|
||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
||||
if let Unlock::Temp = data.unlock {
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
return Err(Error::NotUnlocked);
|
||||
}
|
||||
}
|
||||
data
|
||||
};
|
||||
|
||||
Ok(try!(self.sstore.decrypt(&account, &data.password, shared_mac, message)))
|
||||
}
|
||||
|
||||
/// Unlocks an account, signs the message, and locks it again.
|
||||
pub fn sign_with_password(&self, account: Address, password: String, message: Message) -> Result<Signature, Error> {
|
||||
let signature = try!(self.sstore.sign(&account, &password, &message));
|
||||
Ok(signature)
|
||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||
pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||
Ok(try!(self.sstore.decrypt(&account, &password, shared_mac, message)))
|
||||
}
|
||||
|
||||
/// Returns the underlying `SecretStore` reference if one exists.
|
||||
@ -361,11 +363,11 @@ impl AccountProvider {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AccountProvider, AddressBook};
|
||||
use super::{AccountProvider, AddressBook, Unlock};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
use ethjson::misc::AccountMeta;
|
||||
use ethstore::ethkey::{Generator, Random};
|
||||
use std::time::Duration;
|
||||
use devtools::RandomTempPath;
|
||||
|
||||
#[test]
|
||||
@ -386,8 +388,8 @@ mod tests {
|
||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -397,11 +399,11 @@ mod tests {
|
||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -409,10 +411,10 @@ mod tests {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
||||
::std::thread::sleep(Duration::from_millis(2000));
|
||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 60000).is_err());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 60000).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
ap.unlocked.lock().get_mut(&kp.address()).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,14 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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 types::executed::CallType;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Transaction value
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ActionValue {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
//! Ethcore basic typenames.
|
||||
|
||||
use util::*;
|
||||
use util::hash::H2048;
|
||||
|
||||
/// Type for a 2048-bit log-bloom, as used by our blocks.
|
||||
pub type LogBloom = H2048;
|
||||
|
@ -350,7 +350,7 @@ impl<'x> OpenBlock<'x> {
|
||||
let t = outcome.trace;
|
||||
self.block.traces.as_mut().map(|traces| traces.push(t));
|
||||
self.block.receipts.push(outcome.receipt);
|
||||
Ok(self.block.receipts.last().unwrap())
|
||||
Ok(self.block.receipts.last().expect("receipt just pushed; qed"))
|
||||
}
|
||||
Err(x) => Err(From::from(x))
|
||||
}
|
||||
@ -404,6 +404,10 @@ impl<'x> OpenBlock<'x> {
|
||||
uncle_bytes: uncle_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Return mutable block reference. To be used in tests only.
|
||||
pub fn block_mut (&mut self) -> &mut ExecutedBlock { &mut self.block }
|
||||
}
|
||||
|
||||
impl<'x> IsBlock for OpenBlock<'x> {
|
||||
@ -538,7 +542,7 @@ pub fn enact(
|
||||
Ok(b.close_and_lock())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[cfg(not(feature = "slow-blocks"))]
|
||||
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
|
||||
for t in transactions {
|
||||
@ -583,10 +587,17 @@ pub fn enact_verified(
|
||||
mod tests {
|
||||
use tests::helpers::*;
|
||||
use super::*;
|
||||
use common::*;
|
||||
use engines::Engine;
|
||||
use env_info::LastHashes;
|
||||
use error::Error;
|
||||
use header::Header;
|
||||
use factory::Factories;
|
||||
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
|
||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||
|
@ -29,3 +29,12 @@ pub struct BestBlock {
|
||||
/// Best block uncompressed bytes
|
||||
pub block: Bytes,
|
||||
}
|
||||
|
||||
/// Best ancient block info. If the blockchain has a gap this keeps track of where it starts.
|
||||
#[derive(Default)]
|
||||
pub struct BestAncientBlock {
|
||||
/// Best block hash.
|
||||
pub hash: H256,
|
||||
/// Best block number.
|
||||
pub number: BlockNumber,
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ use log_entry::{LogEntry, LocalizedLogEntry};
|
||||
use receipt::Receipt;
|
||||
use blooms::{Bloom, BloomGroup};
|
||||
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
||||
use blockchain::best_block::BestBlock;
|
||||
use blockchain::best_block::{BestBlock, BestAncientBlock};
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::tree_route::TreeRoute;
|
||||
use blockchain::update::ExtrasUpdate;
|
||||
use blockchain::{CacheSize, ImportRoute, Config};
|
||||
@ -44,16 +45,24 @@ pub trait BlockProvider {
|
||||
/// (though not necessarily a part of the canon chain).
|
||||
fn is_known(&self, hash: &H256) -> bool;
|
||||
|
||||
/// Get the first block which this chain holds.
|
||||
/// Get the first block of the best part of the chain.
|
||||
/// Return `None` if there is no gap and the first block is the genesis.
|
||||
/// Any queries of blocks which precede this one are not guaranteed to
|
||||
/// succeed.
|
||||
fn first_block(&self) -> H256;
|
||||
fn first_block(&self) -> Option<H256>;
|
||||
|
||||
/// Get the number of the first block.
|
||||
fn first_block_number(&self) -> BlockNumber {
|
||||
self.block_number(&self.first_block()).expect("First block always stored; qed")
|
||||
fn first_block_number(&self) -> Option<BlockNumber> {
|
||||
self.first_block().map(|b| self.block_number(&b).expect("First block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||
}
|
||||
|
||||
/// Get the best block of an first block sequence if there is a gap.
|
||||
fn best_ancient_block(&self) -> Option<H256>;
|
||||
|
||||
/// Get the number of the first block.
|
||||
fn best_ancient_number(&self) -> Option<BlockNumber> {
|
||||
self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||
}
|
||||
/// Get raw block data
|
||||
fn block(&self, hash: &H256) -> Option<Bytes>;
|
||||
|
||||
@ -124,7 +133,8 @@ pub trait BlockProvider {
|
||||
|
||||
/// Returns the header of the genesis block.
|
||||
fn genesis_header(&self) -> Header {
|
||||
self.block_header(&self.genesis_hash()).unwrap()
|
||||
self.block_header(&self.genesis_hash())
|
||||
.expect("Genesis header always stored; qed")
|
||||
}
|
||||
|
||||
/// Returns numbers of blocks containing given bloom.
|
||||
@ -161,9 +171,14 @@ impl bc::group::BloomGroupDatabase for BlockChain {
|
||||
pub struct BlockChain {
|
||||
// All locks must be captured in the order declared here.
|
||||
blooms_config: bc::Config,
|
||||
first_block: H256,
|
||||
|
||||
best_block: RwLock<BestBlock>,
|
||||
// Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps.
|
||||
// Only updated with `insert_unordered_block`.
|
||||
best_ancient_block: RwLock<Option<BestAncientBlock>>,
|
||||
// Stores the last block of the last sequence of blocks. `None` if there are no gaps.
|
||||
// This is calculated on start and does not get updated.
|
||||
first_block: Option<H256>,
|
||||
|
||||
// block cache
|
||||
block_headers: RwLock<HashMap<H256, Bytes>>,
|
||||
@ -182,6 +197,7 @@ pub struct BlockChain {
|
||||
|
||||
pending_best_block: RwLock<Option<BestBlock>>,
|
||||
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
||||
pending_block_details: RwLock<HashMap<H256, BlockDetails>>,
|
||||
pending_transaction_addresses: RwLock<HashMap<H256, Option<TransactionAddress>>>,
|
||||
|
||||
engine: Arc<Engine>,
|
||||
@ -194,8 +210,16 @@ impl BlockProvider for BlockChain {
|
||||
self.db.exists_with_cache(db::COL_EXTRA, &self.block_details, hash)
|
||||
}
|
||||
|
||||
fn first_block(&self) -> H256 {
|
||||
self.first_block
|
||||
fn first_block(&self) -> Option<H256> {
|
||||
self.first_block.clone()
|
||||
}
|
||||
|
||||
fn best_ancient_block(&self) -> Option<H256> {
|
||||
self.best_ancient_block.read().as_ref().map(|b| b.hash.clone())
|
||||
}
|
||||
|
||||
fn best_ancient_number(&self) -> Option<BlockNumber> {
|
||||
self.best_ancient_block.read().as_ref().map(|b| b.number)
|
||||
}
|
||||
|
||||
/// Get raw block data
|
||||
@ -335,7 +359,10 @@ impl BlockProvider for BlockChain {
|
||||
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
|
||||
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
||||
assert_eq!(receipts.len(), hashes.len());
|
||||
if receipts.len() != hashes.len() {
|
||||
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
|
||||
assert!(false);
|
||||
}
|
||||
log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
|
||||
|
||||
let receipts_len = receipts.len();
|
||||
@ -371,6 +398,8 @@ impl BlockProvider for BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator which walks the blockchain towards the genesis.
|
||||
#[derive(Clone)]
|
||||
pub struct AncestryIter<'a> {
|
||||
current: H256,
|
||||
chain: &'a BlockChain,
|
||||
@ -380,11 +409,10 @@ impl<'a> Iterator for AncestryIter<'a> {
|
||||
type Item = H256;
|
||||
fn next(&mut self) -> Option<H256> {
|
||||
if self.current.is_zero() {
|
||||
Option::None
|
||||
None
|
||||
} else {
|
||||
let mut n = self.chain.block_details(&self.current).unwrap().parent;
|
||||
mem::swap(&mut self.current, &mut n);
|
||||
Some(n)
|
||||
self.chain.block_details(&self.current)
|
||||
.map(|details| mem::replace(&mut self.current, details.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,8 +428,9 @@ impl BlockChain {
|
||||
levels: LOG_BLOOMS_LEVELS,
|
||||
elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX,
|
||||
},
|
||||
first_block: H256::zero(),
|
||||
first_block: None,
|
||||
best_block: RwLock::new(BestBlock::default()),
|
||||
best_ancient_block: RwLock::new(None),
|
||||
block_headers: RwLock::new(HashMap::new()),
|
||||
block_bodies: RwLock::new(HashMap::new()),
|
||||
block_details: RwLock::new(HashMap::new()),
|
||||
@ -413,6 +442,7 @@ impl BlockChain {
|
||||
cache_man: Mutex::new(cache_man),
|
||||
pending_best_block: RwLock::new(None),
|
||||
pending_block_hashes: RwLock::new(HashMap::new()),
|
||||
pending_block_details: RwLock::new(HashMap::new()),
|
||||
pending_transaction_addresses: RwLock::new(HashMap::new()),
|
||||
engine: engine,
|
||||
};
|
||||
@ -444,7 +474,6 @@ impl BlockChain {
|
||||
batch.write(db::COL_EXTRA, &header.number(), &hash);
|
||||
|
||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||
bc.db.write(batch).expect("Low level database error. Some issue with disk?");
|
||||
hash
|
||||
}
|
||||
@ -456,32 +485,45 @@ impl BlockChain {
|
||||
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
|
||||
let best_block_rlp = bc.block(&best_block_hash).unwrap();
|
||||
|
||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map_or(Vec::new(), |v| v.to_vec());
|
||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec());
|
||||
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
|
||||
let best_ancient_number;
|
||||
if best_ancient.is_none() && best_block_number > 1 && bc.block_hash(1).is_none() {
|
||||
best_ancient = Some(bc.genesis_hash());
|
||||
best_ancient_number = Some(0);
|
||||
} else {
|
||||
best_ancient_number = best_ancient.as_ref().and_then(|h| bc.block_number(h));
|
||||
}
|
||||
|
||||
// binary search for the first block.
|
||||
if raw_first.is_empty() {
|
||||
let (mut f, mut hash) = (best_block_number, best_block_hash);
|
||||
let mut l = 0;
|
||||
match raw_first {
|
||||
None => {
|
||||
let (mut f, mut hash) = (best_block_number, best_block_hash);
|
||||
let mut l = best_ancient_number.unwrap_or(0);
|
||||
|
||||
loop {
|
||||
if l >= f { break; }
|
||||
loop {
|
||||
if l >= f { break; }
|
||||
|
||||
let step = (f - l) >> 1;
|
||||
let m = l + step;
|
||||
let step = (f - l) >> 1;
|
||||
let m = l + step;
|
||||
|
||||
match bc.block_hash(m) {
|
||||
Some(h) => { f = m; hash = h },
|
||||
None => { l = m + 1 },
|
||||
match bc.block_hash(m) {
|
||||
Some(h) => { f = m; hash = h },
|
||||
None => { l = m + 1 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut batch = db.transaction();
|
||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||
db.write(batch).expect("Low level database error.");
|
||||
|
||||
bc.first_block = hash;
|
||||
} else {
|
||||
bc.first_block = H256::from_slice(&raw_first);
|
||||
if hash != bc.genesis_hash() {
|
||||
trace!("First block calculated: {:?}", hash);
|
||||
let mut batch = db.transaction();
|
||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||
db.write(batch).expect("Low level database error.");
|
||||
bc.first_block = Some(hash);
|
||||
}
|
||||
},
|
||||
Some(raw_first) => {
|
||||
bc.first_block = Some(H256::from_slice(&raw_first));
|
||||
},
|
||||
}
|
||||
|
||||
// and write them
|
||||
@ -492,6 +534,14 @@ impl BlockChain {
|
||||
hash: best_block_hash,
|
||||
block: best_block_rlp,
|
||||
};
|
||||
|
||||
if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) {
|
||||
let mut best_ancient_block = bc.best_ancient_block.write();
|
||||
*best_ancient_block = Some(BestAncientBlock {
|
||||
hash: hash,
|
||||
number: number,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bc
|
||||
@ -521,7 +571,7 @@ impl BlockChain {
|
||||
let range = extras.number as bc::Number .. extras.number as bc::Number;
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
let changes = chain.replace(&range, vec![]);
|
||||
for (k, v) in changes.into_iter() {
|
||||
for (k, v) in changes {
|
||||
batch.write(db::COL_EXTRA, &LogGroupPosition::from(k), &BloomGroup::from(v));
|
||||
}
|
||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||
@ -645,11 +695,12 @@ impl BlockChain {
|
||||
/// Inserts a verified, known block from the canonical chain.
|
||||
///
|
||||
/// Can be performed out-of-order, but care must be taken that the final chain is in a correct state.
|
||||
/// This is used by snapshot restoration.
|
||||
///
|
||||
/// This is used by snapshot restoration and when downloading missing blocks for the chain gap.
|
||||
/// `is_best` forces the best block to be updated to this block.
|
||||
/// `is_ancient` forces the best block of the first block sequence to be updated to this block.
|
||||
/// Supply a dummy parent total difficulty when the parent block may not be in the chain.
|
||||
/// Returns true if the block is disconnected.
|
||||
pub fn insert_snapshot_block(&self, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool) -> bool {
|
||||
pub fn insert_unordered_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool, is_ancient: bool) -> bool {
|
||||
let block = BlockView::new(bytes);
|
||||
let header = block.header_view();
|
||||
let hash = header.sha3();
|
||||
@ -660,8 +711,6 @@ impl BlockChain {
|
||||
|
||||
assert!(self.pending_best_block.read().is_none());
|
||||
|
||||
let mut batch = self.db.transaction();
|
||||
|
||||
let block_rlp = UntrustedRlp::new(bytes);
|
||||
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks);
|
||||
let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks);
|
||||
@ -675,13 +724,13 @@ impl BlockChain {
|
||||
if let Some(parent_details) = maybe_parent {
|
||||
// parent known to be in chain.
|
||||
let info = BlockInfo {
|
||||
hash: hash,
|
||||
hash: hash.clone(),
|
||||
number: header.number(),
|
||||
total_difficulty: parent_details.total_difficulty + header.difficulty(),
|
||||
location: BlockLocation::CanonChain,
|
||||
};
|
||||
|
||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
||||
self.prepare_update(batch, ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
@ -690,7 +739,21 @@ impl BlockChain {
|
||||
info: info,
|
||||
block: bytes
|
||||
}, is_best);
|
||||
self.db.write(batch).unwrap();
|
||||
|
||||
if is_ancient {
|
||||
let mut best_ancient_block = self.best_ancient_block.write();
|
||||
let ancient_number = best_ancient_block.as_ref().map_or(0, |b| b.number);
|
||||
if self.block_hash(header.number() + 1).is_some() {
|
||||
batch.delete(db::COL_EXTRA, b"ancient");
|
||||
*best_ancient_block = None;
|
||||
} else if header.number() > ancient_number {
|
||||
batch.put(db::COL_EXTRA, b"ancient", &hash);
|
||||
*best_ancient_block = Some(BestAncientBlock {
|
||||
hash: hash,
|
||||
number: header.number(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
} else {
|
||||
@ -715,7 +778,7 @@ impl BlockChain {
|
||||
let mut update = HashMap::new();
|
||||
update.insert(hash, block_details);
|
||||
|
||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
||||
self.prepare_update(batch, ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: update,
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
@ -724,8 +787,6 @@ impl BlockChain {
|
||||
info: info,
|
||||
block: bytes,
|
||||
}, is_best);
|
||||
self.db.write(batch).unwrap();
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
@ -734,11 +795,10 @@ impl BlockChain {
|
||||
/// the chain and the child's parent is this block.
|
||||
///
|
||||
/// Used in snapshots to glue the chunks together at the end.
|
||||
pub fn add_child(&self, block_hash: H256, child_hash: H256) {
|
||||
pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) {
|
||||
let mut parent_details = self.block_details(&block_hash)
|
||||
.unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash));
|
||||
|
||||
let mut batch = self.db.transaction();
|
||||
parent_details.children.push(child_hash);
|
||||
|
||||
let mut update = HashMap::new();
|
||||
@ -749,8 +809,6 @@ impl BlockChain {
|
||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite);
|
||||
|
||||
self.cache_man.lock().note_used(CacheID::BlockDetails(block_hash));
|
||||
|
||||
self.db.write(batch).unwrap();
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(similar_names))]
|
||||
@ -838,17 +896,6 @@ impl BlockChain {
|
||||
|
||||
/// Prepares extras update.
|
||||
fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
||||
{
|
||||
let block_hashes: Vec<_> = update.block_details.keys().cloned().collect();
|
||||
|
||||
let mut write_details = self.block_details.write();
|
||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
|
||||
|
||||
let mut cache_man = self.cache_man.lock();
|
||||
for hash in block_hashes {
|
||||
cache_man.note_used(CacheID::BlockDetails(hash));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut write_receipts = self.block_receipts.write();
|
||||
@ -860,7 +907,7 @@ impl BlockChain {
|
||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_blocks_blooms, update.blocks_blooms, CacheUpdatePolicy::Remove);
|
||||
}
|
||||
|
||||
// These cached values must be updated last with all three locks taken to avoid
|
||||
// These cached values must be updated last with all four locks taken to avoid
|
||||
// cache decoherence
|
||||
{
|
||||
let mut best_block = self.pending_best_block.write();
|
||||
@ -878,8 +925,10 @@ impl BlockChain {
|
||||
},
|
||||
}
|
||||
let mut write_hashes = self.pending_block_hashes.write();
|
||||
let mut write_details = self.pending_block_details.write();
|
||||
let mut write_txs = self.pending_transaction_addresses.write();
|
||||
|
||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
|
||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
|
||||
batch.extend_with_option_cache(db::COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
|
||||
}
|
||||
@ -889,9 +938,11 @@ impl BlockChain {
|
||||
pub fn commit(&self) {
|
||||
let mut pending_best_block = self.pending_best_block.write();
|
||||
let mut pending_write_hashes = self.pending_block_hashes.write();
|
||||
let mut pending_block_details = self.pending_block_details.write();
|
||||
let mut pending_write_txs = self.pending_transaction_addresses.write();
|
||||
|
||||
let mut best_block = self.best_block.write();
|
||||
let mut write_block_details = self.block_details.write();
|
||||
let mut write_hashes = self.block_hashes.write();
|
||||
let mut write_txs = self.transaction_addresses.write();
|
||||
// update best block
|
||||
@ -904,9 +955,11 @@ impl BlockChain {
|
||||
|
||||
let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect();
|
||||
let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect();
|
||||
let pending_block_hashes: Vec<_> = pending_block_details.keys().cloned().collect();
|
||||
|
||||
write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new()));
|
||||
write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed"))));
|
||||
write_block_details.extend(mem::replace(&mut *pending_block_details, HashMap::new()));
|
||||
|
||||
for hash in retracted_txs.keys() {
|
||||
write_txs.remove(hash);
|
||||
@ -920,6 +973,10 @@ impl BlockChain {
|
||||
for hash in enacted_txs_keys {
|
||||
cache_man.note_used(CacheID::TransactionAddresses(hash));
|
||||
}
|
||||
|
||||
for hash in pending_block_hashes {
|
||||
cache_man.note_used(CacheID::BlockDetails(hash));
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
|
||||
@ -944,17 +1001,29 @@ impl BlockChain {
|
||||
if !self.is_known(parent) { return None; }
|
||||
|
||||
let mut excluded = HashSet::new();
|
||||
for a in self.ancestry_iter(parent.clone()).unwrap().take(uncle_generations) {
|
||||
excluded.extend(self.uncle_hashes(&a).unwrap().into_iter());
|
||||
excluded.insert(a);
|
||||
let ancestry = match self.ancestry_iter(parent.clone()) {
|
||||
Some(iter) => iter,
|
||||
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();
|
||||
for a in self.ancestry_iter(parent.clone()).unwrap().skip(1).take(uncle_generations) {
|
||||
ret.extend(self.block_details(&a).unwrap().children.iter()
|
||||
.filter(|h| !excluded.contains(h))
|
||||
);
|
||||
for a in ancestry.skip(1).take(uncle_generations) {
|
||||
if let Some(details) = self.block_details(&a) {
|
||||
ret.extend(details.children.iter().filter(|h| !excluded.contains(h)))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
@ -1210,6 +1279,29 @@ impl BlockChain {
|
||||
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
||||
body.out()
|
||||
}
|
||||
|
||||
/// Returns general blockchain information
|
||||
pub fn chain_info(&self) -> BlockChainInfo {
|
||||
// ensure data consistencly by locking everything first
|
||||
let best_block = self.best_block.read();
|
||||
let best_ancient_block = self.best_ancient_block.read();
|
||||
BlockChainInfo {
|
||||
total_difficulty: best_block.total_difficulty.clone(),
|
||||
pending_total_difficulty: best_block.total_difficulty.clone(),
|
||||
genesis_hash: self.genesis_hash(),
|
||||
best_block_hash: best_block.hash.clone(),
|
||||
best_block_number: best_block.number,
|
||||
first_block_hash: self.first_block(),
|
||||
first_block_number: From::from(self.first_block_number()),
|
||||
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()),
|
||||
ancient_block_number: best_ancient_block.as_ref().map(|b| b.number),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn db(&self) -> &Arc<Database> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -55,18 +55,23 @@ impl<T> CacheManager<T> where T: Eq + Hash {
|
||||
}
|
||||
|
||||
for _ in 0..COLLECTION_QUEUE_SIZE {
|
||||
let current_size = notify_unused(self.cache_usage.pop_back().unwrap());
|
||||
self.cache_usage.push_front(Default::default());
|
||||
if current_size < self.max_cache_size {
|
||||
break;
|
||||
if let Some(back) = self.cache_usage.pop_back() {
|
||||
let current_size = notify_unused(back);
|
||||
self.cache_usage.push_front(Default::default());
|
||||
if current_size < self.max_cache_size {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_cache_if_needed(&mut self) {
|
||||
if self.cache_usage.is_empty() { return }
|
||||
|
||||
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
|
||||
let cache = self.cache_usage.pop_back().unwrap();
|
||||
self.cache_usage.push_front(cache);
|
||||
if let Some(cache) = self.cache_usage.pop_back() {
|
||||
self.cache_usage.push_front(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ use util::kvdb::*;
|
||||
// other
|
||||
use ethkey::recover;
|
||||
use io::*;
|
||||
use views::{HeaderView, BodyView};
|
||||
use views::{HeaderView, BodyView, BlockView};
|
||||
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
|
||||
use header::BlockNumber;
|
||||
use state::State;
|
||||
@ -47,8 +47,9 @@ use block::*;
|
||||
use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
||||
use blockchain::extras::TransactionAddress;
|
||||
use types::filter::Filter;
|
||||
use types::mode::Mode as IpcMode;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use verification::queue::{BlockQueue, QueueInfo as BlockQueueInfo};
|
||||
use verification::queue::BlockQueue;
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||
use client::{
|
||||
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
|
||||
@ -62,20 +63,23 @@ use receipt::LocalizedReceipt;
|
||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||
use trace;
|
||||
use trace::FlatTransactionTraces;
|
||||
use evm::Factory as EvmFactory;
|
||||
use evm::{Factory as EvmFactory, Schedule};
|
||||
use miner::{Miner, MinerService};
|
||||
use snapshot::{self, io as snapshot_io};
|
||||
use factory::Factories;
|
||||
use rlp::{View, UntrustedRlp};
|
||||
use state_db::StateDB;
|
||||
use rand::OsRng;
|
||||
|
||||
// re-export
|
||||
pub use types::blockchain_info::BlockChainInfo;
|
||||
pub use types::block_status::BlockStatus;
|
||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||
pub use verification::queue::QueueInfo as BlockQueueInfo;
|
||||
|
||||
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
||||
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
||||
const MIN_HISTORY_SIZE: u64 = 8;
|
||||
|
||||
impl fmt::Display for BlockChainInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@ -122,7 +126,7 @@ impl SleepState {
|
||||
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
||||
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||
pub struct Client {
|
||||
mode: Mode,
|
||||
mode: Mutex<Mode>,
|
||||
chain: RwLock<Arc<BlockChain>>,
|
||||
tracedb: RwLock<TraceDB<BlockChain>>,
|
||||
engine: Arc<Engine>,
|
||||
@ -138,17 +142,15 @@ pub struct Client {
|
||||
miner: Arc<Miner>,
|
||||
sleep_state: Mutex<SleepState>,
|
||||
liveness: AtomicBool,
|
||||
io_channel: IoChannel<ClientIoMessage>,
|
||||
io_channel: Mutex<IoChannel<ClientIoMessage>>,
|
||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||
queue_transactions: AtomicUsize,
|
||||
last_hashes: RwLock<VecDeque<H256>>,
|
||||
factories: Factories,
|
||||
history: u64,
|
||||
rng: Mutex<OsRng>,
|
||||
}
|
||||
|
||||
/// The pruning constant -- how old blocks must be before we
|
||||
/// assume finality of a given candidate.
|
||||
pub const HISTORY: u64 = 1200;
|
||||
|
||||
impl Client {
|
||||
/// Create a new client with given spec and DB path and custom verifier.
|
||||
pub fn new(
|
||||
@ -172,27 +174,49 @@ impl Client {
|
||||
};
|
||||
|
||||
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
||||
let mut state_db = StateDB::new(journal_db);
|
||||
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
|
||||
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
try!(state_db.commit(&mut batch, 0, &spec.genesis_header().hash(), None));
|
||||
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
}
|
||||
|
||||
trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());
|
||||
|
||||
let history = if config.history < MIN_HISTORY_SIZE {
|
||||
info!(target: "client", "Ignoring pruning history parameter of {}\
|
||||
, falling back to minimum of {}",
|
||||
config.history, MIN_HISTORY_SIZE);
|
||||
MIN_HISTORY_SIZE
|
||||
} else {
|
||||
config.history
|
||||
};
|
||||
|
||||
if let (Some(earliest), Some(latest)) = (state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()) {
|
||||
if latest > earliest && latest - earliest > history {
|
||||
for era in earliest..(latest - history + 1) {
|
||||
trace!("Removing era {}", era);
|
||||
let mut batch = DBTransaction::new(&db);
|
||||
try!(state_db.mark_canonical(&mut batch, era, &chain.block_hash(era).expect("Old block not found in the database")));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
||||
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
||||
}
|
||||
|
||||
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();
|
||||
panic_handler.forward_from(&block_queue);
|
||||
|
||||
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
|
||||
|
||||
let factories = Factories {
|
||||
vm: EvmFactory::new(config.vm_type.clone()),
|
||||
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||
trie: TrieFactory::new(trie_spec),
|
||||
accountdb: Default::default(),
|
||||
};
|
||||
@ -200,7 +224,7 @@ impl Client {
|
||||
let client = Client {
|
||||
sleep_state: Mutex::new(SleepState::new(awake)),
|
||||
liveness: AtomicBool::new(awake),
|
||||
mode: config.mode.clone(),
|
||||
mode: Mutex::new(config.mode.clone()),
|
||||
chain: RwLock::new(chain),
|
||||
tracedb: tracedb,
|
||||
engine: engine,
|
||||
@ -214,11 +238,13 @@ impl Client {
|
||||
import_lock: Mutex::new(()),
|
||||
panic_handler: panic_handler,
|
||||
miner: miner,
|
||||
io_channel: message_channel,
|
||||
io_channel: Mutex::new(message_channel),
|
||||
notify: RwLock::new(Vec::new()),
|
||||
queue_transactions: AtomicUsize::new(0),
|
||||
last_hashes: RwLock::new(VecDeque::new()),
|
||||
factories: factories,
|
||||
history: history,
|
||||
rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))),
|
||||
};
|
||||
Ok(Arc::new(client))
|
||||
}
|
||||
@ -277,7 +303,7 @@ impl Client {
|
||||
let chain = self.chain.read();
|
||||
// Check the block isn't so old we won't be able to enact it.
|
||||
let best_block_number = chain.best_block_number();
|
||||
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
||||
if best_block_number >= self.history && header.number() <= best_block_number - self.history {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
||||
return Err(());
|
||||
}
|
||||
@ -294,7 +320,7 @@ impl Client {
|
||||
if let Some(parent) = chain_has_parent {
|
||||
// Enact Verified Block
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
||||
let db = self.state_db.lock().boxed_clone_canon(&header.parent_hash());
|
||||
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||
let locked_block = try!(enact_result.map_err(|e| {
|
||||
@ -342,16 +368,19 @@ impl Client {
|
||||
|
||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||
pub fn import_verified_blocks(&self) -> usize {
|
||||
let max_blocks_to_import = 64;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, duration) = {
|
||||
let max_blocks_to_import = 4;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, duration, is_empty) = {
|
||||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||
let mut invalid_blocks = HashSet::new();
|
||||
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
||||
|
||||
let _import_lock = self.import_lock.lock();
|
||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||
if blocks.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let _timer = PerfTimer::new("import_verified_blocks");
|
||||
let start = precise_time_ns();
|
||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||
|
||||
for block in blocks {
|
||||
let header = &block.header;
|
||||
@ -375,23 +404,19 @@ impl Client {
|
||||
let imported = imported_blocks.len();
|
||||
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
||||
|
||||
{
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
if !imported_blocks.is_empty() {
|
||||
self.block_queue.mark_as_good(&imported_blocks);
|
||||
}
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
|
||||
let duration_ns = precise_time_ns() - start;
|
||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns)
|
||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns, is_empty)
|
||||
};
|
||||
|
||||
{
|
||||
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||
if !imported_blocks.is_empty() && is_empty {
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
|
||||
|
||||
if self.queue_info().is_empty() {
|
||||
if is_empty {
|
||||
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||
}
|
||||
|
||||
@ -412,17 +437,45 @@ impl Client {
|
||||
imported
|
||||
}
|
||||
|
||||
/// Import a block with transaction receipts.
|
||||
/// The block is guaranteed to be the next best blocks in the first block sequence.
|
||||
/// Does no sealing or transaction validation.
|
||||
fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, ::error::Error> {
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header();
|
||||
let hash = header.hash();
|
||||
let _import_lock = self.import_lock.lock();
|
||||
{
|
||||
let _timer = PerfTimer::new("import_old_block");
|
||||
let mut rng = self.rng.lock();
|
||||
let chain = self.chain.read();
|
||||
|
||||
// verify block.
|
||||
try!(::snapshot::verify_old_block(
|
||||
&mut *rng,
|
||||
&header,
|
||||
&*self.engine,
|
||||
&*chain,
|
||||
Some(&block_bytes),
|
||||
false,
|
||||
));
|
||||
|
||||
// Commit results
|
||||
let receipts = ::rlp::decode(&receipts_bytes);
|
||||
let mut batch = DBTransaction::new(&self.db.read());
|
||||
chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true);
|
||||
// Final commit to the DB
|
||||
self.db.read().write_buffered(batch);
|
||||
chain.commit();
|
||||
}
|
||||
self.db.read().flush().expect("DB flush failed.");
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
||||
let number = block.header().number();
|
||||
let parent = block.header().parent_hash().clone();
|
||||
let chain = self.chain.read();
|
||||
// Are we committing an era?
|
||||
let ancient = if number >= HISTORY {
|
||||
let n = number - HISTORY;
|
||||
Some((n, chain.block_hash(n).expect("only verified blocks can be commited; verified block has hash; qed")))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Commit results
|
||||
let receipts = block.receipts().to_owned();
|
||||
@ -438,7 +491,17 @@ impl Client {
|
||||
// already-imported block of the same number.
|
||||
// TODO: Prove it with a test.
|
||||
let mut state = block.drain();
|
||||
state.commit(&mut batch, number, hash, ancient).expect("DB commit failed.");
|
||||
|
||||
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
|
||||
|
||||
if number >= self.history {
|
||||
let n = number - self.history;
|
||||
if let Some(ancient_hash) = chain.block_hash(n) {
|
||||
state.mark_canonical(&mut batch, n, &ancient_hash).expect("DB commit failed");
|
||||
} else {
|
||||
debug!(target: "client", "Missing expected hash for block {}", n);
|
||||
}
|
||||
}
|
||||
|
||||
let route = chain.insert_block(&mut batch, block_data, receipts);
|
||||
self.tracedb.read().import(&mut batch, TraceImportRequest {
|
||||
@ -448,6 +511,7 @@ impl Client {
|
||||
enacted: route.enacted.clone(),
|
||||
retracted: route.retracted.len()
|
||||
});
|
||||
|
||||
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
|
||||
state.sync_cache(&route.enacted, &route.retracted, is_canon);
|
||||
// Final commit to the DB
|
||||
@ -497,7 +561,7 @@ impl Client {
|
||||
let db = self.state_db.lock().boxed_clone();
|
||||
|
||||
// early exit for pruned blocks
|
||||
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + HISTORY {
|
||||
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + self.history {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -553,7 +617,8 @@ impl Client {
|
||||
self.block_queue.collect_garbage();
|
||||
self.tracedb.read().collect_garbage();
|
||||
|
||||
match self.mode {
|
||||
let mode = self.mode.lock().clone();
|
||||
match mode {
|
||||
Mode::Dark(timeout) => {
|
||||
let mut ss = self.sleep_state.lock();
|
||||
if let Some(t) = ss.last_activity {
|
||||
@ -602,20 +667,23 @@ impl Client {
|
||||
let best_block_number = self.chain_info().best_block_number;
|
||||
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
||||
|
||||
if best_block_number > HISTORY + block_number && db.is_pruned() {
|
||||
if best_block_number > self.history + block_number && db.is_pruned() {
|
||||
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
||||
}
|
||||
|
||||
let history = ::std::cmp::min(self.history, 1000);
|
||||
|
||||
let start_hash = match at {
|
||||
BlockID::Latest => {
|
||||
let start_num = if best_block_number > 1000 {
|
||||
best_block_number - 1000
|
||||
} else {
|
||||
0
|
||||
let start_num = match db.earliest_era() {
|
||||
Some(era) => ::std::cmp::max(era, best_block_number - history),
|
||||
None => best_block_number - history,
|
||||
};
|
||||
|
||||
self.block_hash(BlockID::Number(start_num))
|
||||
.expect("blocks within HISTORY are always stored.")
|
||||
match self.block_hash(BlockID::Number(start_num)) {
|
||||
Some(h) => h,
|
||||
None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
|
||||
}
|
||||
}
|
||||
_ => match self.block_hash(at) {
|
||||
Some(hash) => hash,
|
||||
@ -628,6 +696,11 @@ impl Client {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ask the client what the history parameter is.
|
||||
pub fn pruning_history(&self) -> u64 {
|
||||
self.history
|
||||
}
|
||||
|
||||
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
|
||||
match id {
|
||||
BlockID::Hash(hash) => Some(hash),
|
||||
@ -684,7 +757,8 @@ impl snapshot::DatabaseRestore for Client {
|
||||
let db = self.db.write();
|
||||
try!(db.restore(new_db));
|
||||
|
||||
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE));
|
||||
let cache_size = state_db.cache_size();
|
||||
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
|
||||
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone(), self.engine.clone()));
|
||||
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
|
||||
Ok(())
|
||||
@ -696,7 +770,7 @@ impl BlockChainClient for Client {
|
||||
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
|
||||
let view = HeaderView::new(&header);
|
||||
let last_hashes = self.build_last_hashes(view.hash());
|
||||
let last_hashes = self.build_last_hashes(view.parent_hash());
|
||||
let env_info = EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
@ -768,12 +842,24 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn keep_alive(&self) {
|
||||
if self.mode != Mode::Active {
|
||||
let mode = self.mode.lock().clone();
|
||||
if mode != Mode::Active {
|
||||
self.wake_up();
|
||||
(*self.sleep_state.lock()).last_activity = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
fn mode(&self) -> IpcMode { self.mode.lock().clone().into() }
|
||||
|
||||
fn set_mode(&self, mode: IpcMode) {
|
||||
*self.mode.lock() = mode.clone().into();
|
||||
match mode {
|
||||
IpcMode::Active => self.wake_up(),
|
||||
IpcMode::Off => self.sleep(),
|
||||
_ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); }
|
||||
}
|
||||
}
|
||||
|
||||
fn best_block_header(&self) -> Bytes {
|
||||
self.chain.read().best_block_header()
|
||||
}
|
||||
@ -970,6 +1056,20 @@ impl BlockChainClient for Client {
|
||||
Ok(try!(self.block_queue.import(unverified)))
|
||||
}
|
||||
|
||||
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
{
|
||||
// check block order
|
||||
let header = BlockView::new(&block_bytes).header_view();
|
||||
if self.chain.read().is_known(&header.hash()) {
|
||||
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
|
||||
}
|
||||
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
||||
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
|
||||
}
|
||||
}
|
||||
self.import_old_block(block_bytes, receipts_bytes).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn queue_info(&self) -> BlockQueueInfo {
|
||||
self.block_queue.queue_info()
|
||||
}
|
||||
@ -979,14 +1079,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo {
|
||||
let chain = self.chain.read();
|
||||
BlockChainInfo {
|
||||
total_difficulty: chain.best_block_total_difficulty(),
|
||||
pending_total_difficulty: chain.best_block_total_difficulty(),
|
||||
genesis_hash: chain.genesis_hash(),
|
||||
best_block_hash: chain.best_block_hash(),
|
||||
best_block_number: From::from(chain.best_block_number())
|
||||
}
|
||||
self.chain.read().chain_info()
|
||||
}
|
||||
|
||||
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||
@ -1062,7 +1155,7 @@ impl BlockChainClient for Client {
|
||||
debug!("Ignoring {} transactions: queue is full", transactions.len());
|
||||
} else {
|
||||
let len = transactions.len();
|
||||
match self.io_channel.send(ClientIoMessage::NewTransactions(transactions)) {
|
||||
match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions)) {
|
||||
Ok(_) => {
|
||||
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
|
||||
}
|
||||
@ -1094,6 +1187,23 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for Client {
|
||||
|
||||
fn latest_schedule(&self) -> Schedule {
|
||||
let header_data = self.best_block_header();
|
||||
let view = HeaderView::new(&header_data);
|
||||
|
||||
let env_info = EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
timestamp: view.timestamp(),
|
||||
difficulty: view.difficulty(),
|
||||
last_hashes: self.build_last_hashes(view.hash()),
|
||||
gas_used: U256::default(),
|
||||
gas_limit: view.gas_limit(),
|
||||
};
|
||||
self.engine.schedule(&env_info)
|
||||
}
|
||||
|
||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
||||
let engine = &*self.engine;
|
||||
let chain = self.chain.read();
|
||||
@ -1133,21 +1243,22 @@ impl MiningBlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
||||
let _import_lock = self.import_lock.lock();
|
||||
let _timer = PerfTimer::new("import_sealed_block");
|
||||
let start = precise_time_ns();
|
||||
|
||||
let h = block.header().hash();
|
||||
let number = block.header().number();
|
||||
|
||||
let block_data = block.rlp_bytes();
|
||||
let route = self.commit_block(block, &h, &block_data);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
||||
let start = precise_time_ns();
|
||||
let route = {
|
||||
// scope for self.import_lock
|
||||
let _import_lock = self.import_lock.lock();
|
||||
let _timer = PerfTimer::new("import_sealed_block");
|
||||
|
||||
let number = block.header().number();
|
||||
let block_data = block.rlp_bytes();
|
||||
let route = self.commit_block(block, &h, &block_data);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
||||
route
|
||||
};
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
||||
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
||||
|
||||
self.notify(|notify| {
|
||||
notify.new_blocks(
|
||||
vec![h.clone()],
|
||||
@ -1168,3 +1279,33 @@ impl MayPanic for Client {
|
||||
self.panic_handler.on_panic(closure);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_not_cache_details_before_commit() {
|
||||
use tests::helpers::*;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
let client = generate_dummy_client(0);
|
||||
let genesis = client.chain_info().best_block_hash;
|
||||
let (new_hash, new_block) = get_good_dummy_block_hash();
|
||||
|
||||
let go = {
|
||||
// Separate thread uncommited transaction
|
||||
let go = Arc::new(AtomicBool::new(false));
|
||||
let go_thread = go.clone();
|
||||
let another_client = client.reference().clone();
|
||||
thread::spawn(move || {
|
||||
let mut batch = DBTransaction::new(&*another_client.chain.read().db().clone());
|
||||
another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new());
|
||||
go_thread.store(true, Ordering::SeqCst);
|
||||
});
|
||||
go
|
||||
};
|
||||
|
||||
while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); }
|
||||
|
||||
assert!(client.tree_route(&genesis, &new_hash).is_none());
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::path::Path;
|
||||
pub use std::time::Duration;
|
||||
pub use blockchain::Config as BlockChainConfig;
|
||||
pub use trace::Config as TraceConfig;
|
||||
@ -26,23 +27,26 @@ use util::{journaldb, CompactionProfile};
|
||||
/// Client state db compaction profile
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DatabaseCompactionProfile {
|
||||
/// Default compaction profile
|
||||
Default,
|
||||
/// Try to determine compaction profile automatically
|
||||
Auto,
|
||||
/// SSD compaction profile
|
||||
SSD,
|
||||
/// HDD or other slow storage io compaction profile
|
||||
HDD,
|
||||
}
|
||||
|
||||
impl Default for DatabaseCompactionProfile {
|
||||
fn default() -> Self {
|
||||
DatabaseCompactionProfile::Default
|
||||
DatabaseCompactionProfile::Auto
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseCompactionProfile {
|
||||
/// Returns corresponding compaction profile.
|
||||
pub fn compaction_profile(&self) -> CompactionProfile {
|
||||
pub fn compaction_profile(&self, db_path: &Path) -> CompactionProfile {
|
||||
match *self {
|
||||
DatabaseCompactionProfile::Default => Default::default(),
|
||||
DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path),
|
||||
DatabaseCompactionProfile::SSD => CompactionProfile::ssd(),
|
||||
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||
}
|
||||
}
|
||||
@ -53,9 +57,10 @@ impl FromStr for DatabaseCompactionProfile {
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"ssd" | "default" => Ok(DatabaseCompactionProfile::Default),
|
||||
"auto" => Ok(DatabaseCompactionProfile::Auto),
|
||||
"ssd" => Ok(DatabaseCompactionProfile::SSD),
|
||||
"hdd" => Ok(DatabaseCompactionProfile::HDD),
|
||||
_ => Err("Invalid compaction profile given. Expected hdd/ssd (default).".into()),
|
||||
_ => Err("Invalid compaction profile given. Expected default/hdd/ssd.".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,6 +76,8 @@ pub enum Mode {
|
||||
/// Goes offline after RLP is inactive for some (given) time and
|
||||
/// stays inactive.
|
||||
Dark(Duration),
|
||||
/// Always off.
|
||||
Off,
|
||||
}
|
||||
|
||||
impl Default for Mode {
|
||||
@ -96,7 +103,7 @@ pub struct ClientConfig {
|
||||
pub pruning: journaldb::Algorithm,
|
||||
/// The name of the client instance.
|
||||
pub name: String,
|
||||
/// State db cache-size if not default
|
||||
/// RocksDB state column cache-size if not default
|
||||
pub db_cache_size: Option<usize>,
|
||||
/// State db compaction profile
|
||||
pub db_compaction: DatabaseCompactionProfile,
|
||||
@ -106,6 +113,14 @@ pub struct ClientConfig {
|
||||
pub mode: Mode,
|
||||
/// Type of block verifier used by client.
|
||||
pub verifier_type: VerifierType,
|
||||
/// State db cache-size.
|
||||
pub state_cache_size: usize,
|
||||
/// EVM jump-tables cache size.
|
||||
pub jump_table_size: usize,
|
||||
/// State pruning history size.
|
||||
pub history: u64,
|
||||
/// Check seal valididity on block import
|
||||
pub check_seal: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -114,13 +129,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_default_compaction_profile() {
|
||||
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Default);
|
||||
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Auto);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsing_compaction_profile() {
|
||||
assert_eq!(DatabaseCompactionProfile::Default, "ssd".parse().unwrap());
|
||||
assert_eq!(DatabaseCompactionProfile::Default, "default".parse().unwrap());
|
||||
assert_eq!(DatabaseCompactionProfile::Auto, "auto".parse().unwrap());
|
||||
assert_eq!(DatabaseCompactionProfile::SSD, "ssd".parse().unwrap());
|
||||
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ pub use block_import_error::BlockImportError;
|
||||
pub use transaction_import::TransactionImportResult;
|
||||
pub use transaction_import::TransactionImportError;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
|
||||
pub use verification::VerifierType;
|
||||
|
||||
/// IPC interfaces
|
||||
#[cfg(feature="ipc")]
|
||||
|
@ -34,9 +34,10 @@ use log_entry::LocalizedLogEntry;
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use blockchain::extras::BlockReceipts;
|
||||
use error::{ImportResult};
|
||||
use evm::{Factory as EvmFactory, VMType};
|
||||
use evm::{Factory as EvmFactory, VMType, Schedule};
|
||||
use miner::{Miner, MinerService, TransactionImportResult};
|
||||
use spec::Spec;
|
||||
use types::mode::Mode;
|
||||
|
||||
use verification::queue::QueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
@ -83,6 +84,10 @@ pub struct TestBlockChainClient {
|
||||
pub vm_factory: EvmFactory,
|
||||
/// Timestamp assigned to latest sealed block
|
||||
pub latest_block_timestamp: RwLock<u64>,
|
||||
/// Ancient block info.
|
||||
pub ancient_block: RwLock<Option<(H256, u64)>>,
|
||||
/// First block info.
|
||||
pub first_block: RwLock<Option<(H256, u64)>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -130,8 +135,10 @@ impl TestBlockChainClient {
|
||||
queue_size: AtomicUsize::new(0),
|
||||
miner: Arc::new(Miner::with_spec(&spec)),
|
||||
spec: spec,
|
||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
||||
vm_factory: EvmFactory::new(VMType::Interpreter, 1024 * 1024),
|
||||
latest_block_timestamp: RwLock::new(10_000_000),
|
||||
ancient_block: RwLock::new(None),
|
||||
first_block: RwLock::new(None),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
@ -298,7 +305,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||
let temp = RandomTempPath::new();
|
||||
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
|
||||
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
||||
let state_db = StateDB::new(journal_db);
|
||||
let state_db = StateDB::new(journal_db, 1024 * 1024);
|
||||
GuardedTempResult {
|
||||
_temp: temp,
|
||||
result: Some(state_db)
|
||||
@ -306,6 +313,10 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for TestBlockChainClient {
|
||||
fn latest_schedule(&self) -> Schedule {
|
||||
Schedule::new_homestead_gas_fix()
|
||||
}
|
||||
|
||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
||||
let engine = &*self.spec.engine;
|
||||
let genesis_header = self.spec.genesis_header();
|
||||
@ -560,6 +571,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
Ok(h)
|
||||
}
|
||||
|
||||
fn import_block_with_receipts(&self, b: Bytes, _r: Bytes) -> Result<H256, BlockImportError> {
|
||||
self.import_block(b)
|
||||
}
|
||||
|
||||
fn queue_info(&self) -> QueueInfo {
|
||||
QueueInfo {
|
||||
verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed),
|
||||
@ -585,6 +600,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
genesis_hash: self.genesis_hash.clone(),
|
||||
best_block_hash: self.last_hash.read().clone(),
|
||||
best_block_number: self.blocks.read().len() as BlockNumber - 1,
|
||||
first_block_hash: self.first_block.read().as_ref().map(|x| x.0),
|
||||
first_block_number: self.first_block.read().as_ref().map(|x| x.1),
|
||||
ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0),
|
||||
ancient_block_number: self.ancient_block.read().as_ref().map(|x| x.1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,4 +636,8 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Mode { Mode::Active }
|
||||
|
||||
fn set_mode(&self, _: Mode) { unimplemented!(); }
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::{U256, Address, H256, H2048, Bytes, Itertools};
|
||||
use util::stats::Histogram;
|
||||
use blockchain::TreeRoute;
|
||||
use verification::queue::QueueInfo as BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
@ -27,7 +28,7 @@ use views::{BlockView};
|
||||
use error::{ImportResult, CallError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
use evm::Factory as EvmFactory;
|
||||
use evm::{Factory as EvmFactory, Schedule};
|
||||
use types::ids::*;
|
||||
use types::trace_filter::Filter as TraceFilter;
|
||||
use executive::Executed;
|
||||
@ -37,6 +38,7 @@ use block_import_error::BlockImportError;
|
||||
use ipc::IpcConfig;
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::block_status::BlockStatus;
|
||||
use types::mode::Mode;
|
||||
|
||||
#[ipc(client_ident="RemoteClient")]
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
@ -139,6 +141,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Import a block into the blockchain.
|
||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||
|
||||
/// Import a block with transaction receipts. Does no sealing and transaction validation.
|
||||
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||
|
||||
/// Get block queue information.
|
||||
fn queue_info(&self) -> BlockQueueInfo;
|
||||
|
||||
@ -190,31 +195,44 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// list all transactions
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get the gas price distribution.
|
||||
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
|
||||
/// Sorted list of transaction gas prices from at least last sample_size blocks.
|
||||
fn gas_price_corpus(&self, sample_size: usize) -> Vec<U256> {
|
||||
let mut h = self.chain_info().best_block_hash;
|
||||
let mut corpus = Vec::new();
|
||||
for _ in 0..sample_size {
|
||||
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
break;
|
||||
while corpus.is_empty() {
|
||||
for _ in 0..sample_size {
|
||||
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
return corpus;
|
||||
}
|
||||
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();
|
||||
let n = corpus.len();
|
||||
if n > 0 {
|
||||
Ok((0..(distribution_size + 1))
|
||||
.map(|i| corpus[i * (n - 1) / distribution_size])
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
corpus
|
||||
}
|
||||
|
||||
/// Calculate median gas price from recent blocks if they have any transactions.
|
||||
fn gas_price_median(&self, sample_size: usize) -> Option<U256> {
|
||||
let corpus = self.gas_price_corpus(sample_size);
|
||||
corpus.get(corpus.len()/2).cloned()
|
||||
}
|
||||
|
||||
/// Get the gas price distribution based on recent blocks if they have any transactions.
|
||||
fn gas_price_histogram(&self, sample_size: usize, bucket_number: usize) -> Option<Histogram> {
|
||||
let raw_corpus = self.gas_price_corpus(sample_size);
|
||||
let raw_len = raw_corpus.len();
|
||||
// Throw out outliers.
|
||||
let (corpus, _) = raw_corpus.split_at(raw_len-raw_len/40);
|
||||
Histogram::new(corpus, bucket_number)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Mode;
|
||||
|
||||
fn set_mode(&self, mode: Mode);
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
@ -231,6 +249,9 @@ pub trait MiningBlockChainClient : BlockChainClient {
|
||||
|
||||
/// Import sealed block. Skips all verifications.
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
|
||||
|
||||
/// Returns latest schedule.
|
||||
fn latest_schedule(&self) -> Schedule;
|
||||
}
|
||||
|
||||
impl IpcConfig for BlockChainClient { }
|
||||
|
@ -114,7 +114,7 @@ pub trait Writable {
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values.into_iter() {
|
||||
for (key, value) in values {
|
||||
self.write(col, &key, &value);
|
||||
cache.insert(key, value);
|
||||
}
|
||||
@ -135,7 +135,7 @@ pub trait Writable {
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values.into_iter() {
|
||||
for (key, value) in values {
|
||||
match value {
|
||||
Some(ref v) => self.write(col, &key, v),
|
||||
None => self.delete(col, &key),
|
||||
@ -144,7 +144,7 @@ pub trait Writable {
|
||||
}
|
||||
},
|
||||
CacheUpdatePolicy::Remove => {
|
||||
for (key, value) in values.into_iter() {
|
||||
for (key, value) in values {
|
||||
match value {
|
||||
Some(v) => self.write(col, &key, &v),
|
||||
None => self.delete(col, &key),
|
||||
|
@ -16,14 +16,20 @@
|
||||
|
||||
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
||||
|
||||
use common::*;
|
||||
use ethkey::{recover, public_to_address};
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use builtin::Builtin;
|
||||
use spec::CommonParams;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use error::{BlockError, Error};
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
use header::Header;
|
||||
use transaction::SignedTransaction;
|
||||
|
||||
use util::*;
|
||||
|
||||
/// `BasicAuthority` params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -112,7 +118,7 @@ impl Engine for BasicAuthority {
|
||||
let header = block.header();
|
||||
let message = header.bare_hash();
|
||||
// account should be pernamently unlocked, otherwise sealing will fail
|
||||
if let Ok(signature) = ap.sign(*block.header().author(), message) {
|
||||
if let Ok(signature) = ap.sign(*block.header().author(), None, message) {
|
||||
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||
} else {
|
||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||
@ -184,10 +190,13 @@ impl Header {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use util::*;
|
||||
use block::*;
|
||||
use env_info::EnvInfo;
|
||||
use error::{BlockError, Error};
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use header::Header;
|
||||
use spec::Spec;
|
||||
|
||||
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
||||
|
@ -18,11 +18,11 @@ use std::collections::BTreeMap;
|
||||
use util::Address;
|
||||
use builtin::Builtin;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use env_info::EnvInfo;
|
||||
use block::ExecutedBlock;
|
||||
use common::Bytes;
|
||||
use util::Bytes;
|
||||
use account_provider::AccountProvider;
|
||||
|
||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||
@ -67,10 +67,11 @@ impl Engine for InstantSeal {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use util::*;
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use spec::Spec;
|
||||
use header::Header;
|
||||
use block::*;
|
||||
|
||||
#[test]
|
||||
|
@ -30,14 +30,20 @@ pub use self::tendermint::Tendermint;
|
||||
pub use self::signed_vote::SignedVote;
|
||||
pub use self::propose_collect::ProposeCollect;
|
||||
|
||||
use common::*;
|
||||
use rlp::UntrustedRlp;
|
||||
use util::*;
|
||||
use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
use builtin::Builtin;
|
||||
use env_info::EnvInfo;
|
||||
use error::Error;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use header::Header;
|
||||
use transaction::SignedTransaction;
|
||||
use ethereum::ethash;
|
||||
use blockchain::extras::BlockDetails;
|
||||
use views::HeaderView;
|
||||
|
||||
/// Voting errors.
|
||||
#[derive(Debug)]
|
||||
@ -153,10 +159,14 @@ pub trait Engine : Sync + Send {
|
||||
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
||||
/// Determine the code execution cost of the builtin contract with address `a`.
|
||||
/// Panics if `is_builtin(a)` is not true.
|
||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.builtins().get(a).unwrap().cost(input.len()) }
|
||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 {
|
||||
self.builtins().get(a).expect("queried cost of nonexistent builtin").cost(input.len())
|
||||
}
|
||||
/// Execution the builtin contract `a` on `input` and return `output`.
|
||||
/// Panics if `is_builtin(a)` is not true.
|
||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) { self.builtins().get(a).unwrap().execute(input, output); }
|
||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) {
|
||||
self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output);
|
||||
}
|
||||
|
||||
/// Check if new block should be chosen as the one in chain.
|
||||
fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Voting on a hash, where each vote has to come from a set of addresses.
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use common::{HashSet, RwLock, H256, Address};
|
||||
use util::{HashSet, RwLock, H256, Address};
|
||||
|
||||
/// Collect votes on a hash.
|
||||
#[derive(Debug)]
|
||||
@ -70,9 +70,10 @@ impl ProposeCollect {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use engines::propose_collect::ProposeCollect;
|
||||
use account_provider::AccountProvider;
|
||||
use util::*;
|
||||
use header::Header;
|
||||
|
||||
#[test]
|
||||
fn simple_propose_collect() {
|
||||
|
@ -17,7 +17,8 @@
|
||||
//! Voting on hashes, where each vote has to come from a set of public keys.
|
||||
|
||||
use super::EngineError;
|
||||
use common::{HashSet, HashMap, RwLock, H256, Address, Error, Hashable};
|
||||
use util::*;
|
||||
use error::Error;
|
||||
use ethkey::{Signature, recover};
|
||||
|
||||
/// Signed voting on hashes.
|
||||
@ -82,7 +83,8 @@ impl SignedVote {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use util::*;
|
||||
use header::Header;
|
||||
use engines::signed_vote::SignedVote;
|
||||
use account_provider::AccountProvider;
|
||||
|
||||
@ -105,18 +107,18 @@ mod tests {
|
||||
let bare_hash = header.bare_hash();
|
||||
|
||||
// Unapproved voter.
|
||||
let signature = tap.sign(addr3, bare_hash).unwrap();
|
||||
let signature = tap.sign(addr3, None, bare_hash).unwrap();
|
||||
assert!(!vote.vote(bare_hash, signature));
|
||||
assert!(vote.winner().is_none());
|
||||
// First good vote.
|
||||
let signature = tap.sign(addr1, bare_hash).unwrap();
|
||||
let signature = tap.sign(addr1, None, bare_hash).unwrap();
|
||||
assert!(vote.vote(bare_hash, signature));
|
||||
assert_eq!(vote.winner().unwrap(), bare_hash);
|
||||
// Voting again is ineffective.
|
||||
let signature = tap.sign(addr1, bare_hash).unwrap();
|
||||
let signature = tap.sign(addr1, None, bare_hash).unwrap();
|
||||
assert!(!vote.vote(bare_hash, signature));
|
||||
// Second valid vote.
|
||||
let signature = tap.sign(addr2, bare_hash).unwrap();
|
||||
let signature = tap.sign(addr2, None, bare_hash).unwrap();
|
||||
assert!(vote.vote(bare_hash, signature));
|
||||
assert_eq!(vote.winner().unwrap(), bare_hash);
|
||||
}
|
||||
|
@ -23,7 +23,12 @@ mod vote;
|
||||
mod vote_collector;
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||
use common::*;
|
||||
use util::*;
|
||||
use error::{Error, BlockError};
|
||||
use header::Header;
|
||||
use builtin::Builtin;
|
||||
use env_info::EnvInfo;
|
||||
use transaction::SignedTransaction;
|
||||
use rlp::{UntrustedRlp, View, encode};
|
||||
use ethkey::{recover, public_to_address};
|
||||
use account_provider::AccountProvider;
|
||||
@ -31,6 +36,7 @@ use block::*;
|
||||
use spec::CommonParams;
|
||||
use engines::{Engine, EngineError, ProposeCollect};
|
||||
use blockchain::extras::BlockDetails;
|
||||
use views::HeaderView;
|
||||
use evm::Schedule;
|
||||
use io::IoService;
|
||||
use self::message::ConsensusMessage;
|
||||
@ -368,11 +374,14 @@ impl Engine for Tendermint {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration};
|
||||
use util::*;
|
||||
use rlp::{UntrustedRlp, RlpStream, Stream, View, encode};
|
||||
use block::*;
|
||||
use error::{Error, BlockError};
|
||||
use header::Header;
|
||||
use env_info::EnvInfo;
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use spec::Spec;
|
||||
@ -406,11 +415,11 @@ mod tests {
|
||||
let mut seal = Vec::new();
|
||||
|
||||
let v0 = tap.insert_account("0".sha3(), "0").unwrap();
|
||||
let sig0 = tap.sign_with_password(v0, "0".into(), header.bare_hash()).unwrap();
|
||||
let sig0 = tap.sign(v0, Some("0".into()), header.bare_hash()).unwrap();
|
||||
seal.push(encode(&(&*sig0 as &[u8])).to_vec());
|
||||
|
||||
let v1 = tap.insert_account("1".sha3(), "1").unwrap();
|
||||
let sig1 = tap.sign_with_password(v1, "1".into(), header.bare_hash()).unwrap();
|
||||
let sig1 = tap.sign(v1, Some("1".into()), header.bare_hash()).unwrap();
|
||||
seal.push(encode(&(&*sig1 as &[u8])).to_vec());
|
||||
seal
|
||||
}
|
||||
@ -465,7 +474,7 @@ mod tests {
|
||||
let mut seal = Vec::new();
|
||||
|
||||
let v1 = tap.insert_account("0".sha3(), "0").unwrap();
|
||||
let sig1 = tap.sign_with_password(v1, "0".into(), header.bare_hash()).unwrap();
|
||||
let sig1 = tap.sign(v1, Some("0".into()), header.bare_hash()).unwrap();
|
||||
seal.push(encode(&(&*sig1 as &[u8])).to_vec());
|
||||
|
||||
header.set_seal(seal.clone());
|
||||
@ -474,7 +483,7 @@ mod tests {
|
||||
assert!(engine.verify_block_basic(&header, None).is_err());
|
||||
|
||||
let v2 = tap.insert_account("101".sha3(), "101").unwrap();
|
||||
let sig2 = tap.sign_with_password(v2, "101".into(), header.bare_hash()).unwrap();
|
||||
let sig2 = tap.sign(v2, Some("101".into()), header.bare_hash()).unwrap();
|
||||
seal.push(encode(&(&*sig2 as &[u8])).to_vec());
|
||||
|
||||
header.set_seal(seal);
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
//! Tendermint specific parameters.
|
||||
|
||||
use common::{Address, U256};
|
||||
use ethjson;
|
||||
use super::timeout::DefaultTimeouts;
|
||||
use util::{Address, U256};
|
||||
|
||||
/// `Tendermint` params.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
//! Tendermint block seal.
|
||||
|
||||
use common::{H256, Address, H520, Header};
|
||||
use util::Hashable;
|
||||
use util::*;
|
||||
use header::Header;
|
||||
use account_provider::AccountProvider;
|
||||
use rlp::{View, DecoderError, Decodable, Decoder, Encodable, RlpStream, Stream};
|
||||
use basic_types::Seal;
|
||||
@ -46,7 +46,7 @@ impl Vote {
|
||||
/// Use any unlocked validator account to create a vote.
|
||||
pub fn validate(header: &Header, accounts: &AccountProvider, validator: Address) -> Option<Vote> {
|
||||
let message = block_hash(&header);
|
||||
accounts.sign(validator, message)
|
||||
accounts.sign(validator, None, message)
|
||||
.ok()
|
||||
.map(Into::into)
|
||||
.map(|sig| Self::new(message, sig))
|
||||
|
@ -17,9 +17,9 @@
|
||||
//! Collects votes on hashes at each height and round.
|
||||
|
||||
use super::vote::Vote;
|
||||
use super::{Height, Round, BlockHash};
|
||||
use common::{HashSet, HashMap, RwLock, H256, Address, Error, Hashable};
|
||||
use ethkey::{Signature, recover};
|
||||
use super::{Height, Round};
|
||||
use ethkey::recover;
|
||||
use util::{RwLock, HashMap, HashSet};
|
||||
|
||||
/// Signed voting on hashes.
|
||||
#[derive(Debug)]
|
||||
|
@ -48,6 +48,13 @@ pub enum TransactionError {
|
||||
/// Transaction gas price
|
||||
got: U256,
|
||||
},
|
||||
/// Transaction's gas is below currently set minimal gas requirement.
|
||||
InsufficientGas {
|
||||
/// Minimal expected gas
|
||||
minimal: U256,
|
||||
/// Transaction gas
|
||||
got: U256,
|
||||
},
|
||||
/// Sender doesn't have enough funds to pay for this transaction
|
||||
InsufficientBalance {
|
||||
/// Senders balance
|
||||
@ -64,6 +71,12 @@ pub enum TransactionError {
|
||||
},
|
||||
/// Transaction's gas limit (aka gas) is invalid.
|
||||
InvalidGasLimit(OutOfBounds<U256>),
|
||||
/// Transaction sender is banned.
|
||||
SenderBanned,
|
||||
/// Transaction receipient is banned.
|
||||
RecipientBanned,
|
||||
/// Contract creation code is banned.
|
||||
CodeBanned,
|
||||
}
|
||||
|
||||
impl fmt::Display for TransactionError {
|
||||
@ -76,12 +89,17 @@ impl fmt::Display for TransactionError {
|
||||
LimitReached => "Transaction limit reached".into(),
|
||||
InsufficientGasPrice { minimal, got } =>
|
||||
format!("Insufficient gas price. Min={}, Given={}", minimal, got),
|
||||
InsufficientGas { minimal, got } =>
|
||||
format!("Insufficient gas. Min={}, Given={}", minimal, got),
|
||||
InsufficientBalance { balance, cost } =>
|
||||
format!("Insufficient balance for transaction. Balance={}, Cost={}",
|
||||
balance, cost),
|
||||
GasLimitExceeded { limit, got } =>
|
||||
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
||||
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
||||
SenderBanned => "Sender is temporarily banned.".into(),
|
||||
RecipientBanned => "Recipient is temporarily banned.".into(),
|
||||
CodeBanned => "Contract code is temporarily banned.".into(),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Transaction error ({})", msg))
|
||||
|
@ -15,9 +15,15 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use util::*;
|
||||
use block::*;
|
||||
use builtin::Builtin;
|
||||
use env_info::EnvInfo;
|
||||
use error::{BlockError, Error};
|
||||
use header::Header;
|
||||
use views::HeaderView;
|
||||
use spec::CommonParams;
|
||||
use transaction::SignedTransaction;
|
||||
use engines::Engine;
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
@ -42,7 +48,7 @@ pub struct EthashParams {
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
/// Homestead transition block number.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
pub homestead_transition: u64,
|
||||
/// DAO hard-fork transition block (X).
|
||||
pub dao_hardfork_transition: u64,
|
||||
/// DAO hard-fork refund contract address (C).
|
||||
@ -55,6 +61,8 @@ pub struct EthashParams {
|
||||
pub difficulty_hardfork_bound_divisor: U256,
|
||||
/// Block on which there is no additional difficulty from the exponential bomb.
|
||||
pub bomb_defuse_transition: u64,
|
||||
/// Bad gas transition block number.
|
||||
pub eip150_transition: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
@ -67,13 +75,14 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map_or(0, Into::into),
|
||||
homestead_transition: p.homestead_transition.map_or(0, Into::into),
|
||||
dao_hardfork_transition: p.dao_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
|
||||
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
|
||||
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into),
|
||||
bomb_defuse_transition: p.bomb_defuse_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
eip150_transition: p.eip150_transition.map_or(0, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,12 +127,14 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
trace!(target: "client", "Creating schedule. fCML={}", self.ethash_params.frontier_compatibility_mode_limit);
|
||||
trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition);
|
||||
|
||||
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
|
||||
if env_info.number < self.ethash_params.homestead_transition {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
} else if env_info.number < self.ethash_params.eip150_transition {
|
||||
Schedule::new_homestead()
|
||||
} else {
|
||||
Schedule::new_homestead_gas_fix()
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,8 +194,8 @@ impl Engine for Ethash {
|
||||
|
||||
// Commit state so that we can actually figure out the state root.
|
||||
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> {
|
||||
@ -265,7 +276,7 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
if header.number() >= self.ethash_params.frontier_compatibility_mode_limit {
|
||||
if header.number() >= self.ethash_params.homestead_transition {
|
||||
try!(t.check_low_s());
|
||||
}
|
||||
Ok(())
|
||||
@ -296,7 +307,7 @@ impl Ethash {
|
||||
false => self.ethash_params.difficulty_bound_divisor,
|
||||
};
|
||||
let duration_limit = self.ethash_params.duration_limit;
|
||||
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
||||
let frontier_limit = self.ethash_params.homestead_transition;
|
||||
|
||||
let mut target = if header.number() < frontier_limit {
|
||||
if header.timestamp() >= parent.timestamp() + duration_limit {
|
||||
@ -378,9 +389,12 @@ impl Header {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use util::*;
|
||||
use block::*;
|
||||
use tests::helpers::*;
|
||||
use env_info::EnvInfo;
|
||||
use error::{BlockError, Error};
|
||||
use header::Header;
|
||||
use super::super::new_morden;
|
||||
use super::Ethash;
|
||||
use rlp;
|
||||
|
@ -51,8 +51,11 @@ pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/fro
|
||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
|
||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
||||
pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
||||
|
||||
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
||||
pub fn new_daohardfork_test() -> Spec { load(include_bytes!("../../res/ethereum/daohardfork_test.json")) }
|
||||
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
||||
|
||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
@ -62,10 +65,11 @@ pub fn new_morden() -> Spec { load(include_bytes!("../../res/ethereum/morden.jso
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use util::*;
|
||||
use state::*;
|
||||
use super::*;
|
||||
use tests::helpers::*;
|
||||
use views::BlockView;
|
||||
|
||||
#[test]
|
||||
fn ensure_db_good() {
|
||||
|
@ -24,7 +24,8 @@ extern crate test;
|
||||
|
||||
use self::test::{Bencher, black_box};
|
||||
|
||||
use common::*;
|
||||
use util::*;
|
||||
use action_params::ActionParams;
|
||||
use evm::{self, Factory, VMType};
|
||||
use evm::tests::FakeExt;
|
||||
|
||||
|
@ -118,11 +118,12 @@ impl Factory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new instance of specific `VMType` factory
|
||||
pub fn new(evm: VMType) -> Self {
|
||||
/// Create new instance of specific `VMType` factory, with a size in bytes
|
||||
/// for caching jump destinations.
|
||||
pub fn new(evm: VMType, cache_size: usize) -> Self {
|
||||
Factory {
|
||||
evm: evm,
|
||||
evm_cache: Arc::new(SharedCache::default()),
|
||||
evm_cache: Arc::new(SharedCache::new(cache_size)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,22 +165,22 @@ macro_rules! evm_test(
|
||||
#[ignore]
|
||||
#[cfg(feature = "jit")]
|
||||
fn $name_jit() {
|
||||
$name_test(Factory::new(VMType::Jit));
|
||||
$name_test(Factory::new(VMType::Jit, 1024 * 32));
|
||||
}
|
||||
#[test]
|
||||
fn $name_int() {
|
||||
$name_test(Factory::new(VMType::Interpreter));
|
||||
$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
|
||||
}
|
||||
};
|
||||
($name_test: ident: $name_jit: ident, $name_int: ident) => {
|
||||
#[test]
|
||||
#[cfg(feature = "jit")]
|
||||
fn $name_jit() {
|
||||
$name_test(Factory::new(VMType::Jit));
|
||||
$name_test(Factory::new(VMType::Jit, 1024 * 32));
|
||||
}
|
||||
#[test]
|
||||
fn $name_int() {
|
||||
$name_test(Factory::new(VMType::Interpreter));
|
||||
$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -193,13 +194,13 @@ macro_rules! evm_test_ignore(
|
||||
#[cfg(feature = "jit")]
|
||||
#[cfg(feature = "ignored-tests")]
|
||||
fn $name_jit() {
|
||||
$name_test(Factory::new(VMType::Jit));
|
||||
$name_test(Factory::new(VMType::Jit, 1024 * 32));
|
||||
}
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(feature = "ignored-tests")]
|
||||
fn $name_int() {
|
||||
$name_test(Factory::new(VMType::Interpreter));
|
||||
$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -173,7 +173,7 @@ lazy_static! {
|
||||
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special);
|
||||
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext);
|
||||
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Special);
|
||||
arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
@ -183,8 +183,8 @@ lazy_static! {
|
||||
arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
|
||||
arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext);
|
||||
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext);
|
||||
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Special);
|
||||
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Special);
|
||||
arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext);
|
||||
arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base);
|
||||
@ -277,7 +277,7 @@ lazy_static! {
|
||||
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special);
|
||||
arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero);
|
||||
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special);
|
||||
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero);
|
||||
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Special);
|
||||
arr
|
||||
};
|
||||
}
|
||||
|
@ -14,11 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use common::*;
|
||||
use util::*;
|
||||
use super::u256_to_address;
|
||||
use evm::{self, CostType};
|
||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
||||
use evm::interpreter::stack::Stack;
|
||||
use evm::schedule::Schedule;
|
||||
|
||||
macro_rules! overflowing {
|
||||
($x: expr) => {{
|
||||
@ -29,12 +30,20 @@ macro_rules! overflowing {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
||||
enum InstructionCost<Cost: CostType> {
|
||||
enum Request<Cost: CostType> {
|
||||
Gas(Cost),
|
||||
GasMem(Cost, Cost),
|
||||
GasMemProvide(Cost, Cost, Option<U256>),
|
||||
GasMemCopy(Cost, Cost, Cost)
|
||||
}
|
||||
|
||||
pub struct InstructionRequirements<Cost: CostType> {
|
||||
pub gas_cost: Cost,
|
||||
pub provide_gas: Option<Cost>,
|
||||
pub memory_total_gas: Cost,
|
||||
pub memory_required_size: usize,
|
||||
}
|
||||
|
||||
pub struct Gasometer<Gas: CostType> {
|
||||
pub current_gas: Gas,
|
||||
pub current_mem_gas: Gas,
|
||||
@ -56,22 +65,60 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
}
|
||||
}
|
||||
|
||||
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
|
||||
/// and that we `requested` some.
|
||||
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> evm::Result<Gas> {
|
||||
// Try converting requested gas to `Gas` (`U256/u64`)
|
||||
// but in EIP150 even if we request more we should never fail from OOG
|
||||
let requested = requested.map(Gas::from_u256);
|
||||
|
||||
match schedule.sub_gas_cap_divisor {
|
||||
Some(cap_divisor) if self.current_gas >= needed => {
|
||||
let gas_remaining = self.current_gas - needed;
|
||||
let max_gas_provided = match cap_divisor {
|
||||
64 => gas_remaining - (gas_remaining >> 6),
|
||||
cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor),
|
||||
};
|
||||
|
||||
if let Some(Ok(r)) = requested {
|
||||
Ok(min(r, max_gas_provided))
|
||||
} else {
|
||||
Ok(max_gas_provided)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if let Some(r) = requested {
|
||||
r
|
||||
} else if self.current_gas >= needed {
|
||||
Ok(self.current_gas - needed)
|
||||
} else {
|
||||
Ok(0.into())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||
pub fn get_gas_cost_mem(
|
||||
/// 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`
|
||||
/// 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.
|
||||
pub fn requirements(
|
||||
&mut self,
|
||||
ext: &evm::Ext,
|
||||
instruction: Instruction,
|
||||
info: &InstructionInfo,
|
||||
stack: &Stack<U256>,
|
||||
current_mem_size: usize,
|
||||
) -> evm::Result<(Gas, Gas, usize)> {
|
||||
) -> evm::Result<InstructionRequirements<Gas>> {
|
||||
let schedule = ext.schedule();
|
||||
let tier = instructions::get_tier_idx(info.tier);
|
||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||
|
||||
let cost = match instruction {
|
||||
instructions::JUMPDEST => {
|
||||
InstructionCost::Gas(Gas::from(1))
|
||||
Request::Gas(Gas::from(1))
|
||||
},
|
||||
instructions::SSTORE => {
|
||||
let address = H256::from(stack.peek(0));
|
||||
@ -85,31 +132,47 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
// !is_zero(&val) && is_zero(newval)
|
||||
schedule.sstore_reset_gas
|
||||
};
|
||||
InstructionCost::Gas(Gas::from(gas))
|
||||
Request::Gas(Gas::from(gas))
|
||||
},
|
||||
instructions::SLOAD => {
|
||||
InstructionCost::Gas(Gas::from(schedule.sload_gas))
|
||||
Request::Gas(Gas::from(schedule.sload_gas))
|
||||
},
|
||||
instructions::BALANCE => {
|
||||
Request::Gas(Gas::from(schedule.balance_gas))
|
||||
},
|
||||
instructions::EXTCODESIZE => {
|
||||
Request::Gas(Gas::from(schedule.extcodesize_gas))
|
||||
},
|
||||
instructions::SUICIDE => {
|
||||
let mut gas = Gas::from(schedule.suicide_gas);
|
||||
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
if !ext.exists(&address) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
|
||||
}
|
||||
|
||||
Request::Gas(gas)
|
||||
},
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
|
||||
Request::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
|
||||
},
|
||||
instructions::MSTORE8 => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
|
||||
Request::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
|
||||
},
|
||||
instructions::RETURN => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
Request::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
},
|
||||
instructions::SHA3 => {
|
||||
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
|
||||
let words = w >> 5;
|
||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
Request::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
},
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
||||
Request::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
||||
},
|
||||
instructions::EXTCODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
},
|
||||
instructions::LOG0...instructions::LOG4 => {
|
||||
let no_of_topics = instructions::get_log_topics(instruction);
|
||||
@ -117,10 +180,10 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
|
||||
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
Request::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(5), stack.peek(6))),
|
||||
try!(mem_needed(stack.peek(3), stack.peek(4)))
|
||||
@ -129,55 +192,89 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
|
||||
if instruction == instructions::CALL && !ext.exists(&address) {
|
||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
|
||||
};
|
||||
|
||||
if !stack.peek(2).is_zero() {
|
||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
|
||||
};
|
||||
|
||||
InstructionCost::GasMem(gas,mem)
|
||||
let requested = *stack.peek(0);
|
||||
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
},
|
||||
instructions::DELEGATECALL => {
|
||||
let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
|
||||
let gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(4), stack.peek(5))),
|
||||
try!(mem_needed(stack.peek(2), stack.peek(3)))
|
||||
);
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
let requested = *stack.peek(0);
|
||||
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
},
|
||||
instructions::CREATE => {
|
||||
let gas = Gas::from(schedule.create_gas);
|
||||
let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
|
||||
Request::GasMemProvide(gas, mem, None)
|
||||
},
|
||||
instructions::EXP => {
|
||||
let expon = stack.peek(1);
|
||||
let bytes = ((expon.bits() + 7) / 8) as usize;
|
||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||
InstructionCost::Gas(gas)
|
||||
Request::Gas(gas)
|
||||
},
|
||||
_ => InstructionCost::Gas(default_gas)
|
||||
_ => Request::Gas(default_gas),
|
||||
};
|
||||
|
||||
match cost {
|
||||
InstructionCost::Gas(gas) => {
|
||||
Ok((gas, self.current_mem_gas, 0))
|
||||
Ok(match cost {
|
||||
Request::Gas(gas) => {
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: 0,
|
||||
memory_total_gas: self.current_mem_gas,
|
||||
}
|
||||
},
|
||||
InstructionCost::GasMem(gas, mem_size) => {
|
||||
Request::GasMem(gas, mem_size) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
Ok((gas, new_mem_gas, new_mem_size))
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
},
|
||||
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
|
||||
Request::GasMemProvide(gas, mem_size, requested) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
let provided = try!(self.gas_provided(schedule, gas, requested));
|
||||
let total_gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionRequirements {
|
||||
gas_cost: total_gas,
|
||||
provide_gas: Some(provided),
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
},
|
||||
Request::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let copy = overflowing!(add_gas_usize(copy, 31)) >> 5;
|
||||
let copy_gas = Gas::from(schedule.copy_gas) * copy;
|
||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
Ok((gas, new_mem_gas, new_mem_size))
|
||||
}
|
||||
}
|
||||
|
||||
InstructionRequirements {
|
||||
gas_cost: gas,
|
||||
provide_gas: None,
|
||||
memory_required_size: new_mem_size,
|
||||
memory_total_gas: new_mem_gas,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
|
||||
@ -187,7 +284,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
|
||||
|
||||
// Calculate s*s/quad_coeff_div
|
||||
debug_assert_eq!(schedule.quad_coeff_div, 512);
|
||||
assert_eq!(schedule.quad_coeff_div, 512);
|
||||
let b = overflowing!(s.overflow_mul_shr(s, 9));
|
||||
Ok(overflowing!(a.overflow_add(b)))
|
||||
};
|
||||
@ -259,3 +356,4 @@ fn test_calculate_mem_cost() {
|
||||
assert_eq!(new_mem_gas, 3);
|
||||
assert_eq!(mem_size, 32);
|
||||
}
|
||||
|
||||
|
@ -29,12 +29,14 @@ use self::memory::Memory;
|
||||
pub use self::shared_cache::SharedCache;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use common::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use types::executed::CallType;
|
||||
use super::instructions::{self, Instruction, InstructionInfo};
|
||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
||||
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
|
||||
use bit_set::BitSet;
|
||||
|
||||
use util::*;
|
||||
|
||||
type CodePosition = usize;
|
||||
type ProgramCounter = usize;
|
||||
|
||||
@ -52,14 +54,14 @@ const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 000
|
||||
/// Abstraction over raw vector of Bytes. Easier state management of PC.
|
||||
struct CodeReader<'a> {
|
||||
position: ProgramCounter,
|
||||
code: &'a Bytes
|
||||
code: &'a [u8]
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(len_without_is_empty))]
|
||||
impl<'a> CodeReader<'a> {
|
||||
|
||||
/// Create new code reader - starting at position 0.
|
||||
fn new(code: &'a Bytes) -> Self {
|
||||
fn new(code: &'a [u8]) -> Self {
|
||||
CodeReader {
|
||||
position: 0,
|
||||
code: code,
|
||||
@ -81,8 +83,6 @@ impl<'a> CodeReader<'a> {
|
||||
|
||||
enum InstructionResult<Gas> {
|
||||
Ok,
|
||||
UseAllGas,
|
||||
GasLeft(Gas),
|
||||
UnusedGas(Gas),
|
||||
JumpToPosition(U256),
|
||||
// gas left, init_orf, init_size
|
||||
@ -104,7 +104,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
|
||||
let mut informant = informant::EvmInformant::new(ext.depth());
|
||||
|
||||
let code = ¶ms.code.as_ref().unwrap();
|
||||
let code = ¶ms.code.as_ref().expect("exec always called with code; qed");
|
||||
let valid_jump_destinations = self.cache.jump_destinations(¶ms.code_hash, code);
|
||||
|
||||
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
|
||||
@ -120,14 +120,14 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
try!(self.verify_instruction(ext, instruction, info, &stack));
|
||||
|
||||
// Calculate gas cost
|
||||
let (gas_cost, mem_gas, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, info, &stack, self.mem.size()));
|
||||
let requirements = try!(gasometer.requirements(ext, instruction, info, &stack, self.mem.size()));
|
||||
// TODO: make compile-time removable if too much of a performance hit.
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &requirements.gas_cost.as_u256());
|
||||
|
||||
try!(gasometer.verify_gas(&gas_cost));
|
||||
self.mem.expand(mem_size);
|
||||
gasometer.current_mem_gas = mem_gas;
|
||||
gasometer.current_gas = gasometer.current_gas - gas_cost;
|
||||
try!(gasometer.verify_gas(&requirements.gas_cost));
|
||||
self.mem.expand(requirements.memory_required_size);
|
||||
gasometer.current_mem_gas = requirements.memory_total_gas;
|
||||
gasometer.current_gas = gasometer.current_gas - requirements.gas_cost;
|
||||
|
||||
evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) });
|
||||
|
||||
@ -138,27 +138,21 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack, requirements.provide_gas
|
||||
));
|
||||
|
||||
evm_debug!({ informant.after_instruction(instruction) });
|
||||
|
||||
if let InstructionResult::UnusedGas(ref gas) = result {
|
||||
gasometer.current_gas = gasometer.current_gas + *gas;
|
||||
}
|
||||
|
||||
if trace_executed {
|
||||
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
|
||||
}
|
||||
|
||||
// Advance
|
||||
match result {
|
||||
InstructionResult::Ok => {},
|
||||
InstructionResult::UnusedGas(gas) => {
|
||||
gasometer.current_gas = gasometer.current_gas + gas;
|
||||
},
|
||||
InstructionResult::UseAllGas => {
|
||||
gasometer.current_gas = Cost::from(0);
|
||||
},
|
||||
InstructionResult::GasLeft(gas_left) => {
|
||||
gasometer.current_gas = gas_left;
|
||||
},
|
||||
InstructionResult::JumpToPosition(position) => {
|
||||
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
||||
reader.position = pos;
|
||||
@ -168,6 +162,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
||||
},
|
||||
InstructionResult::StopExecution => break,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
informant.done();
|
||||
@ -250,7 +245,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
ext: &mut evm::Ext,
|
||||
instruction: Instruction,
|
||||
code: &mut CodeReader,
|
||||
stack: &mut Stack<U256>
|
||||
stack: &mut Stack<U256>,
|
||||
provided: Option<Cost>
|
||||
) -> evm::Result<InstructionResult<Cost>> {
|
||||
match instruction {
|
||||
instructions::JUMP => {
|
||||
@ -275,31 +271,32 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let endowment = stack.pop_back();
|
||||
let init_off = stack.pop_back();
|
||||
let init_size = stack.pop_back();
|
||||
let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed");
|
||||
|
||||
let contract_code = self.mem.read_slice(init_off, init_size);
|
||||
let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||
|
||||
if !can_create {
|
||||
stack.push(U256::zero());
|
||||
return Ok(InstructionResult::Ok);
|
||||
return Ok(InstructionResult::UnusedGas(create_gas));
|
||||
}
|
||||
|
||||
let create_result = ext.create(&gas.as_u256(), &endowment, contract_code);
|
||||
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code);
|
||||
return match create_result {
|
||||
ContractCreateResult::Created(address, gas_left) => {
|
||||
stack.push(address_to_u256(address));
|
||||
Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
},
|
||||
ContractCreateResult::Failed => {
|
||||
stack.push(U256::zero());
|
||||
// TODO [todr] Should we just StopExecution here?
|
||||
Ok(InstructionResult::UseAllGas)
|
||||
Ok(InstructionResult::Ok)
|
||||
}
|
||||
};
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
||||
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
||||
let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated.");
|
||||
stack.pop_back();
|
||||
let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed");
|
||||
let code_address = stack.pop_back();
|
||||
let code_address = u256_to_address(&code_address);
|
||||
|
||||
@ -317,17 +314,17 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
// Add stipend (only CALL|CALLCODE when value > 0)
|
||||
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val.is_zero() {
|
||||
false => Cost::from(ext.schedule().call_stipend),
|
||||
true => Cost::from(0)
|
||||
true => Cost::from(0),
|
||||
});
|
||||
|
||||
// Get sender & receive addresses, check if we have balance
|
||||
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
||||
instructions::CALL => {
|
||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
||||
let has_balance = ext.balance(¶ms.address) >= value.expect("value set for all but delegate call; qed");
|
||||
(¶ms.address, &code_address, has_balance, CallType::Call)
|
||||
},
|
||||
instructions::CALLCODE => {
|
||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
||||
let has_balance = ext.balance(¶ms.address) >= value.expect("value set for all but delegate call; qed");
|
||||
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
||||
},
|
||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
||||
|
@ -15,31 +15,51 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use lru_cache::LruCache;
|
||||
use util::{H256, Mutex};
|
||||
use util::{H256, HeapSizeOf, Mutex};
|
||||
use util::sha3::*;
|
||||
use util::cache::MemoryLruCache;
|
||||
use bit_set::BitSet;
|
||||
use super::super::instructions;
|
||||
|
||||
const CACHE_CODE_ITEMS: usize = 65536;
|
||||
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// GLobal cache for EVM interpreter
|
||||
// stub for a HeapSizeOf implementation.
|
||||
struct Bits(Arc<BitSet>);
|
||||
|
||||
impl HeapSizeOf for Bits {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
// dealing in bits here
|
||||
self.0.capacity() * 8
|
||||
}
|
||||
}
|
||||
|
||||
/// Global cache for EVM interpreter
|
||||
pub struct SharedCache {
|
||||
jump_destinations: Mutex<LruCache<H256, Arc<BitSet>>>
|
||||
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
|
||||
}
|
||||
|
||||
impl SharedCache {
|
||||
/// Get jump destincations bitmap for a contract.
|
||||
/// Create a jump destinations cache with a maximum size in bytes
|
||||
/// to cache.
|
||||
pub fn new(max_size: usize) -> Self {
|
||||
SharedCache {
|
||||
jump_destinations: Mutex::new(MemoryLruCache::new(max_size)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get jump destinations bitmap for a contract.
|
||||
pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc<BitSet> {
|
||||
if code_hash == &SHA3_EMPTY {
|
||||
return Self::find_jump_destinations(code);
|
||||
}
|
||||
|
||||
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
||||
return d.clone();
|
||||
return d.0.clone();
|
||||
}
|
||||
|
||||
let d = Self::find_jump_destinations(code);
|
||||
self.jump_destinations.lock().insert(code_hash.clone(), d.clone());
|
||||
self.jump_destinations.lock().insert(code_hash.clone(), Bits(d.clone()));
|
||||
|
||||
d
|
||||
}
|
||||
|
||||
@ -57,15 +77,15 @@ impl SharedCache {
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
|
||||
jump_dests.shrink_to_fit();
|
||||
Arc::new(jump_dests)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SharedCache {
|
||||
fn default() -> SharedCache {
|
||||
SharedCache {
|
||||
jump_destinations: Mutex::new(LruCache::new(CACHE_CODE_ITEMS)),
|
||||
}
|
||||
fn default() -> Self {
|
||||
SharedCache::new(DEFAULT_CACHE_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Just in time compiler execution environment.
|
||||
use common::*;
|
||||
use util::*;
|
||||
use evmjit;
|
||||
use evm::{self, GasLeft};
|
||||
use types::executed::CallType;
|
||||
@ -359,7 +359,7 @@ impl evm::Evm for JitEvm {
|
||||
data.timestamp = ext.env_info().timestamp as i64;
|
||||
|
||||
self.context = Some(unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) });
|
||||
let mut context = self.context.as_mut().unwrap();
|
||||
let mut context = self.context.as_mut().expect("context handle set on the prior line; qed");
|
||||
let res = context.exec();
|
||||
|
||||
match res {
|
||||
|
@ -80,6 +80,19 @@ pub struct Schedule {
|
||||
pub tx_data_non_zero_gas: usize,
|
||||
/// Gas price for copying memory
|
||||
pub copy_gas: usize,
|
||||
/// Price of EXTCODESIZE
|
||||
pub extcodesize_gas: usize,
|
||||
/// Base price of EXTCODECOPY
|
||||
pub extcodecopy_base_gas: usize,
|
||||
/// Price of BALANCE
|
||||
pub balance_gas: usize,
|
||||
/// Price of SUICIDE
|
||||
pub suicide_gas: usize,
|
||||
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
|
||||
pub suicide_to_new_account_cost: usize,
|
||||
/// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit.
|
||||
/// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS
|
||||
pub sub_gas_cap_divisor: Option<usize>,
|
||||
}
|
||||
|
||||
impl Schedule {
|
||||
@ -93,6 +106,49 @@ impl Schedule {
|
||||
Self::new(true, true, 53000)
|
||||
}
|
||||
|
||||
/// Schedule for the Homestead-era of the Ethereum main net.
|
||||
pub fn new_homestead_gas_fix() -> Schedule {
|
||||
Schedule{
|
||||
exceptional_failed_code_deposit: true,
|
||||
have_delegate_call: true,
|
||||
stack_limit: 1024,
|
||||
max_depth: 1024,
|
||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||
exp_gas: 10,
|
||||
exp_byte_gas: 10,
|
||||
sha3_gas: 30,
|
||||
sha3_word_gas: 6,
|
||||
sload_gas: 200,
|
||||
sstore_set_gas: 20000,
|
||||
sstore_reset_gas: 5000,
|
||||
sstore_refund_gas: 15000,
|
||||
jumpdest_gas: 1,
|
||||
log_gas: 375,
|
||||
log_data_gas: 8,
|
||||
log_topic_gas: 375,
|
||||
create_gas: 32000,
|
||||
call_gas: 700,
|
||||
call_stipend: 2300,
|
||||
call_value_transfer_gas: 9000,
|
||||
call_new_account_gas: 25000,
|
||||
suicide_refund_gas: 24000,
|
||||
memory_gas: 3,
|
||||
quad_coeff_div: 512,
|
||||
create_data_gas: 200,
|
||||
tx_gas: 21000,
|
||||
tx_create_gas: 53000,
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
extcodesize_gas: 700,
|
||||
extcodecopy_base_gas: 700,
|
||||
balance_gas: 400,
|
||||
suicide_gas: 5000,
|
||||
suicide_to_new_account_cost: 25000,
|
||||
sub_gas_cap_divisor: Some(64),
|
||||
}
|
||||
}
|
||||
|
||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||
Schedule{
|
||||
exceptional_failed_code_deposit: efcd,
|
||||
@ -126,6 +182,12 @@ impl Schedule {
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
extcodesize_gas: 20,
|
||||
extcodecopy_base_gas: 20,
|
||||
balance_gas: 20,
|
||||
suicide_gas: 0,
|
||||
suicide_to_new_account_cost: 0,
|
||||
sub_gas_cap_divisor: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// 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 evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
|
||||
use std::fmt::Debug;
|
||||
@ -817,7 +819,7 @@ fn test_signextend(factory: super::Factory) {
|
||||
|
||||
#[test] // JIT just returns out of gas
|
||||
fn test_badinstruction_int() {
|
||||
let factory = super::Factory::new(VMType::Interpreter);
|
||||
let factory = super::Factory::new(VMType::Interpreter, 1024 * 32);
|
||||
let code = "af".from_hex().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
|
@ -15,13 +15,17 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction Execution environment.
|
||||
use common::*;
|
||||
use util::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use state::{State, Substate};
|
||||
use engines::Engine;
|
||||
use types::executed::CallType;
|
||||
use env_info::EnvInfo;
|
||||
use error::ExecutionError;
|
||||
use evm::{self, Ext, Factory, Finalize};
|
||||
use externalities::*;
|
||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
||||
use transaction::{Action, SignedTransaction};
|
||||
use crossbeam;
|
||||
pub use types::executed::{Executed, ExecutionResult};
|
||||
|
||||
@ -250,7 +254,7 @@ impl<'a> Executive<'a> {
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
|
||||
// backup used in case of running out of gas
|
||||
self.state.snapshot();
|
||||
self.state.checkpoint();
|
||||
|
||||
// at first, transfer value to destination
|
||||
if let ActionValue::Transfer(val) = params.value {
|
||||
@ -269,7 +273,7 @@ impl<'a> Executive<'a> {
|
||||
let cost = self.engine.cost_of_builtin(¶ms.code_address, data);
|
||||
if cost <= params.gas {
|
||||
self.engine.execute_builtin(¶ms.code_address, data, &mut output);
|
||||
self.state.discard_snapshot();
|
||||
self.state.discard_checkpoint();
|
||||
|
||||
// trace only top level calls to builtins to avoid DDoS attacks
|
||||
if self.depth == 0 {
|
||||
@ -289,7 +293,7 @@ impl<'a> Executive<'a> {
|
||||
Ok(params.gas - cost)
|
||||
} else {
|
||||
// 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());
|
||||
|
||||
@ -335,7 +339,7 @@ impl<'a> Executive<'a> {
|
||||
res
|
||||
} else {
|
||||
// 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![]);
|
||||
Ok(params.gas)
|
||||
@ -354,7 +358,7 @@ impl<'a> Executive<'a> {
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
|
||||
// backup used in case of running out of gas
|
||||
self.state.snapshot();
|
||||
self.state.checkpoint();
|
||||
|
||||
// part of substate that may be reverted
|
||||
let mut unconfirmed_substate = Substate::new();
|
||||
@ -427,8 +431,16 @@ impl<'a> Executive<'a> {
|
||||
trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n",
|
||||
t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
|
||||
|
||||
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, t.sender().unwrap());
|
||||
self.state.add_balance(&t.sender().unwrap(), &refund_value);
|
||||
let sender = match t.sender() {
|
||||
Ok(sender) => sender,
|
||||
Err(e) => {
|
||||
debug!(target: "executive", "attempted to finalize transaction without sender: {}", e);
|
||||
return Err(ExecutionError::Internal);
|
||||
}
|
||||
};
|
||||
|
||||
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender);
|
||||
self.state.add_balance(&sender, &refund_value);
|
||||
trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author);
|
||||
self.state.add_balance(&self.info.author, &fees_value);
|
||||
|
||||
@ -477,10 +489,10 @@ impl<'a> Executive<'a> {
|
||||
| Err(evm::Error::BadInstruction {.. })
|
||||
| Err(evm::Error::StackUnderflow {..})
|
||||
| Err(evm::Error::OutOfStack {..}) => {
|
||||
self.state.revert_to_snapshot();
|
||||
self.state.revert_to_checkpoint();
|
||||
},
|
||||
Ok(_) | Err(evm::Error::Internal) => {
|
||||
self.state.discard_snapshot();
|
||||
self.state.discard_checkpoint();
|
||||
substate.accrue(un_substate);
|
||||
}
|
||||
}
|
||||
@ -492,13 +504,18 @@ impl<'a> Executive<'a> {
|
||||
mod tests {
|
||||
use ethkey::{Generator, Random};
|
||||
use super::*;
|
||||
use common::*;
|
||||
use util::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use env_info::EnvInfo;
|
||||
use evm::{Factory, VMType};
|
||||
use error::ExecutionError;
|
||||
use state::Substate;
|
||||
use tests::helpers::*;
|
||||
use trace::trace;
|
||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
||||
use transaction::{Action, Transaction};
|
||||
|
||||
use types::executed::CallType;
|
||||
|
||||
#[test]
|
||||
@ -598,7 +615,7 @@ mod tests {
|
||||
#[test]
|
||||
// Tracing is not suported in JIT
|
||||
fn test_call_to_create() {
|
||||
let factory = Factory::new(VMType::Interpreter);
|
||||
let factory = Factory::new(VMType::Interpreter, 1024 * 32);
|
||||
|
||||
// code:
|
||||
//
|
||||
@ -697,7 +714,7 @@ mod tests {
|
||||
VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 99979.into(), executed: Some(VMExecutedOperation { gas_used: 64755.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) }
|
||||
],
|
||||
@ -724,7 +741,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_create_contract() {
|
||||
// Tracing is not supported in JIT
|
||||
let factory = Factory::new(VMType::Interpreter);
|
||||
let factory = Factory::new(VMType::Interpreter, 1024 * 32);
|
||||
// code:
|
||||
//
|
||||
// 60 10 - push 16
|
||||
|
@ -15,9 +15,11 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction Execution environment.
|
||||
use common::*;
|
||||
use util::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use state::{State, Substate};
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use executive::*;
|
||||
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
|
||||
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]) {
|
||||
use log_entry::LogEntry;
|
||||
|
||||
let address = self.origin_info.address.clone();
|
||||
self.substate.logs.push(LogEntry {
|
||||
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)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use util::*;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use evm::Ext;
|
||||
use state::{State, Substate};
|
||||
use tests::helpers::*;
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Block header.
|
||||
|
||||
use util::*;
|
||||
use basic_types::*;
|
||||
use basic_types::{LogBloom, Seal, ZERO_LOGBLOOM};
|
||||
use time::get_time;
|
||||
use rlp::*;
|
||||
|
||||
@ -199,8 +199,9 @@ impl Header {
|
||||
match &mut *hash {
|
||||
&mut Some(ref h) => h.clone(),
|
||||
hash @ &mut None => {
|
||||
*hash = Some(self.rlp_sha3(Seal::With));
|
||||
hash.as_ref().unwrap().clone()
|
||||
let h = self.rlp_sha3(Seal::With);
|
||||
*hash = Some(h.clone());
|
||||
h
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,8 +212,9 @@ impl Header {
|
||||
match &mut *hash {
|
||||
&mut Some(ref h) => h.clone(),
|
||||
hash @ &mut None => {
|
||||
*hash = Some(self.rlp_sha3(Seal::Without));
|
||||
hash.as_ref().unwrap().clone()
|
||||
let h = self.rlp_sha3(Seal::Without);
|
||||
*hash = Some(h.clone());
|
||||
h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,8 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
let mut spec = match era {
|
||||
ChainEra::Frontier => ethereum::new_frontier_test(),
|
||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||
ChainEra::DaoHardfork => ethereum::new_daohardfork_test(),
|
||||
ChainEra::Eip150 => ethereum::new_eip150_test(),
|
||||
ChainEra::TransitionTest => ethereum::new_transition_test(),
|
||||
};
|
||||
spec.set_genesis_state(state);
|
||||
spec.overwrite_genesis_params(genesis);
|
||||
@ -116,14 +117,38 @@ mod frontier_era_tests {
|
||||
declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"}
|
||||
}
|
||||
|
||||
mod daohardfork_tests {
|
||||
mod transition_tests {
|
||||
use tests::helpers::*;
|
||||
use super::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::DaoHardfork)
|
||||
json_chain_test(json_data, ChainEra::TransitionTest)
|
||||
}
|
||||
|
||||
declare_test!{BlockchainTests_TestNetwork_bcSimpleTransitionTest, "BlockchainTests/TestNetwork/bcSimpleTransitionTest"}
|
||||
declare_test!{BlockchainTests_TestNetwork_bcTheDaoTest, "BlockchainTests/TestNetwork/bcTheDaoTest"}
|
||||
declare_test!{BlockchainTests_TestNetwork_bcEIP150Test, "BlockchainTests/TestNetwork/bcEIP150Test"}
|
||||
}
|
||||
|
||||
mod eip150_blockchain_tests {
|
||||
use tests::helpers::*;
|
||||
use super::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip150)
|
||||
}
|
||||
|
||||
declare_test!{BlockchainTests_EIP150_bcBlockGasLimitTest, "BlockchainTests/EIP150/bcBlockGasLimitTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcForkStressTest, "BlockchainTests/EIP150/bcForkStressTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcGasPricerTest, "BlockchainTests/EIP150/bcGasPricerTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcInvalidHeaderTest, "BlockchainTests/EIP150/bcInvalidHeaderTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcInvalidRLPTest, "BlockchainTests/EIP150/bcInvalidRLPTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcMultiChainTest, "BlockchainTests/EIP150/bcMultiChainTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcRPC_API_Test, "BlockchainTests/EIP150/bcRPC_API_Test"}
|
||||
declare_test!{BlockchainTests_EIP150_bcStateTest, "BlockchainTests/EIP150/bcStateTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcTotalDifficultyTest, "BlockchainTests/EIP150/bcTotalDifficultyTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcUncleHeaderValiditiy, "BlockchainTests/EIP150/bcUncleHeaderValiditiy"}
|
||||
declare_test!{BlockchainTests_EIP150_bcUncleTest, "BlockchainTests/EIP150/bcUncleTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcValidBlockTest, "BlockchainTests/EIP150/bcValidBlockTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcWalletTest, "BlockchainTests/EIP150/bcWalletTest"}
|
||||
}
|
||||
|
43
ethcore/src/json_tests/eip150_state.rs
Normal file
43
ethcore/src/json_tests/eip150_state.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use super::state::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip150)
|
||||
}
|
||||
|
||||
declare_test!{StateTests_EIP150_stEIPSpecificTest, "StateTests/EIP150/stEIPSpecificTest"}
|
||||
declare_test!{StateTests_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP150/stEIPsingleCodeGasPrices"}
|
||||
declare_test!{StateTests_EIP150_stMemExpandingEIPCalls, "StateTests/EIP150/stMemExpandingEIPCalls"}
|
||||
|
||||
declare_test!{StateTests_EIP150_stCallCodes, "StateTests/EIP150/Homestead/stCallCodes"}
|
||||
declare_test!{StateTests_EIP150_stCallCreateCallCodeTest, "StateTests/EIP150/Homestead/stCallCreateCallCodeTest"}
|
||||
declare_test!{StateTests_EIP150_stDelegatecallTest, "StateTests/EIP150/Homestead/stDelegatecallTest"}
|
||||
declare_test!{StateTests_EIP150_stInitCodeTest, "StateTests/EIP150/Homestead/stInitCodeTest"}
|
||||
declare_test!{StateTests_EIP150_stLogTests, "StateTests/EIP150/Homestead/stLogTests"}
|
||||
declare_test!{heavy => StateTests_EIP150_stMemoryStressTest, "StateTests/EIP150/Homestead/stMemoryStressTest"}
|
||||
declare_test!{heavy => StateTests_EIP150_stMemoryTest, "StateTests/EIP150/Homestead/stMemoryTest"}
|
||||
declare_test!{StateTests_EIP150_stPreCompiledContracts, "StateTests/EIP150/Homestead/stPreCompiledContracts"}
|
||||
declare_test!{heavy => StateTests_EIP150_stQuadraticComplexityTest, "StateTests/EIP150/Homestead/stQuadraticComplexityTest"}
|
||||
declare_test!{StateTests_EIP150_stRecursiveCreate, "StateTests/EIP150/Homestead/stRecursiveCreate"}
|
||||
declare_test!{StateTests_EIP150_stRefundTest, "StateTests/EIP150/Homestead/stRefundTest"}
|
||||
declare_test!{StateTests_EIP150_stSpecialTest, "StateTests/EIP150/Homestead/stSpecialTest"}
|
||||
declare_test!{StateTests_EIP150_stSystemOperationsTest, "StateTests/EIP150/Homestead/stSystemOperationsTest"}
|
||||
declare_test!{StateTests_EIP150_stTransactionTest, "StateTests/EIP150/Homestead/stTransactionTest"}
|
||||
declare_test!{StateTests_EIP150_stWalletTest, "StateTests/EIP150/Homestead/stWalletTest"}
|
@ -15,9 +15,11 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::test_common::*;
|
||||
use action_params::ActionParams;
|
||||
use state::{State, Substate};
|
||||
use executive::*;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use evm;
|
||||
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
|
||||
use externalities::*;
|
||||
@ -191,7 +193,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
|
||||
state.populate_from(From::from(vm.pre_state.clone()));
|
||||
let info = From::from(vm.env);
|
||||
let engine = TestEngine::new(1);
|
||||
let vm_factory = Factory::new(vm_type.clone());
|
||||
let vm_factory = Factory::new(vm_type.clone(), 1024 * 32);
|
||||
let params = ActionParams::from(vm.transaction);
|
||||
|
||||
let mut substate = Substate::new();
|
||||
|
@ -36,3 +36,6 @@ declare_test!{BlockchainTests_Homestead_bcUncleHeaderValiditiy, "BlockchainTests
|
||||
declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/bcUncleTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
|
||||
declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
|
||||
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user