Merge remote-tracking branch 'parity/master' into bft

This commit is contained in:
keorn 2016-11-15 10:26:37 +00:00
commit d19e8c5505
400 changed files with 13886 additions and 3861 deletions

2
.gitignore vendored
View File

@ -30,3 +30,5 @@
# Build artifacts # Build artifacts
out/ out/
.vscode

View File

@ -1,4 +1,6 @@
stages: stages:
- test
- js-build
- build - build
variables: variables:
GIT_DEPTH: "3" GIT_DEPTH: "3"
@ -6,26 +8,27 @@ variables:
RUST_BACKTRACE: "1" RUST_BACKTRACE: "1"
RUSTFLAGS: "" RUSTFLAGS: ""
CARGOFLAGS: "" CARGOFLAGS: ""
NIGHTLY: "nigtly"
cache: cache:
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME" key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
untracked: true untracked: true
linux-stable: linux-stable:
stage: build stage: build
image: ethcore/rust:stable image: ethcore/rust:stable
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- cargo build --release $CARGOFLAGS - cargo build --release $CARGOFLAGS
- strip target/release/parity - strip target/release/parity
- md5sum target/release/parity >> parity.md5 - md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64 - sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity - cp target/release/parity deb/usr/bin/parity
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n") - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb" - dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5" - md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
@ -39,44 +42,14 @@ linux-stable:
paths: paths:
- target/release/parity - target/release/parity
name: "stable-x86_64-unknown-linux-gnu_parity" name: "stable-x86_64-unknown-linux-gnu_parity"
linux-stable-14.04:
stage: build
image: ethcore/rust-14.04:latest
only:
- master
- beta
- tags
- stable
script:
- cargo build --release $CARGOFLAGS
- strip target/release/parity
- 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/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
artifacts:
paths:
- target/release/parity
name: "stable-x86_64-unknown-ubuntu_14_04-gnu_parity"
linux-beta: linux-beta:
stage: build stage: build
image: ethcore/rust:beta image: ethcore/rust:beta
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- cargo build --release $CARGOFLAGS - cargo build --release $CARGOFLAGS
- strip target/release/parity - strip target/release/parity
@ -92,10 +65,10 @@ linux-nightly:
stage: build stage: build
image: ethcore/rust:nightly image: ethcore/rust:nightly
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- cargo build --release $CARGOFLAGS - cargo build --release $CARGOFLAGS
- strip target/release/parity - strip target/release/parity
@ -111,16 +84,16 @@ linux-centos:
stage: build stage: build
image: ethcore/rust-centos:latest image: ethcore/rust-centos:latest
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- export CXX="g++" - export CXX="g++"
- export CC="gcc" - export CC="gcc"
- cargo build --release $CARGOFLAGS - cargo build --release $CARGOFLAGS
- strip target/release/parity - strip target/release/parity
- md5sum target/release/parity >> parity.md5 - md5sum target/release/parity > parity.md5
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
@ -132,14 +105,47 @@ linux-centos:
paths: paths:
- target/release/parity - target/release/parity
name: "x86_64-unknown-centos-gnu_parity" name: "x86_64-unknown-centos-gnu_parity"
linux-i686:
stage: build
image: ethcore/rust-i686:latest
only:
- beta
- tags
- stable
- triggers
script:
- export HOST_CC=gcc
- export HOST_CXX=g++
- cargo build --target i686-unknown-linux-gnu --release $CARGOFLAGS
- strip target/i686-unknown-linux-gnu/release/parity
- md5sum target/i686-unknown-linux-gnu/release/parity > parity.md5
- sh scripts/deb-build.sh i386
- cp target/i686-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"_i386.deb"
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity --body target/i686-unknown-linux-gnu/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
tags:
- rust
- rust-i686
artifacts:
paths:
- target/i686-unknown-linux-gnu/release/parity
name: "i686-unknown-linux-gnu"
allow_failure: true
linux-armv7: linux-armv7:
stage: build stage: build
image: ethcore/rust-armv7:latest image: ethcore/rust-armv7:latest
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- export CC=arm-linux-gnueabihf-gcc - export CC=arm-linux-gnueabihf-gcc
- export CXX=arm-linux-gnueabihf-g++ - export CXX=arm-linux-gnueabihf-g++
@ -152,12 +158,12 @@ linux-armv7:
- cat .cargo/config - cat .cargo/config
- cargo build --target armv7-unknown-linux-gnueabihf --release $CARGOFLAGS - cargo build --target armv7-unknown-linux-gnueabihf --release $CARGOFLAGS
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity - arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> parity.md5 - md5sum target/armv7-unknown-linux-gnueabihf/release/parity > parity.md5
- sh scripts/deb-build.sh armhf - sh scripts/deb-build.sh armhf
- cp target/armv7-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity - 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") - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_armhf.deb" - dpkg-deb -b deb "parity_"$VER"_armhf.deb"
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5" - md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
@ -176,10 +182,10 @@ linux-arm:
stage: build stage: build
image: ethcore/rust-arm:latest image: ethcore/rust-arm:latest
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- export CC=arm-linux-gnueabihf-gcc - export CC=arm-linux-gnueabihf-gcc
- export CXX=arm-linux-gnueabihf-g++ - export CXX=arm-linux-gnueabihf-g++
@ -192,12 +198,12 @@ linux-arm:
- cat .cargo/config - cat .cargo/config
- cargo build --target arm-unknown-linux-gnueabihf --release $CARGOFLAGS - cargo build --target arm-unknown-linux-gnueabihf --release $CARGOFLAGS
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity - arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> parity.md5 - md5sum target/arm-unknown-linux-gnueabihf/release/parity > parity.md5
- sh scripts/deb-build.sh armhf - sh scripts/deb-build.sh armhf
- cp target/arm-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity - 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") - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_armhf.deb" - dpkg-deb -b deb "parity_"$VER"_armhf.deb"
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5" - md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
@ -219,6 +225,7 @@ linux-armv6:
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- export CC=arm-linux-gnueabi-gcc - export CC=arm-linux-gnueabi-gcc
- export CXX=arm-linux-gnueabi-g++ - export CXX=arm-linux-gnueabi-g++
@ -231,7 +238,7 @@ linux-armv6:
- cat .cargo/config - cat .cargo/config
- cargo build --target arm-unknown-linux-gnueabi --release $CARGOFLAGS - cargo build --target arm-unknown-linux-gnueabi --release $CARGOFLAGS
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity - arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> parity.md5 - md5sum target/arm-unknown-linux-gnueabi/release/parity > parity.md5
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-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/parity --body target/arm-unknown-linux-gnueabi/release/parity
@ -248,10 +255,10 @@ linux-aarch64:
stage: build stage: build
image: ethcore/rust-aarch64:latest image: ethcore/rust-aarch64:latest
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- export CC=aarch64-linux-gnu-gcc - export CC=aarch64-linux-gnu-gcc
- export CXX=aarch64-linux-gnu-g++ - export CXX=aarch64-linux-gnu-g++
@ -264,12 +271,12 @@ linux-aarch64:
- cat .cargo/config - cat .cargo/config
- cargo build --target aarch64-unknown-linux-gnu --release $CARGOFLAGS - cargo build --target aarch64-unknown-linux-gnu --release $CARGOFLAGS
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity - aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> parity.md5 - md5sum target/aarch64-unknown-linux-gnu/release/parity > parity.md5
- sh scripts/deb-build.sh arm64 - sh scripts/deb-build.sh arm64
- cp target/aarch64-unknown-linux-gnu/release/parity deb/usr/bin/parity - 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") - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_arm64.deb" - dpkg-deb -b deb "parity_"$VER"_arm64.deb"
- md5sum "parity_"$VER"_arm64.deb" >> "parity_"$VER"_arm64.deb.md5" - md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
@ -287,14 +294,14 @@ linux-aarch64:
darwin: darwin:
stage: build stage: build
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- cargo build --release $CARGOFLAGS - cargo build --release $CARGOFLAGS
- rm -rf parity.md5 - rm -rf parity.md5
- md5sum target/release/parity >> parity.md5 - md5sum target/release/parity > parity.md5
- aws configure set aws_access_key_id $s3_key - aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret - aws configure set aws_secret_access_key $s3_secret
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity - aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity
@ -306,22 +313,27 @@ darwin:
- target/release/parity - target/release/parity
name: "x86_64-apple-darwin_parity" name: "x86_64-apple-darwin_parity"
windows: windows:
cache:
key: "%CI_BUILD_STAGE%/%CI_BUILD_REF_NAME%"
untracked: true
stage: build stage: build
only: only:
- master
- beta - beta
- tags - tags
- stable - stable
- triggers
script: script:
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64 - set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
- set RUST_BACKTRACE=1 - set RUST_BACKTRACE=1
- set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off - set RUSTFLAGS=%RUSTFLAGS%
- rustup default stable-x86_64-pc-windows-msvc - rustup default stable-x86_64-pc-windows-msvc
- cargo build --release %CARGOFLAGS% - 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/SimpleFC.dll" -o nsis\SimpleFC.dll
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe - curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe - signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
- msbuild windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release
- signtool sign /f %keyfile% /p %certpass% windows\ptray\x64\release\ptray.exe
- cd nsis - cd nsis
- makensis.exe installer.nsi - makensis.exe installer.nsi
- copy installer.exe InstallParity.exe - copy installer.exe InstallParity.exe
@ -353,70 +365,103 @@ windows:
- target/release/parity.pdb - target/release/parity.pdb
- nsis/InstallParity.exe - nsis/InstallParity.exe
name: "x86_64-pc-windows-msvc_parity" name: "x86_64-pc-windows-msvc_parity"
#test-darwin: test-darwin:
# stage: build stage: test
# before_script: only:
# - git submodule update --init --recursive - triggers
# 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: build
before_script: before_script:
- git submodule update --init --recursive - git submodule update --init --recursive
script: script:
- export RUST_BACKTRACE=1 - export RUST_BACKTRACE=1
- ./test.sh $CARGOFLAGS --no-release - ./test.sh $CARGOFLAGS --no-release
tags: tags:
- rust-test - osx
js-release: allow_failure: true
stage: build test-windows:
image: ethcore/javascript:latest stage: test
only: only:
- master - triggers
before_script: before_script:
- ./js/scripts/install-deps.sh - git submodule update --init --recursive
script: script:
- ./js/scripts/build.sh - set RUST_BACKTRACE=1
- ./js/scripts/release.sh - 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: tags:
- javascript - rust-windows
js-lint: allow_failure: true
stage: build test-rust-stable:
image: ethcore/javascript:latest stage: test
image: ethcore/rust:stable
before_script:
- git submodule update --init --recursive
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only CI_BUILD_REF CI_BUILD_REF@{1} | grep \.js | wc -l)
- echo $JS_FILES_MODIFIED
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; fi
script:
- export RUST_BACKTRACE=1
- echo $JS_FILES_MODIFIED
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; else ./test.sh $CARGOFLAGS --no-release; fi
tags:
- rust
- rust-stable
test-rust-beta:
stage: test
only:
- triggers
image: ethcore/rust:beta
before_script:
- git submodule update --init --recursive
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only CI_BUILD_REF CI_BUILD_REF@{1} | grep \.js | wc -l)
- echo $JS_FILES_MODIFIED
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; fi
script:
- export RUST_BACKTRACE=1
- echo $JS_FILES_MODIFIED
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; else ./test.sh $CARGOFLAGS --no-release; fi
tags:
- rust
- rust-beta
allow_failure: true
test-rust-nightly:
stage: test
only:
- triggers
image: ethcore/rust:nightly
before_script:
- git submodule update --init --recursive
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only CI_BUILD_REF CI_BUILD_REF@{1} | grep \.js | wc -l)
- echo $JS_FILES_MODIFIED
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; fi
script:
- export RUST_BACKTRACE=1
- echo $JS_FILES_MODIFIED
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; else ./test.sh $CARGOFLAGS --no-release; fi
tags:
- rust
- rust-nightly
allow_failure: true
js-tests:
stage: test
image: ethcore/rust:stable
before_script: before_script:
- ./js/scripts/install-deps.sh - ./js/scripts/install-deps.sh
script: script:
- ./js/scripts/lint.sh - ./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 - ./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 - ./js/scripts/build.sh
tags: tags:
- javascript-test - javascript-test
js-release:
stage: js-build
only:
- master
- beta
- stable
image: ethcore/rust:stable
before_script:
- if [[ $NIGHTLY != "master" ]]; then ./js/scripts/install-deps.sh; fi
script:
- if [[ $NIGHTLY != "master" ]]; then ./js/scripts/build.sh; fi
- if [[ $NIGHTLY != "master" ]]; then ./js/scripts/release.sh; fi
tags:
- javascript

View File

@ -17,7 +17,7 @@ matrix:
include: include:
- rust: stable - rust: stable
env: RUN_TESTS="true" TEST_OPTIONS="--no-release" env: RUN_TESTS="true" TEST_OPTIONS="--no-release"
- rust: beta - rust: stable
env: RUN_COVERAGE="true" env: RUN_COVERAGE="true"
- rust: stable - rust: stable
env: RUN_DOCS="true" env: RUN_DOCS="true"

193
Cargo.lock generated
View File

@ -1,6 +1,6 @@
[root] [root]
name = "parity" name = "parity"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
@ -8,21 +8,21 @@ dependencies = [
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.4.0", "ethcore 1.5.0",
"ethcore-dapps 1.4.0", "ethcore-dapps 1.5.0",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-io 1.4.0", "ethcore-io 1.5.0",
"ethcore-ipc 1.4.0", "ethcore-ipc 1.4.0",
"ethcore-ipc-codegen 1.4.0", "ethcore-ipc-codegen 1.4.0",
"ethcore-ipc-hypervisor 1.2.0", "ethcore-ipc-hypervisor 1.2.0",
"ethcore-ipc-nano 1.4.0", "ethcore-ipc-nano 1.4.0",
"ethcore-ipc-tests 0.1.0", "ethcore-ipc-tests 0.1.0",
"ethcore-logger 1.4.0", "ethcore-logger 1.5.0",
"ethcore-rpc 1.4.0", "ethcore-rpc 1.5.0",
"ethcore-signer 1.4.0", "ethcore-signer 1.5.0",
"ethcore-stratum 1.4.0", "ethcore-stratum 1.4.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"ethsync 1.4.0", "ethsync 1.5.0",
"fdlimit 0.1.0", "fdlimit 0.1.0",
"hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -185,7 +185,7 @@ version = "1.1.1"
source = "git+https://github.com/ethcore/rust-ctrlc.git#f4927770f89eca80ec250911eea3adcbf579ac48" source = "git+https://github.com/ethcore/rust-ctrlc.git#f4927770f89eca80ec250911eea3adcbf579ac48"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -194,7 +194,7 @@ name = "daemonize"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -244,7 +244,7 @@ source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8
dependencies = [ dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -274,7 +274,7 @@ dependencies = [
[[package]] [[package]]
name = "ethcore" name = "ethcore"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -285,11 +285,11 @@ dependencies = [
"ethash 1.4.0", "ethash 1.4.0",
"ethcore-bloom-journal 0.1.0", "ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-io 1.4.0", "ethcore-io 1.5.0",
"ethcore-ipc 1.4.0", "ethcore-ipc 1.4.0",
"ethcore-ipc-codegen 1.4.0", "ethcore-ipc-codegen 1.4.0",
"ethcore-ipc-nano 1.4.0", "ethcore-ipc-nano 1.4.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"ethstore 0.1.0", "ethstore 0.1.0",
@ -312,7 +312,7 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-bigint" name = "ethcore-bigint"
version = "0.1.1" version = "0.1.2"
dependencies = [ dependencies = [
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -329,14 +329,14 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-dapps" name = "ethcore-dapps"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"clippy 0.0.96 (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)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-rpc 1.4.0", "ethcore-rpc 1.5.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"fetch 0.1.0", "fetch 0.1.0",
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -352,6 +352,7 @@ dependencies = [
"serde 0.8.4 (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_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "zip 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
@ -366,11 +367,11 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-io" name = "ethcore-io"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.0 (git+https://github.com/carllerche/mio)", "mio 0.6.1 (git+https://github.com/carllerche/mio)",
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -380,7 +381,7 @@ name = "ethcore-ipc"
version = "1.4.0" version = "1.4.0"
dependencies = [ dependencies = [
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -427,7 +428,7 @@ dependencies = [
"ethcore-ipc 1.4.0", "ethcore-ipc 1.4.0",
"ethcore-ipc-codegen 1.4.0", "ethcore-ipc-codegen 1.4.0",
"ethcore-ipc-nano 1.4.0", "ethcore-ipc-nano 1.4.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -435,10 +436,10 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-logger" name = "ethcore-logger"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -448,19 +449,19 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-network" name = "ethcore-network"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-io 1.4.0", "ethcore-io 1.5.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"ethcrypto 0.1.0", "ethcrypto 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.0 (git+https://github.com/carllerche/mio)", "mio 0.6.1 (git+https://github.com/carllerche/mio)",
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0", "rlp 0.1.0",
@ -473,20 +474,20 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-rpc" name = "ethcore-rpc"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"clippy 0.0.96 (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", "ethash 1.4.0",
"ethcore 1.4.0", "ethcore 1.5.0",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-io 1.4.0", "ethcore-io 1.5.0",
"ethcore-ipc 1.4.0", "ethcore-ipc 1.4.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"ethcrypto 0.1.0", "ethcrypto 0.1.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"ethstore 0.1.0", "ethstore 0.1.0",
"ethsync 1.4.0", "ethsync 1.5.0",
"fetch 0.1.0", "fetch 0.1.0",
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", "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-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -503,14 +504,14 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-signer" name = "ethcore-signer"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"clippy 0.0.96 (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)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"ethcore-io 1.4.0", "ethcore-io 1.5.0",
"ethcore-rpc 1.4.0", "ethcore-rpc 1.5.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -529,7 +530,7 @@ dependencies = [
"ethcore-ipc 1.4.0", "ethcore-ipc 1.4.0",
"ethcore-ipc-codegen 1.4.0", "ethcore-ipc-codegen 1.4.0",
"ethcore-ipc-nano 1.4.0", "ethcore-ipc-nano 1.4.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)", "json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)",
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -540,7 +541,7 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-util" name = "ethcore-util"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
@ -548,13 +549,13 @@ dependencies = [
"elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)", "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-bigint 0.1.1", "ethcore-bigint 0.1.2",
"ethcore-bloom-journal 0.1.0", "ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.4.0", "ethcore-devtools 1.4.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lru-cache 0.1.0 (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)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -579,7 +580,7 @@ name = "ethcrypto"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-bigint 0.1.1", "ethcore-bigint 0.1.2",
"ethkey 0.2.0", "ethkey 0.2.0",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -589,7 +590,7 @@ dependencies = [
name = "ethjson" name = "ethjson"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.4 (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_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -602,7 +603,7 @@ version = "0.2.0"
dependencies = [ dependencies = [
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-bigint 0.1.1", "ethcore-bigint 0.1.2",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -618,7 +619,7 @@ dependencies = [
"ethkey 0.2.0", "ethkey 0.2.0",
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -632,17 +633,17 @@ dependencies = [
[[package]] [[package]]
name = "ethsync" name = "ethsync"
version = "1.4.0" version = "1.5.0"
dependencies = [ dependencies = [
"clippy 0.0.96 (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)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.4.0", "ethcore 1.5.0",
"ethcore-io 1.4.0", "ethcore-io 1.5.0",
"ethcore-ipc 1.4.0", "ethcore-ipc 1.4.0",
"ethcore-ipc-codegen 1.4.0", "ethcore-ipc-codegen 1.4.0",
"ethcore-ipc-nano 1.4.0", "ethcore-ipc-nano 1.4.0",
"ethcore-network 1.4.0", "ethcore-network 1.5.0",
"ethcore-util 1.4.0", "ethcore-util 1.5.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -663,7 +664,7 @@ dependencies = [
name = "fdlimit" name = "fdlimit"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -681,7 +682,7 @@ name = "flate2"
version = "0.2.14" version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -802,7 +803,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -894,7 +895,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -930,7 +931,7 @@ name = "memchr"
version = "0.1.11" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -958,7 +959,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -967,7 +968,7 @@ version = "0.5.1"
source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd" source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd"
dependencies = [ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
@ -983,7 +984,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
@ -999,7 +1000,7 @@ version = "0.6.0-dev"
source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972" source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1010,16 +1011,16 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.6.0" version = "0.6.1"
source = "git+https://github.com/carllerche/mio#9f17b70d6fecbf912168267ea74cf536f2cba705" source = "git+https://github.com/carllerche/mio#56f8663510196fdca04bdf7c5f4d60b24297826f"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.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)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1050,7 +1051,7 @@ name = "nanomsg"
version = "0.5.1" version = "0.5.1"
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00" source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", "nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)",
] ]
@ -1060,7 +1061,7 @@ version = "0.5.0"
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00" source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
dependencies = [ dependencies = [
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1070,7 +1071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1081,7 +1082,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1091,7 +1092,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1181,7 +1195,7 @@ name = "num_cpus"
version = "0.2.11" version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1235,7 +1249,7 @@ dependencies = [
[[package]] [[package]]
name = "parity-ui-precompiled" name = "parity-ui-precompiled"
version = "1.4.0" version = "1.4.0"
source = "git+https://github.com/ethcore/js-precompiled.git#10a57a7df153360b4abeddff99e5b8f34a85ff53" source = "git+https://github.com/ethcore/js-precompiled.git#ce7e830d36483dab10419d9105ac39dc520e7a61"
dependencies = [ dependencies = [
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1246,7 +1260,7 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.1.8 (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)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1266,7 +1280,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.1.8 (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)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1405,7 +1419,7 @@ name = "rand"
version = "0.3.14" version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1449,7 +1463,7 @@ name = "rlp"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)", "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
"ethcore-bigint 0.1.1", "ethcore-bigint 0.1.2",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1459,7 +1473,7 @@ name = "rocksdb"
version = "0.4.5" version = "0.4.5"
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58" source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
] ]
@ -1469,7 +1483,7 @@ version = "0.3.0"
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58" source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
dependencies = [ dependencies = [
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1489,7 +1503,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1500,7 +1514,7 @@ version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1671,7 +1685,7 @@ name = "syntex_errors"
version = "0.42.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1693,7 +1707,7 @@ version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1706,7 +1720,7 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1747,7 +1761,7 @@ name = "termios"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1756,7 +1770,7 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1773,7 +1787,7 @@ version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1912,7 +1926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "ws" name = "ws"
version = "0.5.3" version = "0.5.3"
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#0cd6c5e3e9d5e61a37d53eb8dcbad523dcc69314" source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#f5c0b35d660244d1b7500693c8cc28277ce1d418"
dependencies = [ dependencies = [
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)", "bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2012,7 +2026,7 @@ dependencies = [
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "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 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 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 libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
"checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48" "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 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 log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
@ -2024,8 +2038,8 @@ dependencies = [
"checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54" "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 (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.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
"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 mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "<none>"
"checksum mio 0.6.1 (git+https://github.com/carllerche/mio)" = "<none>"
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a" "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 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>" "checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
@ -2033,6 +2047,7 @@ dependencies = [
"checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204" "checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204"
"checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e" "checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e"
"checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2" "checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2"
"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b"
"checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f" "checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f"
"checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e" "checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e"
"checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2" "checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2"

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Ethcore client." description = "Ethcore client."
name = "parity" name = "parity"
version = "1.4.0" version = "1.5.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io>"] authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs" build = "build.rs"

View File

@ -1,9 +1,12 @@
# [Parity](https://ethcore.io/parity.html) # [Parity](https://ethcore.io/parity.html)
### Fast, light, and robust Ethereum implementation ### 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] [![build status](https://gitlab.ethcore.io/Mirrors/ethcore-parity/badges/master/build.svg)](https://gitlab.ethcore.io/Mirrors/ethcore-parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
[![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] ### Join the chat!
Parity [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] and
parity.js [![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)
[Internal Documentation][doc-url] [Internal Documentation][doc-url]
@ -21,7 +24,7 @@ Be sure to check out [our wiki][wiki-url] for more information.
[doc-url]: https://ethcore.github.io/parity/ethcore/index.html [doc-url]: https://ethcore.github.io/parity/ethcore/index.html
[wiki-url]: https://github.com/ethcore/parity/wiki [wiki-url]: https://github.com/ethcore/parity/wiki
**Requires Rust version 1.12.0 to build** **Parity requires Rust version 1.12.0 to build**
---- ----
@ -31,12 +34,15 @@ Be sure to check out [our wiki][wiki-url] for more information.
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs. cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
By default, Parity will run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number Parity comes with a built-in wallet. To access [Parity Wallet](http://127.0.0.1:8080/) this simply go to http://127.0.0.1:8080/. It
of RPC APIs. includes various functionality allowing you to:
- create and manage your Ethereum accounts;
- manage your Ether and any Ethereum tokens;
- create and register your own tokens;
- and much more.
Parity also runs a server for running decentralized apps, or "Dapps", on `http://127.0.0.1:8080`. By default, Parity will also run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number
This includes a few useful Dapps, including Ethereum Wallet, Maker OTC, and a node status page. of RPC APIs.
In a near-future release, it will be easy to install Dapps and use them through this web interface.
If you run into an issue while using parity, feel free to file one in this repository If you run into an issue while using parity, feel free to file one in this repository
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help! or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
@ -56,7 +62,14 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
```bash ```bash
$ curl https://sh.rustup.rs -sSf | sh $ curl https://sh.rustup.rs -sSf | sh
``` ```
Parity also requires `gcc`, `g++` and `make` packages to be installed.
- OSX:
```bash
$ curl https://sh.rustup.rs -sSf | sh
```
`clang` and `make` are required. These come with Xcode command line tools or can be installed with homebrew.
- Windows - Windows
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from

View File

@ -38,6 +38,8 @@ after_test:
- cargo build --verbose --release - cargo build --verbose --release
- ps: if($env:cert) { Start-FileDownload $env:cert -FileName $env:keyfile } - ps: if($env:cert) { Start-FileDownload $env:cert -FileName $env:keyfile }
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass target\release\parity.exe } - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass target\release\parity.exe }
- msbuild windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass windows\ptray\x64\release\ptray.exe }
- makensis.exe nsis\installer.nsi - makensis.exe nsis\installer.nsi
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass nsis\installer.exe } - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass nsis\installer.exe }

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity Dapps crate" description = "Parity Dapps crate"
name = "ethcore-dapps" name = "ethcore-dapps"
version = "1.4.0" version = "1.5.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io"] authors = ["Ethcore <admin@ethcore.io"]
build = "build.rs" build = "build.rs"
@ -24,6 +24,7 @@ ethabi = "0.2.2"
linked-hash-map = "0.3" linked-hash-map = "0.3"
parity-dapps-glue = "1.4" parity-dapps-glue = "1.4"
mime = "0.2" mime = "0.2"
time = "0.1.35"
serde_macros = { version = "0.8", optional = true } serde_macros = { version = "0.8", optional = true }
zip = { version = "0.1", default-features = false } zip = { version = "0.1", default-features = false }
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }

View File

@ -32,21 +32,22 @@ use random_filename;
use SyncStatus; use SyncStatus;
use util::{Mutex, H256}; use util::{Mutex, H256};
use util::sha3::sha3; use util::sha3::sha3;
use page::LocalPageEndpoint; use page::{LocalPageEndpoint, PageCache};
use handlers::{ContentHandler, ContentFetcherHandler, ContentValidator}; use handlers::{ContentHandler, ContentFetcherHandler, ContentValidator};
use endpoint::{Endpoint, EndpointPath, Handler}; use endpoint::{Endpoint, EndpointPath, Handler};
use apps::cache::{ContentCache, ContentStatus}; use apps::cache::{ContentCache, ContentStatus};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest}; use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
use apps::urlhint::{URLHintContract, URLHint, URLHintResult}; use apps::urlhint::{URLHintContract, URLHint, URLHintResult};
const MAX_CACHED_DAPPS: usize = 10; /// Limit of cached dapps/content
const MAX_CACHED_DAPPS: usize = 20;
pub struct ContentFetcher<R: URLHint = URLHintContract> { pub struct ContentFetcher<R: URLHint = URLHintContract> {
dapps_path: PathBuf, dapps_path: PathBuf,
resolver: R, resolver: R,
cache: Arc<Mutex<ContentCache>>, cache: Arc<Mutex<ContentCache>>,
sync: Arc<SyncStatus>, sync: Arc<SyncStatus>,
embeddable_at: Option<u16>, embeddable_on: Option<(String, u16)>,
} }
impl<R: URLHint> Drop for ContentFetcher<R> { impl<R: URLHint> Drop for ContentFetcher<R> {
@ -58,7 +59,7 @@ impl<R: URLHint> Drop for ContentFetcher<R> {
impl<R: URLHint> ContentFetcher<R> { impl<R: URLHint> ContentFetcher<R> {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_at: Option<u16>) -> Self { pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_on: Option<(String, u16)>) -> Self {
let mut dapps_path = env::temp_dir(); let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename()); dapps_path.push(random_filename());
@ -67,16 +68,17 @@ impl<R: URLHint> ContentFetcher<R> {
resolver: resolver, resolver: resolver,
sync: sync_status, sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())), cache: Arc::new(Mutex::new(ContentCache::default())),
embeddable_at: embeddable_at, embeddable_on: embeddable_on,
} }
} }
fn still_syncing() -> Box<Handler> { fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
Box::new(ContentHandler::error( Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable, StatusCode::ServiceUnavailable,
"Sync In Progress", "Sync In Progress",
"Your node is still syncing. We cannot resolve any content before it's fully synced.", "Your node is still syncing. We cannot resolve any content before it's fully synced.",
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>") Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
address,
)) ))
} }
@ -143,19 +145,19 @@ impl<R: URLHint> ContentFetcher<R> {
match content { match content {
// Don't serve dapps if we are still syncing (but serve content) // Don't serve dapps if we are still syncing (but serve content)
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => { Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
(None, Self::still_syncing()) (None, Self::still_syncing(self.embeddable_on.clone()))
}, },
Some(URLHintResult::Dapp(dapp)) => { Some(URLHintResult::Dapp(dapp)) => {
let (handler, fetch_control) = ContentFetcherHandler::new( let (handler, fetch_control) = ContentFetcherHandler::new(
dapp.url(), dapp.url(),
control, control,
path.using_dapps_domains,
DappInstaller { DappInstaller {
id: content_id.clone(), id: content_id.clone(),
dapps_path: self.dapps_path.clone(), dapps_path: self.dapps_path.clone(),
on_done: Box::new(on_done), on_done: Box::new(on_done),
embeddable_at: self.embeddable_at, embeddable_on: self.embeddable_on.clone(),
} },
self.embeddable_on.clone(),
); );
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>) (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
@ -164,19 +166,19 @@ impl<R: URLHint> ContentFetcher<R> {
let (handler, fetch_control) = ContentFetcherHandler::new( let (handler, fetch_control) = ContentFetcherHandler::new(
content.url, content.url,
control, control,
path.using_dapps_domains,
ContentInstaller { ContentInstaller {
id: content_id.clone(), id: content_id.clone(),
mime: content.mime, mime: content.mime,
content_path: self.dapps_path.clone(), content_path: self.dapps_path.clone(),
on_done: Box::new(on_done), on_done: Box::new(on_done),
} },
self.embeddable_on.clone(),
); );
(Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>) (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box<Handler>)
}, },
None if self.sync.is_major_importing() => { None if self.sync.is_major_importing() => {
(None, Self::still_syncing()) (None, Self::still_syncing(self.embeddable_on.clone()))
}, },
None => { None => {
// This may happen when sync status changes in between // This may happen when sync status changes in between
@ -185,7 +187,8 @@ impl<R: URLHint> ContentFetcher<R> {
StatusCode::NotFound, StatusCode::NotFound,
"Resource Not Found", "Resource Not Found",
"Requested resource was not found.", "Requested resource was not found.",
None None,
self.embeddable_on.clone(),
)) as Box<Handler>) )) as Box<Handler>)
}, },
} }
@ -255,6 +258,17 @@ impl ContentValidator for ContentInstaller {
// Create dir // Create dir
try!(fs::create_dir_all(&self.content_path)); try!(fs::create_dir_all(&self.content_path));
// Validate hash
let mut file_reader = io::BufReader::new(try!(fs::File::open(&path)));
let hash = try!(sha3(&mut file_reader));
let id = try!(self.id.as_str().parse().map_err(|_| ValidationError::InvalidContentId));
if id != hash {
return Err(ValidationError::HashMismatch {
expected: id,
got: hash,
});
}
// And prepare path for a file // And prepare path for a file
let filename = path.file_name().expect("We always fetch a file."); let filename = path.file_name().expect("We always fetch a file.");
let mut content_path = self.content_path.clone(); let mut content_path = self.content_path.clone();
@ -266,7 +280,7 @@ impl ContentValidator for ContentInstaller {
try!(fs::copy(&path, &content_path)); try!(fs::copy(&path, &content_path));
Ok((self.id.clone(), LocalPageEndpoint::single_file(content_path, self.mime.clone()))) Ok((self.id.clone(), LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled)))
} }
fn done(&self, endpoint: Option<LocalPageEndpoint>) { fn done(&self, endpoint: Option<LocalPageEndpoint>) {
@ -279,7 +293,7 @@ struct DappInstaller {
id: String, id: String,
dapps_path: PathBuf, dapps_path: PathBuf,
on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>, on_done: Box<Fn(String, Option<LocalPageEndpoint>) + Send>,
embeddable_at: Option<u16>, embeddable_on: Option<(String, u16)>,
} }
impl DappInstaller { impl DappInstaller {
@ -372,7 +386,7 @@ impl ContentValidator for DappInstaller {
try!(manifest_file.write_all(manifest_str.as_bytes())); try!(manifest_file.write_all(manifest_str.as_bytes()));
// Create endpoint // Create endpoint
let app = LocalPageEndpoint::new(target, manifest.clone().into(), self.embeddable_at); let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone());
// Return modified app manifest // Return modified app manifest
Ok((manifest.id.clone(), app)) Ok((manifest.id.clone(), app))
@ -412,7 +426,7 @@ mod tests {
version: "".into(), version: "".into(),
author: "".into(), author: "".into(),
icon_url: "".into(), icon_url: "".into(),
}, None); }, Default::default(), None);
// when // when
fetcher.set_status("test", ContentStatus::Ready(handler)); fetcher.set_status("test", ContentStatus::Ready(handler));

View File

@ -18,7 +18,7 @@ use std::io;
use std::io::Read; use std::io::Read;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use page::LocalPageEndpoint; use page::{LocalPageEndpoint, PageCache};
use endpoint::{Endpoints, EndpointInfo}; use endpoint::{Endpoints, EndpointInfo};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest}; use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
@ -97,12 +97,12 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
}) })
} }
pub fn local_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints { pub fn local_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints {
let mut pages = Endpoints::new(); let mut pages = Endpoints::new();
for dapp in local_dapps(dapps_path) { for dapp in local_dapps(dapps_path) {
pages.insert( pages.insert(
dapp.id, dapp.id,
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, signer_port)) Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
); );
} }
pages pages

View File

@ -33,38 +33,30 @@ pub const RPC_PATH : &'static str = "rpc";
pub const API_PATH : &'static str = "api"; pub const API_PATH : &'static str = "api";
pub const UTILS_PATH : &'static str = "parity-utils"; pub const UTILS_PATH : &'static str = "parity-utils";
pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String {
if using_dapps_domains {
format!("http://{}{}/", app_id, DAPPS_DOMAIN)
} else {
format!("/{}/", app_id)
}
}
pub fn utils() -> Box<Endpoint> { pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned())) Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
} }
pub fn all_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints { pub fn all_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins // fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path, signer_port); let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
// NOTE [ToDr] Dapps will be currently embeded on 8180 // NOTE [ToDr] Dapps will be currently embeded on 8180
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_port)); insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(signer_port)); pages.insert("proxy".into(), ProxyPac::boxed(signer_address));
pages pages
} }
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) { fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
pages.insert(id.to_owned(), Box::new(match embed_at { pages.insert(id.to_owned(), Box::new(match embed_at {
Embeddable::Yes(port) => PageEndpoint::new_safe_to_embed(T::default(), port), Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
Embeddable::No => PageEndpoint::new(T::default()), Embeddable::No => PageEndpoint::new(T::default()),
})); }));
} }
enum Embeddable { enum Embeddable {
Yes(Option<u16>), Yes(Option<(String, u16)>),
#[allow(dead_code)] #[allow(dead_code)]
No, No,
} }

View File

@ -32,7 +32,7 @@ pub struct ContentHandler {
content: String, content: String,
mimetype: Mime, mimetype: Mime,
write_pos: usize, write_pos: usize,
safe_to_embed_at_port: Option<u16>, safe_to_embed_on: Option<(String, u16)>,
} }
impl ContentHandler { impl ContentHandler {
@ -44,35 +44,31 @@ impl ContentHandler {
Self::new(StatusCode::NotFound, content, mimetype) Self::new(StatusCode::NotFound, content, mimetype)
} }
pub fn html(code: StatusCode, content: String, embeddable_at: Option<u16>) -> Self { pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at) Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
} }
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self { pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_on: Option<(String, u16)>) -> 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!( Self::html(code, format!(
include_str!("../error_tpl.html"), include_str!("../error_tpl.html"),
title=title, title=title,
message=message, message=message,
details=details.unwrap_or_else(|| ""), details=details.unwrap_or_else(|| ""),
version=version(), version=version(),
), embeddable_at) ), embeddable_on)
} }
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self { pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
Self::new_embeddable(code, content, mimetype, None) Self::new_embeddable(code, content, mimetype, None)
} }
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_at: Option<u16>) -> Self { pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
ContentHandler { ContentHandler {
code: code, code: code,
content: content, content: content,
mimetype: mimetype, mimetype: mimetype,
write_pos: 0, write_pos: 0,
safe_to_embed_at_port: embeddable_at, safe_to_embed_on: embeddable_on,
} }
} }
} }
@ -89,7 +85,7 @@ impl server::Handler<HttpStream> for ContentHandler {
fn on_response(&mut self, res: &mut server::Response) -> Next { fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(self.code); res.set_status(self.code);
res.headers_mut().set(header::ContentType(self.mimetype.clone())); res.headers_mut().set(header::ContentType(self.mimetype.clone()));
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port.clone()); add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
Next::write() Next::write()
} }

View File

@ -22,14 +22,14 @@ use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use util::Mutex; use util::Mutex;
use url::Url;
use fetch::{Client, Fetch, FetchResult}; use fetch::{Client, Fetch, FetchResult};
use hyper::{server, Decoder, Encoder, Next, Method, Control}; use hyper::{server, Decoder, Encoder, Next, Method, Control};
use hyper::net::HttpStream; use hyper::net::HttpStream;
use hyper::status::StatusCode; use hyper::status::StatusCode;
use handlers::{ContentHandler, Redirection}; use handlers::{ContentHandler, Redirection, extract_url};
use apps::redirection_address;
use page::LocalPageEndpoint; use page::LocalPageEndpoint;
const FETCH_TIMEOUT: u64 = 30; const FETCH_TIMEOUT: u64 = 30;
@ -136,8 +136,9 @@ pub struct ContentFetcherHandler<H: ContentValidator> {
control: Option<Control>, control: Option<Control>,
status: FetchState, status: FetchState,
client: Option<Client>, client: Option<Client>,
using_dapps_domains: bool,
installer: H, installer: H,
request_url: Option<Url>,
embeddable_on: Option<(String, u16)>,
} }
impl<H: ContentValidator> Drop for ContentFetcherHandler<H> { impl<H: ContentValidator> Drop for ContentFetcherHandler<H> {
@ -155,8 +156,9 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
pub fn new( pub fn new(
url: String, url: String,
control: Control, control: Control,
using_dapps_domains: bool, handler: H,
handler: H) -> (Self, Arc<FetchControl>) { embeddable_on: Option<(String, u16)>,
) -> (Self, Arc<FetchControl>) {
let fetch_control = Arc::new(FetchControl::default()); let fetch_control = Arc::new(FetchControl::default());
let client = Client::default(); let client = Client::default();
@ -165,8 +167,9 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
control: Some(control), control: Some(control),
client: Some(client), client: Some(client),
status: FetchState::NotStarted(url), status: FetchState::NotStarted(url),
using_dapps_domains: using_dapps_domains,
installer: handler, installer: handler,
request_url: None,
embeddable_on: embeddable_on,
}; };
(handler, fetch_control) (handler, fetch_control)
@ -189,6 +192,7 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<H> { impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<H> {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next { fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
self.request_url = extract_url(&request);
let status = if let FetchState::NotStarted(ref url) = self.status { let status = if let FetchState::NotStarted(ref url) = self.status {
Some(match *request.method() { Some(match *request.method() {
// Start fetching content // Start fetching content
@ -204,6 +208,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Unable To Start Dapp Download", "Unable To Start Dapp Download",
"Could not initialize download of the dapp. It might be a problem with the remote server.", "Could not initialize download of the dapp. It might be a problem with the remote server.",
Some(&format!("{}", e)), Some(&format!("{}", e)),
self.embeddable_on.clone(),
)), )),
} }
}, },
@ -213,6 +218,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Method Not Allowed", "Method Not Allowed",
"Only <code>GET</code> requests are allowed.", "Only <code>GET</code> requests are allowed.",
None, None,
self.embeddable_on.clone(),
)), )),
}) })
} else { None }; } else { None };
@ -234,7 +240,8 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
StatusCode::GatewayTimeout, StatusCode::GatewayTimeout,
"Download Timeout", "Download Timeout",
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT), &format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
None None,
self.embeddable_on.clone(),
); );
Self::close_client(&mut self.client); Self::close_client(&mut self.client);
(Some(FetchState::Error(timeout)), Next::write()) (Some(FetchState::Error(timeout)), Next::write())
@ -255,12 +262,15 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
StatusCode::BadGateway, StatusCode::BadGateway,
"Invalid Dapp", "Invalid Dapp",
"Downloaded bundle does not contain a valid content.", "Downloaded bundle does not contain a valid content.",
Some(&format!("{:?}", e)) Some(&format!("{:?}", e)),
self.embeddable_on.clone(),
)) ))
}, },
Ok((id, result)) => { Ok((id, result)) => {
let address = redirection_address(self.using_dapps_domains, &id); let url: String = self.request_url.take()
FetchState::Done(id, result, Redirection::new(&address)) .map(|url| url.raw.into_string())
.expect("Request URL always read in on_request; qed");
FetchState::Done(id, result, Redirection::new(&url))
}, },
}; };
// Remove temporary zip file // Remove temporary zip file
@ -274,6 +284,7 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
"Download Error", "Download Error",
"There was an error when fetching the content.", "There was an error when fetching the content.",
Some(&format!("{:?}", e)), Some(&format!("{:?}", e)),
self.embeddable_on.clone(),
); );
(Some(FetchState::Error(error)), Next::write()) (Some(FetchState::Error(error)), Next::write())
}, },

View File

@ -30,18 +30,18 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl};
use url::Url; use url::Url;
use hyper::{server, header, net, uri}; use hyper::{server, header, net, uri};
use signer_address; use address;
/// Adds security-related headers to the Response. /// Adds security-related headers to the Response.
pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option<u16>) { pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option<(String, u16)>) {
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]); 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()]); headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
// Embedding header: // Embedding header:
if let Some(port) = embeddable_at { if let Some(embeddable_on) = embeddable_on {
headers.set_raw( headers.set_raw(
"X-Frame-Options", "X-Frame-Options",
vec![format!("ALLOW-FROM http://{}", signer_address(port)).into_bytes()] vec![format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()]
); );
} else { } else {
// TODO [ToDr] Should we be more strict here (DENY?)? // TODO [ToDr] Should we be more strict here (DENY?)?

View File

@ -44,6 +44,7 @@
#![cfg_attr(feature="nightly", plugin(clippy))] #![cfg_attr(feature="nightly", plugin(clippy))]
extern crate hyper; extern crate hyper;
extern crate time;
extern crate url as url_lib; extern crate url as url_lib;
extern crate unicase; extern crate unicase;
extern crate serde; extern crate serde;
@ -111,7 +112,7 @@ pub struct ServerBuilder {
handler: Arc<IoHandler>, handler: Arc<IoHandler>,
registrar: Arc<ContractClient>, registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>, sync_status: Arc<SyncStatus>,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
} }
impl Extendable for ServerBuilder { impl Extendable for ServerBuilder {
@ -128,7 +129,7 @@ impl ServerBuilder {
handler: Arc::new(IoHandler::new()), handler: Arc::new(IoHandler::new()),
registrar: registrar, registrar: registrar,
sync_status: Arc::new(|| false), sync_status: Arc::new(|| false),
signer_port: None, signer_address: None,
} }
} }
@ -138,8 +139,8 @@ impl ServerBuilder {
} }
/// Change default signer port. /// Change default signer port.
pub fn with_signer_port(&mut self, signer_port: Option<u16>) { pub fn with_signer_address(&mut self, signer_address: Option<(String, u16)>) {
self.signer_port = signer_port; self.signer_address = signer_address;
} }
/// Asynchronously start server with no authentication, /// Asynchronously start server with no authentication,
@ -151,7 +152,7 @@ impl ServerBuilder {
NoAuth, NoAuth,
self.handler.clone(), self.handler.clone(),
self.dapps_path.clone(), self.dapps_path.clone(),
self.signer_port.clone(), self.signer_address.clone(),
self.registrar.clone(), self.registrar.clone(),
self.sync_status.clone(), self.sync_status.clone(),
) )
@ -166,7 +167,7 @@ impl ServerBuilder {
HttpBasicAuth::single_user(username, password), HttpBasicAuth::single_user(username, password),
self.handler.clone(), self.handler.clone(),
self.dapps_path.clone(), self.dapps_path.clone(),
self.signer_port.clone(), self.signer_address.clone(),
self.registrar.clone(), self.registrar.clone(),
self.sync_status.clone(), self.sync_status.clone(),
) )
@ -196,11 +197,11 @@ impl Server {
} }
/// Returns a list of CORS domains for API endpoint. /// Returns a list of CORS domains for API endpoint.
fn cors_domains(signer_port: Option<u16>) -> Vec<String> { fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
match signer_port { match signer_address {
Some(port) => vec![ Some(signer_address) => vec![
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN), format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("http://{}", signer_address(port)), format!("http://{}", address(signer_address)),
], ],
None => vec![], None => vec![],
} }
@ -212,15 +213,15 @@ impl Server {
authorization: A, authorization: A,
handler: Arc<IoHandler>, handler: Arc<IoHandler>,
dapps_path: String, dapps_path: String,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
registrar: Arc<ContractClient>, registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>, sync_status: Arc<SyncStatus>,
) -> Result<Server, ServerError> { ) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None)); let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization); let authorization = Arc::new(authorization);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_port)); let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_address.clone()));
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_port.clone())); let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone()));
let cors_domains = Self::cors_domains(signer_port); let cors_domains = Self::cors_domains(signer_address.clone());
let special = Arc::new({ let special = Arc::new({
let mut special = HashMap::new(); let mut special = HashMap::new();
@ -237,7 +238,7 @@ impl Server {
try!(hyper::Server::http(addr)) try!(hyper::Server::http(addr))
.handle(move |ctrl| router::Router::new( .handle(move |ctrl| router::Router::new(
ctrl, ctrl,
signer_port.clone(), signer_address.clone(),
content_fetcher.clone(), content_fetcher.clone(),
endpoints.clone(), endpoints.clone(),
special.clone(), special.clone(),
@ -301,8 +302,8 @@ pub fn random_filename() -> String {
rng.gen_ascii_chars().take(12).collect() rng.gen_ascii_chars().take(12).collect()
} }
fn signer_address(port: u16) -> String { fn address(address: (String, u16)) -> String {
format!("127.0.0.1:{}", port) format!("{}:{}", address.0, address.1)
} }
#[cfg(test)] #[cfg(test)]
@ -331,7 +332,7 @@ mod util_tests {
// when // when
let none = Server::cors_domains(None); let none = Server::cors_domains(None);
let some = Server::cors_domains(Some(18180)); let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
// then // then
assert_eq!(none, Vec::<String>::new()); assert_eq!(none, Vec::<String>::new());

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use page::handler; use page::{handler, PageCache};
use std::sync::Arc; use std::sync::Arc;
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler}; use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
use parity_dapps::{WebApp, File, Info}; use parity_dapps::{WebApp, File, Info};
@ -25,7 +25,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
/// Prefix to strip from the path (when `None` deducted from `app_id`) /// Prefix to strip from the path (when `None` deducted from `app_id`)
pub prefix: Option<String>, pub prefix: Option<String>,
/// Safe to be loaded in frame by other origin. (use wisely!) /// Safe to be loaded in frame by other origin. (use wisely!)
safe_to_embed_at_port: Option<u16>, safe_to_embed_on: Option<(String, u16)>,
info: EndpointInfo, info: EndpointInfo,
} }
@ -36,7 +36,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
PageEndpoint { PageEndpoint {
app: Arc::new(app), app: Arc::new(app),
prefix: None, prefix: None,
safe_to_embed_at_port: None, safe_to_embed_on: None,
info: EndpointInfo::from(info), info: EndpointInfo::from(info),
} }
} }
@ -49,7 +49,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
PageEndpoint { PageEndpoint {
app: Arc::new(app), app: Arc::new(app),
prefix: Some(prefix), prefix: Some(prefix),
safe_to_embed_at_port: None, safe_to_embed_on: None,
info: EndpointInfo::from(info), info: EndpointInfo::from(info),
} }
} }
@ -57,12 +57,12 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
/// Creates new `PageEndpoint` which can be safely used in iframe /// Creates new `PageEndpoint` which can be safely used in iframe
/// even from different origin. It might be dangerous (clickjacking). /// even from different origin. It might be dangerous (clickjacking).
/// Use wisely! /// Use wisely!
pub fn new_safe_to_embed(app: T, port: Option<u16>) -> Self { pub fn new_safe_to_embed(app: T, address: Option<(String, u16)>) -> Self {
let info = app.info(); let info = app.info();
PageEndpoint { PageEndpoint {
app: Arc::new(app), app: Arc::new(app),
prefix: None, prefix: None,
safe_to_embed_at_port: port, safe_to_embed_on: address,
info: EndpointInfo::from(info), info: EndpointInfo::from(info),
} }
} }
@ -79,8 +79,9 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
app: BuiltinDapp::new(self.app.clone()), app: BuiltinDapp::new(self.app.clone()),
prefix: self.prefix.clone(), prefix: self.prefix.clone(),
path: path, path: path,
file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()), file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
safe_to_embed_at_port: self.safe_to_embed_at_port.clone(), cache: PageCache::Disabled,
safe_to_embed_on: self.safe_to_embed_on.clone(),
}) })
} }
} }

View File

@ -15,6 +15,8 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::io::Write; use std::io::Write;
use time::{self, Duration};
use hyper::header; use hyper::header;
use hyper::server; use hyper::server;
use hyper::uri::RequestUri; use hyper::uri::RequestUri;
@ -58,17 +60,30 @@ pub enum ServedFile<T: Dapp> {
} }
impl<T: Dapp> ServedFile<T> { impl<T: Dapp> ServedFile<T> {
pub fn new(embeddable_at: Option<u16>) -> Self { pub fn new(embeddable_on: Option<(String, u16)>) -> Self {
ServedFile::Error(ContentHandler::error_embeddable( ServedFile::Error(ContentHandler::error(
StatusCode::NotFound, StatusCode::NotFound,
"404 Not Found", "404 Not Found",
"Requested dapp resource was not found.", "Requested dapp resource was not found.",
None, None,
embeddable_at, embeddable_on,
)) ))
} }
} }
/// Defines what cache headers should be appended to returned resources.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PageCache {
Enabled,
Disabled,
}
impl Default for PageCache {
fn default() -> Self {
PageCache::Disabled
}
}
/// A handler for a single webapp. /// A handler for a single webapp.
/// Resolves correct paths and serves as a plumbing code between /// Resolves correct paths and serves as a plumbing code between
/// hyper server and dapp. /// hyper server and dapp.
@ -82,7 +97,9 @@ pub struct PageHandler<T: Dapp> {
/// Requested path. /// Requested path.
pub path: EndpointPath, pub path: EndpointPath,
/// Flag indicating if the file can be safely embeded (put in iframe). /// Flag indicating if the file can be safely embeded (put in iframe).
pub safe_to_embed_at_port: Option<u16>, pub safe_to_embed_on: Option<(String, u16)>,
/// Cache settings for this page.
pub cache: PageCache,
} }
impl<T: Dapp> PageHandler<T> { impl<T: Dapp> PageHandler<T> {
@ -116,7 +133,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
self.app.file(&self.extract_path(url.path())) self.app.file(&self.extract_path(url.path()))
}, },
_ => None, _ => None,
}.map_or_else(|| ServedFile::new(self.safe_to_embed_at_port.clone()), |f| ServedFile::File(f)); }.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f));
Next::write() Next::write()
} }
@ -129,13 +146,23 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
ServedFile::File(ref f) => { ServedFile::File(ref f) => {
res.set_status(StatusCode::Ok); res.set_status(StatusCode::Ok);
if let PageCache::Enabled = self.cache {
let mut headers = res.headers_mut();
let validity = Duration::days(365);
headers.set(header::CacheControl(vec![
header::CacheDirective::Public,
header::CacheDirective::MaxAge(validity.num_seconds() as u32),
]));
headers.set(header::Expires(header::HttpDate(time::now() + validity)));
}
match f.content_type().parse() { match f.content_type().parse() {
Ok(mime) => res.headers_mut().set(header::ContentType(mime)), Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
Err(()) => debug!(target: "page_handler", "invalid MIME type: {}", f.content_type()), Err(()) => debug!(target: "dapps", "invalid MIME type: {}", f.content_type()),
} }
// Security headers: // Security headers:
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port); add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
Next::write() Next::write()
}, },
ServedFile::Error(ref mut handler) => { ServedFile::Error(ref mut handler) => {
@ -218,7 +245,8 @@ fn should_extract_path_with_appid() {
using_dapps_domains: true, using_dapps_domains: true,
}, },
file: ServedFile::new(None), file: ServedFile::new(None),
safe_to_embed_at_port: None, cache: Default::default(),
safe_to_embed_on: None,
}; };
// when // when

View File

@ -18,7 +18,7 @@ use mime_guess;
use std::io::{Seek, Read, SeekFrom}; use std::io::{Seek, Read, SeekFrom};
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use page::handler; use page::handler::{self, PageCache};
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler}; use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -26,25 +26,28 @@ pub struct LocalPageEndpoint {
path: PathBuf, path: PathBuf,
mime: Option<String>, mime: Option<String>,
info: Option<EndpointInfo>, info: Option<EndpointInfo>,
embeddable_at: Option<u16>, cache: PageCache,
embeddable_on: Option<(String, u16)>,
} }
impl LocalPageEndpoint { impl LocalPageEndpoint {
pub fn new(path: PathBuf, info: EndpointInfo, embeddable_at: Option<u16>) -> Self { pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self {
LocalPageEndpoint { LocalPageEndpoint {
path: path, path: path,
mime: None, mime: None,
info: Some(info), info: Some(info),
embeddable_at: embeddable_at, cache: cache,
embeddable_on: embeddable_on,
} }
} }
pub fn single_file(path: PathBuf, mime: String) -> Self { pub fn single_file(path: PathBuf, mime: String, cache: PageCache) -> Self {
LocalPageEndpoint { LocalPageEndpoint {
path: path, path: path,
mime: Some(mime), mime: Some(mime),
info: None, info: None,
embeddable_at: None, cache: cache,
embeddable_on: None,
} }
} }
@ -65,7 +68,8 @@ impl Endpoint for LocalPageEndpoint {
prefix: None, prefix: None,
path: path, path: path,
file: handler::ServedFile::new(None), file: handler::ServedFile::new(None),
safe_to_embed_at_port: self.embeddable_at, safe_to_embed_on: self.embeddable_on.clone(),
cache: self.cache,
}) })
} else { } else {
Box::new(handler::PageHandler { Box::new(handler::PageHandler {
@ -73,7 +77,8 @@ impl Endpoint for LocalPageEndpoint {
prefix: None, prefix: None,
path: path, path: path,
file: handler::ServedFile::new(None), file: handler::ServedFile::new(None),
safe_to_embed_at_port: self.embeddable_at, safe_to_embed_on: self.embeddable_on.clone(),
cache: self.cache,
}) })
} }
} }

View File

@ -21,4 +21,5 @@ mod handler;
pub use self::local::LocalPageEndpoint; pub use self::local::LocalPageEndpoint;
pub use self::builtin::PageEndpoint; pub use self::builtin::PageEndpoint;
pub use self::handler::PageCache;

View File

@ -19,24 +19,24 @@
use endpoint::{Endpoint, Handler, EndpointPath}; use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler; use handlers::ContentHandler;
use apps::{HOME_PAGE, DAPPS_DOMAIN}; use apps::{HOME_PAGE, DAPPS_DOMAIN};
use signer_address; use address;
pub struct ProxyPac { pub struct ProxyPac {
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
} }
impl ProxyPac { impl ProxyPac {
pub fn boxed(signer_port: Option<u16>) -> Box<Endpoint> { pub fn boxed(signer_address: Option<(String, u16)>) -> Box<Endpoint> {
Box::new(ProxyPac { Box::new(ProxyPac {
signer_port: signer_port signer_address: signer_address
}) })
} }
} }
impl Endpoint for ProxyPac { impl Endpoint for ProxyPac {
fn to_handler(&self, path: EndpointPath) -> Box<Handler> { fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
let signer = self.signer_port let signer = self.signer_address.clone()
.map(signer_address) .map(address)
.unwrap_or_else(|| format!("{}:{}", path.host, path.port)); .unwrap_or_else(|| format!("{}:{}", path.host, path.port));
let content = format!( let content = format!(

View File

@ -59,7 +59,8 @@ impl Authorization for HttpBasicAuth {
status::StatusCode::Unauthorized, status::StatusCode::Unauthorized,
"Unauthorized", "Unauthorized",
"You need to provide valid credentials to access this page.", "You need to provide valid credentials to access this page.",
None None,
None,
))) )))
}, },
Access::AuthRequired => { Access::AuthRequired => {

View File

@ -41,6 +41,7 @@ pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
Box::new(ContentHandler::error(StatusCode::Forbidden, Box::new(ContentHandler::error(StatusCode::Forbidden,
"Current Host Is Disallowed", "Current Host Is Disallowed",
"You are trying to access your node using incorrect address.", "You are trying to access your node using incorrect address.",
Some("Use allowed URL or specify different <code>hosts</code> CLI options.") Some("Use allowed URL or specify different <code>hosts</code> CLI options."),
None,
)) ))
} }

View File

@ -20,7 +20,7 @@
pub mod auth; pub mod auth;
mod host_validation; mod host_validation;
use signer_address; use address;
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use url::{Url, Host}; use url::{Url, Host};
@ -43,7 +43,7 @@ pub enum SpecialEndpoint {
pub struct Router<A: Authorization + 'static> { pub struct Router<A: Authorization + 'static> {
control: Option<Control>, control: Option<Control>,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
fetch: Arc<ContentFetcher>, fetch: Arc<ContentFetcher>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -117,20 +117,22 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
"404 Not Found", "404 Not Found",
"Requested content was not found.", "Requested content was not found.",
None, None,
self.signer_address.clone(),
)) ))
}, },
// Redirect any other GET request to signer. // Redirect any other GET request to signer.
_ if *req.method() == hyper::Method::Get => { _ if *req.method() == hyper::Method::Get => {
if let Some(port) = self.signer_port { if let Some(signer_address) = self.signer_address.clone() {
trace!(target: "dapps", "Redirecting to signer interface."); trace!(target: "dapps", "Redirecting to signer interface.");
Redirection::boxed(&format!("http://{}", signer_address(port))) Redirection::boxed(&format!("http://{}", address(signer_address)))
} else { } else {
trace!(target: "dapps", "Signer disabled, returning 404."); trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(ContentHandler::error( Box::new(ContentHandler::error(
StatusCode::NotFound, StatusCode::NotFound,
"404 Not Found", "404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.", "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."), Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
self.signer_address.clone(),
)) ))
} }
}, },
@ -166,7 +168,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
impl<A: Authorization> Router<A> { impl<A: Authorization> Router<A> {
pub fn new( pub fn new(
control: Control, control: Control,
signer_port: Option<u16>, signer_address: Option<(String, u16)>,
content_fetcher: Arc<ContentFetcher>, content_fetcher: Arc<ContentFetcher>,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
@ -179,7 +181,7 @@ impl<A: Authorization> Router<A> {
.to_handler(EndpointPath::default()); .to_handler(EndpointPath::default());
Router { Router {
control: Some(control), control: Some(control),
signer_port: signer_port, signer_address: signer_address,
endpoints: endpoints, endpoints: endpoints,
fetch: content_fetcher, fetch: content_fetcher,
special: special, special: special,

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers}; use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers_for_embed};
#[test] #[test]
fn should_resolve_dapp() { fn should_resolve_dapp() {
@ -34,7 +34,7 @@ fn should_resolve_dapp() {
// then // then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert_eq!(registrar.calls.lock().len(), 2); assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers(&response.headers); assert_security_headers_for_embed(&response.headers);
} }
#[test] #[test]
@ -63,5 +63,5 @@ fn should_return_503_when_syncing_but_should_make_the_calls() {
// then // then
assert_eq!(response.status, "HTTP/1.1 503 Service Unavailable".to_owned()); assert_eq!(response.status, "HTTP/1.1 503 Service Unavailable".to_owned());
assert_eq!(registrar.calls.lock().len(), 4); assert_eq!(registrar.calls.lock().len(), 4);
assert_security_headers(&response.headers); assert_security_headers_for_embed(&response.headers);
} }

View File

@ -76,7 +76,7 @@ pub fn init_server(hosts: Option<Vec<String>>, is_syncing: bool) -> (Server, Arc
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let mut 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_sync_status(Arc::new(move || is_syncing));
builder.with_signer_port(Some(SIGNER_PORT)); builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)));
( (
builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(), builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(),
registrar, registrar,
@ -89,7 +89,7 @@ pub fn serve_with_auth(user: &str, pass: &str) -> Server {
let mut dapps_path = env::temp_dir(); let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let mut 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.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)));
builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap() builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
} }

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use tests::helpers::{serve, request, assert_security_headers}; use tests::helpers::{serve, request, assert_security_headers, assert_security_headers_for_embed};
#[test] #[test]
fn should_redirect_to_home() { fn should_redirect_to_home() {
@ -93,7 +93,7 @@ fn should_display_404_on_invalid_dapp() {
// then // then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert_security_headers(&response.headers); assert_security_headers_for_embed(&response.headers);
} }
#[test] #[test]
@ -113,7 +113,7 @@ fn should_display_404_on_invalid_dapp_with_domain() {
// then // then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert_security_headers(&response.headers); assert_security_headers_for_embed(&response.headers);
} }
#[test] #[test]

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::thread;
use std::time::Duration; use std::time::Duration;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::str::{self, Lines}; use std::str::{self, Lines};
@ -42,8 +43,28 @@ pub fn read_block(lines: &mut Lines, all: bool) -> String {
block block
} }
fn connect(address: &SocketAddr) -> TcpStream {
let mut retries = 0;
let mut last_error = None;
while retries < 10 {
retries += 1;
let res = TcpStream::connect(address);
match res {
Ok(stream) => {
return stream;
},
Err(e) => {
last_error = Some(e);
thread::sleep(Duration::from_millis(retries * 10));
}
}
}
panic!("Unable to connect to the server. Last error: {:?}", last_error);
}
pub fn request(address: &SocketAddr, request: &str) -> Response { pub fn request(address: &SocketAddr, request: &str) -> Response {
let mut req = TcpStream::connect(address).unwrap(); let mut req = connect(address);
req.set_read_timeout(Some(Duration::from_secs(1))).unwrap(); req.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
req.write_all(request.as_bytes()).unwrap(); req.write_all(request.as_bytes()).unwrap();

View File

@ -3,7 +3,7 @@ description = "Ethcore library"
homepage = "http://ethcore.io" homepage = "http://ethcore.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore" name = "ethcore"
version = "1.4.0" version = "1.5.0"
authors = ["Ethcore <admin@ethcore.io>"] authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs" build = "build.rs"

16
ethcore/light/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
description = "Parity LES primitives"
homepage = "https://ethcore.io"
license = "GPL-3.0"
name = "ethcore-light"
version = "1.5.0"
authors = ["Ethcore <admin@ethcore.io>"]
[dependencies]
log = "0.3"
ethcore = { path = ".." }
ethcore-util = { path = "../../util" }
ethcore-network = { path = "../../util/network" }
ethcore-io = { path = "../../util/io" }
rlp = { path = "../../util/rlp" }
time = "0.1"

115
ethcore/light/src/client.rs Normal file
View File

@ -0,0 +1,115 @@
// 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/>.
//! Light client implementation. Used for raw data queries as well as the header
//! sync.
use std::sync::Arc;
use ethcore::engines::Engine;
use ethcore::ids::BlockID;
use ethcore::service::ClientIoMessage;
use ethcore::block_import_error::BlockImportError;
use ethcore::block_status::BlockStatus;
use ethcore::verification::queue::{HeaderQueue, QueueInfo};
use ethcore::transaction::SignedTransaction;
use ethcore::blockchain_info::BlockChainInfo;
use io::IoChannel;
use util::hash::H256;
use util::{Bytes, Mutex};
use provider::Provider;
use request;
/// Light client implementation.
pub struct Client {
engine: Arc<Engine>,
header_queue: HeaderQueue,
message_channel: Mutex<IoChannel<ClientIoMessage>>,
}
impl Client {
/// Import a header as rlp-encoded bytes.
pub fn import_header(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
let header = ::rlp::decode(&bytes);
self.header_queue.import(header).map_err(Into::into)
}
/// Whether the block is already known (but not necessarily part of the canonical chain)
pub fn is_known(&self, _id: BlockID) -> bool {
false
}
/// Fetch a vector of all pending transactions.
pub fn pending_transactions(&self) -> Vec<SignedTransaction> {
vec![]
}
/// Inquire about the status of a given block.
pub fn status(&self, _id: BlockID) -> BlockStatus {
BlockStatus::Unknown
}
/// Get the header queue info.
pub fn queue_info(&self) -> QueueInfo {
self.header_queue.queue_info()
}
}
// dummy implementation -- may draw from canonical cache further on.
impl Provider for Client {
fn chain_info(&self) -> BlockChainInfo {
unimplemented!()
}
fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option<u64> {
None
}
fn earliest_state(&self) -> Option<u64> {
None
}
fn block_headers(&self, _req: request::Headers) -> Vec<Bytes> {
Vec::new()
}
fn block_bodies(&self, _req: request::Bodies) -> Vec<Bytes> {
Vec::new()
}
fn receipts(&self, _req: request::Receipts) -> Vec<Bytes> {
Vec::new()
}
fn proofs(&self, _req: request::StateProofs) -> Vec<Bytes> {
Vec::new()
}
fn code(&self, _req: request::ContractCodes) -> Vec<Bytes> {
Vec::new()
}
fn header_proofs(&self, _req: request::HeaderProofs) -> Vec<Bytes> {
Vec::new()
}
fn pending_transactions(&self) -> Vec<SignedTransaction> {
Vec::new()
}
}

47
ethcore/light/src/lib.rs Normal file
View File

@ -0,0 +1,47 @@
// 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/>.
//! Light client logic and implementation.
//!
//! A "light" client stores very little chain-related data locally
//! unlike a full node, which stores all blocks, headers, receipts, and more.
//!
//! This enables the client to have a much lower resource footprint in
//! exchange for the cost of having to ask the network for state data
//! while responding to queries. This makes a light client unsuitable for
//! low-latency applications, but perfectly suitable for simple everyday
//! use-cases like sending transactions from a personal account.
//!
//! It starts by performing a header-only sync, verifying random samples
//! of members of the chain to varying degrees.
// TODO: remove when integrating with parity.
#![allow(dead_code)]
pub mod client;
pub mod net;
pub mod provider;
pub mod request;
extern crate ethcore_util as util;
extern crate ethcore_network as network;
extern crate ethcore_io as io;
extern crate ethcore;
extern crate rlp;
extern crate time;
#[macro_use]
extern crate log;

View File

@ -0,0 +1,264 @@
// 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/>.
//! LES buffer flow management.
//!
//! Every request in the LES protocol leads to a reduction
//! of the requester's buffer value as a rate-limiting mechanism.
//! This buffer value will recharge at a set rate.
//!
//! This module provides an interface for configuration of buffer
//! flow costs and recharge rates.
use request;
use super::packet;
use super::error::Error;
use rlp::*;
use util::U256;
use time::{Duration, SteadyTime};
/// A request cost specification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cost(pub U256, pub U256);
/// Buffer value.
///
/// Produced and recharged using `FlowParams`.
/// Definitive updates can be made as well -- these will reset the recharge
/// point to the time of the update.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Buffer {
estimate: U256,
recharge_point: SteadyTime,
}
impl Buffer {
/// Get the current buffer value.
pub fn current(&self) -> U256 { self.estimate.clone() }
/// Make a definitive update.
/// This will be the value obtained after receiving
/// a response to a request.
pub fn update_to(&mut self, value: U256) {
self.estimate = value;
self.recharge_point = SteadyTime::now();
}
/// Attempt to apply the given cost to the buffer.
///
/// If successful, the cost will be deducted successfully.
///
/// If unsuccessful, the structure will be unaltered an an
/// error will be produced.
pub fn deduct_cost(&mut self, cost: U256) -> Result<(), Error> {
match cost > self.estimate {
true => Err(Error::BufferEmpty),
false => {
self.estimate = self.estimate - cost;
Ok(())
}
}
}
}
/// A cost table, mapping requests to base and per-request costs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CostTable {
headers: Cost,
bodies: Cost,
receipts: Cost,
state_proofs: Cost,
contract_codes: Cost,
header_proofs: Cost,
}
impl Default for CostTable {
fn default() -> Self {
// arbitrarily chosen constants.
CostTable {
headers: Cost(100000.into(), 10000.into()),
bodies: Cost(150000.into(), 15000.into()),
receipts: Cost(50000.into(), 5000.into()),
state_proofs: Cost(250000.into(), 25000.into()),
contract_codes: Cost(200000.into(), 20000.into()),
header_proofs: Cost(150000.into(), 15000.into()),
}
}
}
impl RlpEncodable for CostTable {
fn rlp_append(&self, s: &mut RlpStream) {
fn append_cost(s: &mut RlpStream, msg_id: u8, cost: &Cost) {
s.begin_list(3)
.append(&msg_id)
.append(&cost.0)
.append(&cost.1);
}
s.begin_list(6);
append_cost(s, packet::GET_BLOCK_HEADERS, &self.headers);
append_cost(s, packet::GET_BLOCK_BODIES, &self.bodies);
append_cost(s, packet::GET_RECEIPTS, &self.receipts);
append_cost(s, packet::GET_PROOFS, &self.state_proofs);
append_cost(s, packet::GET_CONTRACT_CODES, &self.contract_codes);
append_cost(s, packet::GET_HEADER_PROOFS, &self.header_proofs);
}
}
impl RlpDecodable for CostTable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
let mut headers = None;
let mut bodies = None;
let mut receipts = None;
let mut state_proofs = None;
let mut contract_codes = None;
let mut header_proofs = None;
for row in rlp.iter() {
let msg_id: u8 = try!(row.val_at(0));
let cost = {
let base = try!(row.val_at(1));
let per = try!(row.val_at(2));
Cost(base, per)
};
match msg_id {
packet::GET_BLOCK_HEADERS => headers = Some(cost),
packet::GET_BLOCK_BODIES => bodies = Some(cost),
packet::GET_RECEIPTS => receipts = Some(cost),
packet::GET_PROOFS => state_proofs = Some(cost),
packet::GET_CONTRACT_CODES => contract_codes = Some(cost),
packet::GET_HEADER_PROOFS => header_proofs = Some(cost),
_ => return Err(DecoderError::Custom("Unrecognized message in cost table")),
}
}
Ok(CostTable {
headers: try!(headers.ok_or(DecoderError::Custom("No headers cost specified"))),
bodies: try!(bodies.ok_or(DecoderError::Custom("No bodies cost specified"))),
receipts: try!(receipts.ok_or(DecoderError::Custom("No receipts cost specified"))),
state_proofs: try!(state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))),
contract_codes: try!(contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))),
header_proofs: try!(header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))),
})
}
}
/// A buffer-flow manager handles costs, recharge, limits
#[derive(Debug, Clone, PartialEq)]
pub struct FlowParams {
costs: CostTable,
limit: U256,
recharge: U256,
}
impl FlowParams {
/// Create new flow parameters from a request cost table,
/// buffer limit, and (minimum) rate of recharge.
pub fn new(limit: U256, costs: CostTable, recharge: U256) -> Self {
FlowParams {
costs: costs,
limit: limit,
recharge: recharge,
}
}
/// Get a reference to the buffer limit.
pub fn limit(&self) -> &U256 { &self.limit }
/// Get a reference to the cost table.
pub fn cost_table(&self) -> &CostTable { &self.costs }
/// Get a reference to the recharge rate.
pub fn recharge_rate(&self) -> &U256 { &self.recharge }
/// Compute the actual cost of a request, given the kind of request
/// and number of requests made.
pub fn compute_cost(&self, kind: request::Kind, amount: usize) -> U256 {
let cost = match kind {
request::Kind::Headers => &self.costs.headers,
request::Kind::Bodies => &self.costs.bodies,
request::Kind::Receipts => &self.costs.receipts,
request::Kind::StateProofs => &self.costs.state_proofs,
request::Kind::Codes => &self.costs.contract_codes,
request::Kind::HeaderProofs => &self.costs.header_proofs,
};
let amount: U256 = amount.into();
cost.0 + (amount * cost.1)
}
/// Create initial buffer parameter.
pub fn create_buffer(&self) -> Buffer {
Buffer {
estimate: self.limit,
recharge_point: SteadyTime::now(),
}
}
/// Recharge the buffer based on time passed since last
/// update.
pub fn recharge(&self, buf: &mut Buffer) {
let now = SteadyTime::now();
// recompute and update only in terms of full seconds elapsed
// in order to keep the estimate as an underestimate.
let elapsed = (now - buf.recharge_point).num_seconds();
buf.recharge_point = buf.recharge_point + Duration::seconds(elapsed);
let elapsed: U256 = elapsed.into();
buf.estimate = ::std::cmp::min(self.limit, buf.estimate + (elapsed * self.recharge));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_serialize_cost_table() {
let costs = CostTable::default();
let serialized = ::rlp::encode(&costs);
let new_costs: CostTable = ::rlp::decode(&*serialized);
assert_eq!(costs, new_costs);
}
#[test]
fn buffer_mechanism() {
use std::thread;
use std::time::Duration;
let flow_params = FlowParams::new(100.into(), Default::default(), 20.into());
let mut buffer = flow_params.create_buffer();
assert!(buffer.deduct_cost(101.into()).is_err());
assert!(buffer.deduct_cost(10.into()).is_ok());
thread::sleep(Duration::from_secs(1));
flow_params.recharge(&mut buffer);
assert_eq!(buffer.estimate, 100.into());
}
}

View File

@ -0,0 +1,94 @@
// 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/>.
//! Defines error types and levels of punishment to use upon
//! encountering.
use rlp::DecoderError;
use network::NetworkError;
use std::fmt;
/// Levels of punishment.
///
/// Currently just encompasses two different kinds of disconnect and
/// no punishment, but this is where reputation systems might come into play.
// In ascending order
#[derive(Debug, PartialEq, Eq)]
pub enum Punishment {
/// Perform no punishment.
None,
/// Disconnect the peer, but don't prevent them from reconnecting.
Disconnect,
/// Disconnect the peer and prevent them from reconnecting.
Disable,
}
/// Kinds of errors which can be encountered in the course of LES.
#[derive(Debug)]
pub enum Error {
/// An RLP decoding error.
Rlp(DecoderError),
/// A network error.
Network(NetworkError),
/// Out of buffer.
BufferEmpty,
/// Unrecognized packet code.
UnrecognizedPacket(u8),
/// Unexpected handshake.
UnexpectedHandshake,
/// Peer on wrong network (wrong NetworkId or genesis hash)
WrongNetwork,
}
impl Error {
/// What level of punishment does this error warrant?
pub fn punishment(&self) -> Punishment {
match *self {
Error::Rlp(_) => Punishment::Disable,
Error::Network(_) => Punishment::None,
Error::BufferEmpty => Punishment::Disable,
Error::UnrecognizedPacket(_) => Punishment::Disconnect,
Error::UnexpectedHandshake => Punishment::Disconnect,
Error::WrongNetwork => Punishment::Disable,
}
}
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Self {
Error::Rlp(err)
}
}
impl From<NetworkError> for Error {
fn from(err: NetworkError) -> Self {
Error::Network(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Rlp(ref err) => err.fmt(f),
Error::Network(ref err) => err.fmt(f),
Error::BufferEmpty => write!(f, "Out of buffer"),
Error::UnrecognizedPacket(code) => write!(f, "Unrecognized packet: 0x{:x}", code),
Error::UnexpectedHandshake => write!(f, "Unexpected handshake"),
Error::WrongNetwork => write!(f, "Wrong network"),
}
}
}

View File

@ -0,0 +1,506 @@
// 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/>.
//! LES Protocol Version 1 implementation.
//!
//! This uses a "Provider" to answer requests.
//! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES)
use io::TimerToken;
use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId};
use rlp::{RlpStream, Stream, UntrustedRlp, View};
use util::hash::H256;
use util::RwLock;
use std::collections::{HashMap, HashSet};
use std::sync::atomic::AtomicUsize;
use provider::Provider;
use request::{self, Request};
use self::buffer_flow::{Buffer, FlowParams};
use self::error::{Error, Punishment};
use self::status::{Status, Capabilities};
mod buffer_flow;
mod error;
mod status;
pub use self::status::Announcement;
const TIMEOUT: TimerToken = 0;
const TIMEOUT_INTERVAL_MS: u64 = 1000;
// LPV1
const PROTOCOL_VERSION: u32 = 1;
// TODO [rob] make configurable.
const PROTOCOL_ID: [u8; 3] = *b"les";
// packet ID definitions.
mod packet {
// the status packet.
pub const STATUS: u8 = 0x00;
// announcement of new block hashes or capabilities.
pub const ANNOUNCE: u8 = 0x01;
// request and response for block headers
pub const GET_BLOCK_HEADERS: u8 = 0x02;
pub const BLOCK_HEADERS: u8 = 0x03;
// request and response for block bodies
pub const GET_BLOCK_BODIES: u8 = 0x04;
pub const BLOCK_BODIES: u8 = 0x05;
// request and response for transaction receipts.
pub const GET_RECEIPTS: u8 = 0x06;
pub const RECEIPTS: u8 = 0x07;
// request and response for merkle proofs.
pub const GET_PROOFS: u8 = 0x08;
pub const PROOFS: u8 = 0x09;
// request and response for contract code.
pub const GET_CONTRACT_CODES: u8 = 0x0a;
pub const CONTRACT_CODES: u8 = 0x0b;
// relay transactions to peers.
pub const SEND_TRANSACTIONS: u8 = 0x0c;
// request and response for header proofs in a CHT.
pub const GET_HEADER_PROOFS: u8 = 0x0d;
pub const HEADER_PROOFS: u8 = 0x0e;
}
// A pending peer: one we've sent our status to but
// may not have received one for.
struct PendingPeer {
sent_head: H256,
}
// data about each peer.
struct Peer {
local_buffer: Buffer, // their buffer relative to us
remote_buffer: Buffer, // our buffer relative to them
current_asking: HashSet<usize>, // pending request ids.
status: Status,
capabilities: Capabilities,
remote_flow: FlowParams,
sent_head: H256, // last head we've given them.
}
/// This is an implementation of the light ethereum network protocol, abstracted
/// over a `Provider` of data and a p2p network.
///
/// This is simply designed for request-response purposes. Higher level uses
/// of the protocol, such as synchronization, will function as wrappers around
/// this system.
pub struct LightProtocol {
provider: Box<Provider>,
genesis_hash: H256,
network_id: status::NetworkId,
pending_peers: RwLock<HashMap<PeerId, PendingPeer>>,
peers: RwLock<HashMap<PeerId, Peer>>,
pending_requests: RwLock<HashMap<usize, Request>>,
capabilities: RwLock<Capabilities>,
flow_params: FlowParams, // assumed static and same for every peer.
req_id: AtomicUsize,
}
impl LightProtocol {
/// Make an announcement of new chain head and capabilities to all peers.
/// The announcement is expected to be valid.
pub fn make_announcement(&self, mut announcement: Announcement, io: &NetworkContext) {
let mut reorgs_map = HashMap::new();
// calculate reorg info and send packets
for (peer_id, peer_info) in self.peers.write().iter_mut() {
let reorg_depth = reorgs_map.entry(peer_info.sent_head)
.or_insert_with(|| {
match self.provider.reorg_depth(&announcement.head_hash, &peer_info.sent_head) {
Some(depth) => depth,
None => {
// both values will always originate locally -- this means something
// has gone really wrong
debug!(target: "les", "couldn't compute reorganization depth between {:?} and {:?}",
&announcement.head_hash, &peer_info.sent_head);
0
}
}
});
peer_info.sent_head = announcement.head_hash;
announcement.reorg_depth = *reorg_depth;
if let Err(e) = io.send(*peer_id, packet::ANNOUNCE, status::write_announcement(&announcement)) {
debug!(target: "les", "Error sending to peer {}: {}", peer_id, e);
}
}
}
}
impl LightProtocol {
// called when a peer connects.
fn on_connect(&self, peer: &PeerId, io: &NetworkContext) {
let peer = *peer;
match self.send_status(peer, io) {
Ok(pending_peer) => {
self.pending_peers.write().insert(peer, pending_peer);
}
Err(e) => {
trace!(target: "les", "Error while sending status: {}", e);
io.disconnect_peer(peer);
}
}
}
// called when a peer disconnects.
fn on_disconnect(&self, peer: PeerId) {
// TODO: reassign all requests assigned to this peer.
self.pending_peers.write().remove(&peer);
self.peers.write().remove(&peer);
}
// send status to a peer.
fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result<PendingPeer, NetworkError> {
let chain_info = self.provider.chain_info();
// TODO: could update capabilities here.
let status = Status {
head_td: chain_info.total_difficulty,
head_hash: chain_info.best_block_hash,
head_num: chain_info.best_block_number,
genesis_hash: chain_info.genesis_hash,
protocol_version: PROTOCOL_VERSION,
network_id: self.network_id,
last_head: None,
};
let capabilities = self.capabilities.read().clone();
let status_packet = status::write_handshake(&status, &capabilities, &self.flow_params);
try!(io.send(peer, packet::STATUS, status_packet));
Ok(PendingPeer {
sent_head: chain_info.best_block_hash,
})
}
// Handle status message from peer.
fn status(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> {
let pending = match self.pending_peers.write().remove(peer) {
Some(pending) => pending,
None => {
return Err(Error::UnexpectedHandshake);
}
};
let (status, capabilities, flow_params) = try!(status::parse_handshake(data));
trace!(target: "les", "Connected peer with chain head {:?}", (status.head_hash, status.head_num));
if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) {
return Err(Error::WrongNetwork);
}
self.peers.write().insert(*peer, Peer {
local_buffer: self.flow_params.create_buffer(),
remote_buffer: flow_params.create_buffer(),
current_asking: HashSet::new(),
status: status,
capabilities: capabilities,
remote_flow: flow_params,
sent_head: pending.sent_head,
});
Ok(())
}
// Handle an announcement.
fn announcement(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> {
if !self.peers.read().contains_key(peer) {
debug!(target: "les", "Ignoring announcement from unknown peer");
return Ok(())
}
let announcement = try!(status::parse_announcement(data));
let mut peers = self.peers.write();
let peer_info = match peers.get_mut(peer) {
Some(info) => info,
None => return Ok(()),
};
// update status.
{
// TODO: punish peer if they've moved backwards.
let status = &mut peer_info.status;
let last_head = status.head_hash;
status.head_hash = announcement.head_hash;
status.head_td = announcement.head_td;
status.head_num = announcement.head_num;
status.last_head = Some((last_head, announcement.reorg_depth));
}
// update capabilities.
{
let caps = &mut peer_info.capabilities;
caps.serve_headers = caps.serve_headers || announcement.serve_headers;
caps.serve_state_since = caps.serve_state_since.or(announcement.serve_state_since);
caps.serve_chain_since = caps.serve_chain_since.or(announcement.serve_chain_since);
caps.tx_relay = caps.tx_relay || announcement.tx_relay;
}
// TODO: notify listeners if new best block.
Ok(())
}
// Handle a request for block headers.
fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_HEADERS: usize = 512;
let mut present_buffer = match self.peers.read().get(peer) {
Some(peer) => peer.local_buffer.clone(),
None => {
debug!(target: "les", "Ignoring announcement from unknown peer");
return Ok(())
}
};
self.flow_params.recharge(&mut present_buffer);
let req_id: u64 = try!(data.val_at(0));
let req = request::Headers {
block: {
let rlp = try!(data.at(1));
(try!(rlp.val_at(0)), try!(rlp.val_at(1)))
},
max: ::std::cmp::min(MAX_HEADERS, try!(data.val_at(2))),
skip: try!(data.val_at(3)),
reverse: try!(data.val_at(4)),
};
let max_cost = self.flow_params.compute_cost(request::Kind::Headers, req.max);
try!(present_buffer.deduct_cost(max_cost));
let response = self.provider.block_headers(req);
let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len());
let cur_buffer = match self.peers.write().get_mut(peer) {
Some(peer) => {
self.flow_params.recharge(&mut peer.local_buffer);
try!(peer.local_buffer.deduct_cost(actual_cost));
peer.local_buffer.current()
}
None => {
debug!(target: "les", "peer disconnected during serving of request.");
return Ok(())
}
};
io.respond(packet::BLOCK_HEADERS, {
let mut stream = RlpStream::new_list(response.len() + 2);
stream.append(&req_id).append(&cur_buffer);
for header in response {
stream.append_raw(&header, 1);
}
stream.out()
}).map_err(Into::into)
}
// Receive a response for block headers.
fn block_headers(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Handle a request for block bodies.
fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_BODIES: usize = 256;
let mut present_buffer = match self.peers.read().get(peer) {
Some(peer) => peer.local_buffer.clone(),
None => {
debug!(target: "les", "Ignoring announcement from unknown peer");
return Ok(())
}
};
self.flow_params.recharge(&mut present_buffer);
let req_id: u64 = try!(data.val_at(0));
let req = request::Bodies {
block_hashes: try!(data.iter().skip(1).take(MAX_BODIES).map(|x| x.as_val()).collect())
};
let max_cost = self.flow_params.compute_cost(request::Kind::Bodies, req.block_hashes.len());
try!(present_buffer.deduct_cost(max_cost));
let response = self.provider.block_bodies(req);
let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count();
let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len);
let cur_buffer = match self.peers.write().get_mut(peer) {
Some(peer) => {
self.flow_params.recharge(&mut peer.local_buffer);
try!(peer.local_buffer.deduct_cost(actual_cost));
peer.local_buffer.current()
}
None => {
debug!(target: "les", "peer disconnected during serving of request.");
return Ok(())
}
};
io.respond(packet::BLOCK_BODIES, {
let mut stream = RlpStream::new_list(response.len() + 2);
stream.append(&req_id).append(&cur_buffer);
for body in response {
stream.append_raw(&body, 1);
}
stream.out()
}).map_err(Into::into)
}
// Receive a response for block bodies.
fn block_bodies(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Handle a request for receipts.
fn get_receipts(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Receive a response for receipts.
fn receipts(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Handle a request for proofs.
fn get_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Receive a response for proofs.
fn proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Handle a request for contract code.
fn get_contract_code(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Receive a response for contract code.
fn contract_code(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Handle a request for header proofs
fn get_header_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Receive a response for header proofs
fn header_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Receive a set of transactions to relay.
fn relay_transactions(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
}
impl NetworkProtocolHandler for LightProtocol {
fn initialize(&self, io: &NetworkContext) {
io.register_timer(TIMEOUT, TIMEOUT_INTERVAL_MS).expect("Error registering sync timer.");
}
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
let rlp = UntrustedRlp::new(data);
// handle the packet
let res = match packet_id {
packet::STATUS => self.status(peer, rlp),
packet::ANNOUNCE => self.announcement(peer, rlp),
packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp),
packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp),
packet::GET_BLOCK_BODIES => self.get_block_bodies(peer, io, rlp),
packet::BLOCK_BODIES => self.block_bodies(peer, io, rlp),
packet::GET_RECEIPTS => self.get_receipts(peer, io, rlp),
packet::RECEIPTS => self.receipts(peer, io, rlp),
packet::GET_PROOFS => self.get_proofs(peer, io, rlp),
packet::PROOFS => self.proofs(peer, io, rlp),
packet::GET_CONTRACT_CODES => self.get_contract_code(peer, io, rlp),
packet::CONTRACT_CODES => self.contract_code(peer, io, rlp),
packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp),
packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp),
packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp),
other => {
Err(Error::UnrecognizedPacket(other))
}
};
// if something went wrong, figure out how much to punish the peer.
if let Err(e) = res {
match e.punishment() {
Punishment::None => {}
Punishment::Disconnect => {
debug!(target: "les", "Disconnecting peer {}: {}", peer, e);
io.disconnect_peer(*peer)
}
Punishment::Disable => {
debug!(target: "les", "Disabling peer {}: {}", peer, e);
io.disable_peer(*peer)
}
}
}
}
fn connected(&self, io: &NetworkContext, peer: &PeerId) {
self.on_connect(peer, io);
}
fn disconnected(&self, _io: &NetworkContext, peer: &PeerId) {
self.on_disconnect(*peer);
}
fn timeout(&self, _io: &NetworkContext, timer: TimerToken) {
match timer {
TIMEOUT => {
// broadcast transactions to peers.
}
_ => warn!(target: "les", "received timeout on unknown token {}", timer),
}
}
}

View File

@ -0,0 +1,539 @@
// 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/>.
//! Peer status and capabilities.
use rlp::{DecoderError, RlpDecodable, RlpEncodable, RlpStream, Stream, UntrustedRlp, View};
use util::{H256, U256};
use super::buffer_flow::FlowParams;
// recognized handshake/announcement keys.
// unknown keys are to be skipped, known keys have a defined order.
// their string values are defined in the LES spec.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
enum Key {
ProtocolVersion,
NetworkId,
HeadTD,
HeadHash,
HeadNum,
GenesisHash,
ServeHeaders,
ServeChainSince,
ServeStateSince,
TxRelay,
BufferLimit,
BufferCostTable,
BufferRechargeRate,
}
impl Key {
// get the string value of this key.
fn as_str(&self) -> &'static str {
match *self {
Key::ProtocolVersion => "protocolVersion",
Key::NetworkId => "networkId",
Key::HeadTD => "headTd",
Key::HeadHash => "headHash",
Key::HeadNum => "headNum",
Key::GenesisHash => "genesisHash",
Key::ServeHeaders => "serveHeaders",
Key::ServeChainSince => "serveChainSince",
Key::ServeStateSince => "serveStateSince",
Key::TxRelay => "txRelay",
Key::BufferLimit => "flowControl/BL",
Key::BufferCostTable => "flowControl/MRC",
Key::BufferRechargeRate => "flowControl/MRR",
}
}
// try to parse the key value from a string.
fn from_str(s: &str) -> Option<Self> {
match s {
"protocolVersion" => Some(Key::ProtocolVersion),
"networkId" => Some(Key::NetworkId),
"headTd" => Some(Key::HeadTD),
"headHash" => Some(Key::HeadHash),
"headNum" => Some(Key::HeadNum),
"genesisHash" => Some(Key::GenesisHash),
"serveHeaders" => Some(Key::ServeHeaders),
"serveChainSince" => Some(Key::ServeChainSince),
"serveStateSince" => Some(Key::ServeStateSince),
"txRelay" => Some(Key::TxRelay),
"flowControl/BL" => Some(Key::BufferLimit),
"flowControl/MRC" => Some(Key::BufferCostTable),
"flowControl/MRR" => Some(Key::BufferRechargeRate),
_ => None
}
}
}
/// Network ID structure.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum NetworkId {
/// ID for the mainnet
Mainnet = 1,
/// ID for the testnet
Testnet = 0,
}
impl NetworkId {
fn from_raw(raw: u32) -> Option<Self> {
match raw {
0 => Some(NetworkId::Testnet),
1 => Some(NetworkId::Mainnet),
_ => None,
}
}
}
// helper for decoding key-value pairs in the handshake or an announcement.
struct Parser<'a> {
pos: usize,
rlp: UntrustedRlp<'a>,
}
impl<'a> Parser<'a> {
// expect a specific next key, and decode the value.
// error on unexpected key or invalid value.
fn expect<T: RlpDecodable>(&mut self, key: Key) -> Result<T, DecoderError> {
self.expect_raw(key).and_then(|item| item.as_val())
}
// expect a specific next key, and get the value's RLP.
// if the key isn't found, the position isn't advanced.
fn expect_raw(&mut self, key: Key) -> Result<UntrustedRlp<'a>, DecoderError> {
let pre_pos = self.pos;
if let Some((k, val)) = try!(self.get_next()) {
if k == key { return Ok(val) }
}
self.pos = pre_pos;
Err(DecoderError::Custom("Missing expected key"))
}
// get the next key and value RLP.
fn get_next(&mut self) -> Result<Option<(Key, UntrustedRlp<'a>)>, DecoderError> {
while self.pos < self.rlp.item_count() {
let pair = try!(self.rlp.at(self.pos));
let k: String = try!(pair.val_at(0));
self.pos += 1;
match Key::from_str(&k) {
Some(key) => return Ok(Some((key , try!(pair.at(1))))),
None => continue,
}
}
Ok(None)
}
}
// Helper for encoding a key-value pair
fn encode_pair<T: RlpEncodable>(key: Key, val: &T) -> Vec<u8> {
let mut s = RlpStream::new_list(2);
s.append(&key.as_str()).append(val);
s.out()
}
// Helper for encoding a flag.
fn encode_flag(key: Key) -> Vec<u8> {
let mut s = RlpStream::new_list(2);
s.append(&key.as_str()).append_empty_data();
s.out()
}
/// A peer status message.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Status {
/// Protocol version.
pub protocol_version: u32,
/// Network id of this peer.
pub network_id: NetworkId,
/// Total difficulty of the head of the chain.
pub head_td: U256,
/// Hash of the best block.
pub head_hash: H256,
/// Number of the best block.
pub head_num: u64,
/// Genesis hash
pub genesis_hash: H256,
/// Last announced chain head and reorg depth to common ancestor.
pub last_head: Option<(H256, u64)>,
}
/// Peer capabilities.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Capabilities {
/// Whether this peer can serve headers
pub serve_headers: bool,
/// Earliest block number it can serve block/receipt requests for.
pub serve_chain_since: Option<u64>,
/// Earliest block number it can serve state requests for.
pub serve_state_since: Option<u64>,
/// Whether it can relay transactions to the eth network.
pub tx_relay: bool,
}
impl Default for Capabilities {
fn default() -> Self {
Capabilities {
serve_headers: true,
serve_chain_since: None,
serve_state_since: None,
tx_relay: false,
}
}
}
/// Attempt to parse a handshake message into its three parts:
/// - chain status
/// - serving capabilities
/// - buffer flow parameters
pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowParams), DecoderError> {
let mut parser = Parser {
pos: 0,
rlp: rlp,
};
let status = Status {
protocol_version: try!(parser.expect(Key::ProtocolVersion)),
network_id: try!(parser.expect(Key::NetworkId)
.and_then(|id: u32| NetworkId::from_raw(id).ok_or(DecoderError::Custom("Invalid network ID")))),
head_td: try!(parser.expect(Key::HeadTD)),
head_hash: try!(parser.expect(Key::HeadHash)),
head_num: try!(parser.expect(Key::HeadNum)),
genesis_hash: try!(parser.expect(Key::GenesisHash)),
last_head: None,
};
let capabilities = Capabilities {
serve_headers: parser.expect_raw(Key::ServeHeaders).is_ok(),
serve_chain_since: parser.expect(Key::ServeChainSince).ok(),
serve_state_since: parser.expect(Key::ServeStateSince).ok(),
tx_relay: parser.expect_raw(Key::TxRelay).is_ok(),
};
let flow_params = FlowParams::new(
try!(parser.expect(Key::BufferLimit)),
try!(parser.expect(Key::BufferCostTable)),
try!(parser.expect(Key::BufferRechargeRate)),
);
Ok((status, capabilities, flow_params))
}
/// Write a handshake, given status, capabilities, and flow parameters.
pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params: &FlowParams) -> Vec<u8> {
let mut pairs = Vec::new();
pairs.push(encode_pair(Key::ProtocolVersion, &status.protocol_version));
pairs.push(encode_pair(Key::NetworkId, &(status.network_id as u32)));
pairs.push(encode_pair(Key::HeadTD, &status.head_td));
pairs.push(encode_pair(Key::HeadHash, &status.head_hash));
pairs.push(encode_pair(Key::HeadNum, &status.head_num));
pairs.push(encode_pair(Key::GenesisHash, &status.genesis_hash));
if capabilities.serve_headers {
pairs.push(encode_flag(Key::ServeHeaders));
}
if let Some(ref serve_chain_since) = capabilities.serve_chain_since {
pairs.push(encode_pair(Key::ServeChainSince, serve_chain_since));
}
if let Some(ref serve_state_since) = capabilities.serve_state_since {
pairs.push(encode_pair(Key::ServeStateSince, serve_state_since));
}
if capabilities.tx_relay {
pairs.push(encode_flag(Key::TxRelay));
}
pairs.push(encode_pair(Key::BufferLimit, flow_params.limit()));
pairs.push(encode_pair(Key::BufferCostTable, flow_params.cost_table()));
pairs.push(encode_pair(Key::BufferRechargeRate, flow_params.recharge_rate()));
let mut stream = RlpStream::new_list(pairs.len());
for pair in pairs {
stream.append_raw(&pair, 1);
}
stream.out()
}
/// An announcement of new chain head or capabilities made by a peer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Announcement {
/// Hash of the best block.
pub head_hash: H256,
/// Number of the best block.
pub head_num: u64,
/// Head total difficulty
pub head_td: U256,
/// reorg depth to common ancestor of last announced head.
pub reorg_depth: u64,
/// optional new header-serving capability. false means "no change"
pub serve_headers: bool,
/// optional new state-serving capability
pub serve_state_since: Option<u64>,
/// optional new chain-serving capability
pub serve_chain_since: Option<u64>,
/// optional new transaction-relay capability. false means "no change"
pub tx_relay: bool,
// TODO: changes in buffer flow?
}
/// Parse an announcement.
pub fn parse_announcement(rlp: UntrustedRlp) -> Result<Announcement, DecoderError> {
let mut last_key = None;
let mut announcement = Announcement {
head_hash: try!(rlp.val_at(0)),
head_num: try!(rlp.val_at(1)),
head_td: try!(rlp.val_at(2)),
reorg_depth: try!(rlp.val_at(3)),
serve_headers: false,
serve_state_since: None,
serve_chain_since: None,
tx_relay: false,
};
let mut parser = Parser {
pos: 4,
rlp: rlp,
};
while let Some((key, item)) = try!(parser.get_next()) {
if Some(key) <= last_key { return Err(DecoderError::Custom("Invalid announcement key ordering")) }
last_key = Some(key);
match key {
Key::ServeHeaders => announcement.serve_headers = true,
Key::ServeStateSince => announcement.serve_state_since = Some(try!(item.as_val())),
Key::ServeChainSince => announcement.serve_chain_since = Some(try!(item.as_val())),
Key::TxRelay => announcement.tx_relay = true,
_ => return Err(DecoderError::Custom("Nonsensical key in announcement")),
}
}
Ok(announcement)
}
/// Write an announcement out.
pub fn write_announcement(announcement: &Announcement) -> Vec<u8> {
let mut pairs = Vec::new();
if announcement.serve_headers {
pairs.push(encode_flag(Key::ServeHeaders));
}
if let Some(ref serve_chain_since) = announcement.serve_chain_since {
pairs.push(encode_pair(Key::ServeChainSince, serve_chain_since));
}
if let Some(ref serve_state_since) = announcement.serve_state_since {
pairs.push(encode_pair(Key::ServeStateSince, serve_state_since));
}
if announcement.tx_relay {
pairs.push(encode_flag(Key::TxRelay));
}
let mut stream = RlpStream::new_list(4 + pairs.len());
stream
.append(&announcement.head_hash)
.append(&announcement.head_num)
.append(&announcement.head_td)
.append(&announcement.reorg_depth);
for item in pairs {
stream.append_raw(&item, 1);
}
stream.out()
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::buffer_flow::FlowParams;
use util::{U256, H256, FixedHash};
use rlp::{RlpStream, Stream ,UntrustedRlp, View};
#[test]
fn full_handshake() {
let status = Status {
protocol_version: 1,
network_id: NetworkId::Mainnet,
head_td: U256::default(),
head_hash: H256::default(),
head_num: 10,
genesis_hash: H256::zero(),
last_head: None,
};
let capabilities = Capabilities {
serve_headers: true,
serve_chain_since: Some(5),
serve_state_since: Some(8),
tx_relay: true,
};
let flow_params = FlowParams::new(
1_000_000.into(),
Default::default(),
1000.into(),
);
let handshake = write_handshake(&status, &capabilities, &flow_params);
let (read_status, read_capabilities, read_flow)
= parse_handshake(UntrustedRlp::new(&handshake)).unwrap();
assert_eq!(read_status, status);
assert_eq!(read_capabilities, capabilities);
assert_eq!(read_flow, flow_params);
}
#[test]
fn partial_handshake() {
let status = Status {
protocol_version: 1,
network_id: NetworkId::Mainnet,
head_td: U256::default(),
head_hash: H256::default(),
head_num: 10,
genesis_hash: H256::zero(),
last_head: None,
};
let capabilities = Capabilities {
serve_headers: false,
serve_chain_since: Some(5),
serve_state_since: None,
tx_relay: true,
};
let flow_params = FlowParams::new(
1_000_000.into(),
Default::default(),
1000.into(),
);
let handshake = write_handshake(&status, &capabilities, &flow_params);
let (read_status, read_capabilities, read_flow)
= parse_handshake(UntrustedRlp::new(&handshake)).unwrap();
assert_eq!(read_status, status);
assert_eq!(read_capabilities, capabilities);
assert_eq!(read_flow, flow_params);
}
#[test]
fn skip_unknown_keys() {
let status = Status {
protocol_version: 1,
network_id: NetworkId::Mainnet,
head_td: U256::default(),
head_hash: H256::default(),
head_num: 10,
genesis_hash: H256::zero(),
last_head: None,
};
let capabilities = Capabilities {
serve_headers: false,
serve_chain_since: Some(5),
serve_state_since: None,
tx_relay: true,
};
let flow_params = FlowParams::new(
1_000_000.into(),
Default::default(),
1000.into(),
);
let handshake = write_handshake(&status, &capabilities, &flow_params);
let interleaved = {
let handshake = UntrustedRlp::new(&handshake);
let mut stream = RlpStream::new_list(handshake.item_count() * 3);
for item in handshake.iter() {
stream.append_raw(item.as_raw(), 1);
let (mut s1, mut s2) = (RlpStream::new_list(2), RlpStream::new_list(2));
s1.append(&"foo").append_empty_data();
s2.append(&"bar").append_empty_data();
stream.append_raw(&s1.out(), 1);
stream.append_raw(&s2.out(), 1);
}
stream.out()
};
let (read_status, read_capabilities, read_flow)
= parse_handshake(UntrustedRlp::new(&interleaved)).unwrap();
assert_eq!(read_status, status);
assert_eq!(read_capabilities, capabilities);
assert_eq!(read_flow, flow_params);
}
#[test]
fn announcement_roundtrip() {
let announcement = Announcement {
head_hash: H256::random(),
head_num: 100_000,
head_td: 1_000_000.into(),
reorg_depth: 4,
serve_headers: false,
serve_state_since: Some(99_000),
serve_chain_since: Some(1),
tx_relay: true,
};
let serialized = write_announcement(&announcement);
let read = parse_announcement(UntrustedRlp::new(&serialized)).unwrap();
assert_eq!(read, announcement);
}
#[test]
fn keys_out_of_order() {
use super::{Key, encode_pair, encode_flag};
let mut stream = RlpStream::new_list(6);
stream
.append(&H256::zero())
.append(&10u64)
.append(&100_000u64)
.append(&2u64)
.append_raw(&encode_pair(Key::ServeStateSince, &44u64), 1)
.append_raw(&encode_flag(Key::ServeHeaders), 1);
let out = stream.drain();
assert!(parse_announcement(UntrustedRlp::new(&out)).is_err());
let mut stream = RlpStream::new_list(6);
stream
.append(&H256::zero())
.append(&10u64)
.append(&100_000u64)
.append(&2u64)
.append_raw(&encode_flag(Key::ServeHeaders), 1)
.append_raw(&encode_pair(Key::ServeStateSince, &44u64), 1);
let out = stream.drain();
assert!(parse_announcement(UntrustedRlp::new(&out)).is_ok());
}
}

View File

@ -0,0 +1,71 @@
// 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/>.
//! A provider for the LES protocol. This is typically a full node, who can
//! give as much data as necessary to its peers.
use ethcore::transaction::SignedTransaction;
use ethcore::blockchain_info::BlockChainInfo;
use util::{Bytes, H256};
use request;
/// Defines the operations that a provider for `LES` must fulfill.
///
/// These are defined at [1], but may be subject to change.
/// Requests which can't be fulfilled should return an empty RLP list.
///
/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES)
pub trait Provider: Send + Sync {
/// Provide current blockchain info.
fn chain_info(&self) -> BlockChainInfo;
/// Find the depth of a common ancestor between two blocks.
fn reorg_depth(&self, a: &H256, b: &H256) -> Option<u64>;
/// Earliest state.
fn earliest_state(&self) -> Option<u64>;
/// Provide a list of headers starting at the requested block,
/// possibly in reverse and skipping `skip` at a time.
///
/// The returned vector may have any length in the range [0, `max`], but the
/// results within must adhere to the `skip` and `reverse` parameters.
fn block_headers(&self, req: request::Headers) -> Vec<Bytes>;
/// Provide as many as possible of the requested blocks (minus the headers) encoded
/// in RLP format.
fn block_bodies(&self, req: request::Bodies) -> Vec<Bytes>;
/// Provide the receipts as many as possible of the requested blocks.
/// Returns a vector of RLP-encoded lists of receipts.
fn receipts(&self, req: request::Receipts) -> Vec<Bytes>;
/// Provide a set of merkle proofs, as requested. Each request is a
/// block hash and request parameters.
///
/// Returns a vector to RLP-encoded lists satisfying the requests.
fn proofs(&self, req: request::StateProofs) -> Vec<Bytes>;
/// Provide contract code for the specified (block_hash, account_hash) pairs.
fn code(&self, req: request::ContractCodes) -> Vec<Bytes>;
/// Provide header proofs from the Canonical Hash Tries.
fn header_proofs(&self, req: request::HeaderProofs) -> Vec<Bytes>;
/// Provide pending transactions.
fn pending_transactions(&self) -> Vec<SignedTransaction>;
}

View File

@ -0,0 +1,145 @@
// 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/>.
//! LES request types.
// TODO: make IPC compatible.
use util::H256;
/// A request for block headers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Headers {
/// Block information for the request being made.
pub block: (u64, H256),
/// The maximum amount of headers which can be returned.
pub max: usize,
/// The amount of headers to skip between each response entry.
pub skip: usize,
/// Whether the headers should proceed in falling number from the initial block.
pub reverse: bool,
}
/// A request for specific block bodies.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bodies {
/// Hashes which bodies are being requested for.
pub block_hashes: Vec<H256>
}
/// A request for transaction receipts.
///
/// This request is answered with a list of transaction receipts for each block
/// requested.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Receipts {
/// Block hashes to return receipts for.
pub block_hashes: Vec<H256>,
}
/// A request for a state proof
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StateProof {
/// Block hash to query state from.
pub block: H256,
/// Key of the state trie -- corresponds to account hash.
pub key1: H256,
/// Key in that account's storage trie; if empty, then the account RLP should be
/// returned.
pub key2: Option<H256>,
/// if greater than zero, trie nodes beyond this level may be omitted.
pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep.
}
/// A request for state proofs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StateProofs {
/// All the proof requests.
pub requests: Vec<StateProof>,
}
/// A request for contract code.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContractCodes {
/// Block hash and account key (== sha3(address)) pairs to fetch code for.
pub code_requests: Vec<(H256, H256)>,
}
/// A request for a header proof from the Canonical Hash Trie.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderProof {
/// Number of the CHT.
pub cht_number: u64,
/// Block number requested.
pub block_number: u64,
/// If greater than zero, trie nodes beyond this level may be omitted.
pub from_level: u32,
}
/// A request for header proofs from the CHT.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderProofs {
/// All the proof requests.
pub requests: Vec<HeaderProofs>,
}
/// Kinds of requests.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
/// Requesting headers.
Headers,
/// Requesting block bodies.
Bodies,
/// Requesting transaction receipts.
Receipts,
/// Requesting proofs of state trie nodes.
StateProofs,
/// Requesting contract code by hash.
Codes,
/// Requesting header proofs (from the CHT).
HeaderProofs,
}
/// Encompasses all possible types of requests in a single structure.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Request {
/// Requesting headers.
Headers(Headers),
/// Requesting block bodies.
Bodies(Bodies),
/// Requesting transaction receipts.
Receipts(Receipts),
/// Requesting state proofs.
StateProofs(StateProofs),
/// Requesting contract codes.
Codes(ContractCodes),
/// Requesting header proofs.
HeaderProofs(HeaderProofs),
}
impl Request {
/// Get the kind of request this is.
pub fn kind(&self) -> Kind {
match *self {
Request::Headers(_) => Kind::Headers,
Request::Bodies(_) => Kind::Bodies,
Request::Receipts(_) => Kind::Receipts,
Request::StateProofs(_) => Kind::StateProofs,
Request::Codes(_) => Kind::Codes,
Request::HeaderProofs(_) => Kind::HeaderProofs,
}
}
}

View File

@ -11,7 +11,13 @@
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x118c30", "homesteadTransition": "0x118c30",
"eip150Transition": "0x2625a0" "eip150Transition": "0x2625a0",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff",
"ecip1010PauseTransition": "0x2dc6c0",
"ecip1010ContinueTransition": "0x4c4b40"
} }
} }
}, },

View File

@ -10,7 +10,11 @@
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x0", "homesteadTransition": "0x0",
"eip150Transition": "0x0" "eip150Transition": "0x0",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -0,0 +1,47 @@
{
"name": "Homestead (Test)",
"engine": {
"Ethash": {
"params": {
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x0",
"eip150Transition": "0x0",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "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 } } } }
}
}

View File

@ -15,7 +15,11 @@
"difficultyHardforkTransition": "0x59d9", "difficultyHardforkTransition": "0x59d9",
"difficultyHardforkBoundDivisor": "0x0200", "difficultyHardforkBoundDivisor": "0x0200",
"bombDefuseTransition": "0x30d40", "bombDefuseTransition": "0x30d40",
"eip150Transition": "0x7fffffffffffffff" "eip150Transition": "0x7fffffffffffffff",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -130,7 +130,11 @@
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a" "0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
], ],
"eip150Transition": "0x259518" "eip150Transition": "0x259518",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },
@ -172,7 +176,13 @@
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303" "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303",
"enode://89d5dc2a81e574c19d0465f497c1af96732d1b61a41de89c2a37f35707689ac416529fae1038809852b235c2d30fd325abdc57c122feeefbeaaf802cc7e9580d@45.55.33.62:30303",
"enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303",
"enode://016b20125f447a3b203a3cae953b2ede8ffe51290c071e7599294be84317635730c397b8ff74404d6be412d539ee5bb5c3c700618723d3b53958c92bd33eaa82@159.203.210.80:30303",
"enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@10.6.6.117:30303",
"enode://fe11ef89fc5ac9da358fc160857855f25bbf9e332c79b9ca7089330c02b728b2349988c6062f10982041702110745e203d26975a6b34bcc97144f9fe439034e8@10.1.72.117:30303"
], ],
"accounts": { "accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@ -130,7 +130,11 @@
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a" "0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
], ],
"eip150Transition": "0x7fffffffffffffff" "eip150Transition": "0x7fffffffffffffff",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -10,7 +10,11 @@
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x7fffffffffffffff", "homesteadTransition": "0x7fffffffffffffff",
"eip150Transition": "0x7fffffffffffffff" "eip150Transition": "0x7fffffffffffffff",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -10,7 +10,11 @@
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x0", "homesteadTransition": "0x0",
"eip150Transition": "0x7fffffffffffffff" "eip150Transition": "0x7fffffffffffffff",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -10,7 +10,11 @@
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d", "registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
"homesteadTransition": "0x789b0", "homesteadTransition": "0x789b0",
"eip150Transition": "0x1b34d8" "eip150Transition": "0x1b34d8",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -10,7 +10,11 @@
"blockReward": "0x14D1120D7B160000", "blockReward": "0x14D1120D7B160000",
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050", "registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
"homesteadTransition": "0x7fffffffffffffff", "homesteadTransition": "0x7fffffffffffffff",
"eip150Transition": "0x7fffffffffffffff" "eip150Transition": "0x7fffffffffffffff",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -130,7 +130,11 @@
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413", "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a" "0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
], ],
"eip150Transition": "0xa" "eip150Transition": "0xa",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x7fffffffffffffff",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff"
} }
} }
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "TestInstantSeal", "name": "DevelopmentChain",
"engine": { "engine": {
"InstantSeal": null "InstantSeal": null
}, },
@ -28,6 +28,6 @@
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } "0x00a329c0648769a73afac7f9381e08fb43dbea72": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
} }
} }

View File

@ -95,6 +95,7 @@ impl KeyDirectory for NullDir {
struct AddressBook { struct AddressBook {
path: PathBuf, path: PathBuf,
cache: HashMap<Address, AccountMeta>, cache: HashMap<Address, AccountMeta>,
transient: bool,
} }
impl AddressBook { impl AddressBook {
@ -106,11 +107,18 @@ impl AddressBook {
let mut r = AddressBook { let mut r = AddressBook {
path: path, path: path,
cache: HashMap::new(), cache: HashMap::new(),
transient: false,
}; };
r.revert(); r.revert();
r r
} }
pub fn transient() -> Self {
let mut book = AddressBook::new(Default::default());
book.transient = true;
book
}
pub fn get(&self) -> HashMap<Address, AccountMeta> { pub fn get(&self) -> HashMap<Address, AccountMeta> {
self.cache.clone() self.cache.clone()
} }
@ -134,6 +142,7 @@ impl AddressBook {
} }
fn revert(&mut self) { fn revert(&mut self) {
if self.transient { return; }
trace!(target: "addressbook", "revert"); trace!(target: "addressbook", "revert");
let _ = fs::File::open(self.path.clone()) let _ = fs::File::open(self.path.clone())
.map_err(|e| trace!(target: "addressbook", "Couldn't open address book: {}", e)) .map_err(|e| trace!(target: "addressbook", "Couldn't open address book: {}", e))
@ -144,6 +153,7 @@ impl AddressBook {
} }
fn save(&mut self) { fn save(&mut self) {
if self.transient { return; }
trace!(target: "addressbook", "save"); trace!(target: "addressbook", "save");
let _ = fs::File::create(self.path.clone()) let _ = fs::File::create(self.path.clone())
.map_err(|e| warn!(target: "addressbook", "Couldn't open address book for writing: {}", e)) .map_err(|e| warn!(target: "addressbook", "Couldn't open address book for writing: {}", e))
@ -175,7 +185,7 @@ impl AccountProvider {
pub fn transient_provider() -> Self { pub fn transient_provider() -> Self {
AccountProvider { AccountProvider {
unlocked: Mutex::new(HashMap::new()), unlocked: Mutex::new(HashMap::new()),
address_book: Mutex::new(AddressBook::new(Default::default())), address_book: Mutex::new(AddressBook::transient()),
sstore: Box::new(EthStore::open(Box::new(NullDir::default())) sstore: Box::new(EthStore::open(Box::new(NullDir::default()))
.expect("NullDir load always succeeds; qed")) .expect("NullDir load always succeeds; qed"))
} }

View File

@ -1475,7 +1475,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let b1a = canon_chain let b1a = canon_chain
@ -1539,7 +1539,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let t2 = Transaction { let t2 = Transaction {
nonce: 1.into(), nonce: 1.into(),
@ -1548,7 +1548,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let t3 = Transaction { let t3 = Transaction {
nonce: 2.into(), nonce: 2.into(),
@ -1557,7 +1557,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let b1a = canon_chain let b1a = canon_chain
.with_transaction(t1.clone()) .with_transaction(t1.clone())
@ -1863,7 +1863,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 101.into(), value: 101.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let t2 = Transaction { let t2 = Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
@ -1871,7 +1871,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 102.into(), value: 102.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let t3 = Transaction { let t3 = Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
@ -1879,7 +1879,7 @@ mod tests {
action: Action::Create, action: Action::Create,
value: 103.into(), value: 103.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(), data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let tx_hash1 = t1.hash(); let tx_hash1 = t1.hash();
let tx_hash2 = t2.hash(); let tx_hash2 = t2.hash();
let tx_hash3 = t3.hash(); let tx_hash3 = t3.hash();

View File

@ -35,7 +35,7 @@ use io::*;
use views::{HeaderView, BodyView, BlockView}; use views::{HeaderView, BodyView, BlockView};
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError}; use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
use header::BlockNumber; use header::BlockNumber;
use state::State; use state::{State, CleanupMode};
use spec::Spec; use spec::Spec;
use basic_types::Seal; use basic_types::Seal;
use engines::Engine; use engines::Engine;
@ -67,7 +67,7 @@ use evm::{Factory as EvmFactory, Schedule};
use miner::{Miner, MinerService}; use miner::{Miner, MinerService};
use snapshot::{self, io as snapshot_io}; use snapshot::{self, io as snapshot_io};
use factory::Factories; use factory::Factories;
use rlp::{View, UntrustedRlp}; use rlp::{decode, View, UntrustedRlp};
use state_db::StateDB; use state_db::StateDB;
use rand::OsRng; use rand::OsRng;
@ -149,6 +149,7 @@ pub struct Client {
factories: Factories, factories: Factories,
history: u64, history: u64,
rng: Mutex<OsRng>, rng: Mutex<OsRng>,
on_mode_change: Mutex<Option<Box<FnMut(&Mode) + 'static + Send>>>,
} }
impl Client { impl Client {
@ -213,7 +214,7 @@ impl Client {
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue); panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true }; let awake = match config.mode { Mode::Dark(..) | Mode::Off => false, _ => true };
let factories = Factories { let factories = Factories {
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size), vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
@ -245,6 +246,7 @@ impl Client {
factories: factories, factories: factories,
history: history, history: history,
rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))), rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))),
on_mode_change: Mutex::new(None),
}; };
Ok(Arc::new(client)) Ok(Arc::new(client))
} }
@ -262,6 +264,11 @@ impl Client {
} }
} }
/// Register an action to be done if a mode change happens.
pub fn on_mode_change<F>(&self, f: F) where F: 'static + FnMut(&Mode) + Send {
*self.on_mode_change.lock() = Some(Box::new(f));
}
/// Flush the block import queue. /// Flush the block import queue.
pub fn flush_queue(&self) { pub fn flush_queue(&self) {
self.block_queue.flush(); self.block_queue.flush();
@ -270,6 +277,22 @@ impl Client {
} }
} }
/// The env info as of the best block.
fn latest_env_info(&self) -> EnvInfo {
let header_data = self.best_block_header();
let view = HeaderView::new(&header_data);
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(),
}
}
fn build_last_hashes(&self, parent_hash: H256) -> Arc<LastHashes> { fn build_last_hashes(&self, parent_hash: H256) -> Arc<LastHashes> {
{ {
let hashes = self.last_hashes.read(); let hashes = self.last_hashes.read();
@ -792,7 +815,7 @@ impl BlockChainClient for Client {
let needed_balance = t.value + t.gas * t.gas_price; let needed_balance = t.value + t.gas * t.gas_price;
if balance < needed_balance { if balance < needed_balance {
// give the sender a sufficient balance // give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance)); state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
} }
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)); let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options));
@ -842,18 +865,37 @@ impl BlockChainClient for Client {
} }
fn keep_alive(&self) { fn keep_alive(&self) {
let mode = self.mode.lock().clone(); let should_wake = match &*self.mode.lock() {
if mode != Mode::Active { &Mode::Dark(..) | &Mode::Passive(..) => true,
_ => false,
};
if should_wake {
self.wake_up(); self.wake_up();
(*self.sleep_state.lock()).last_activity = Some(Instant::now()); (*self.sleep_state.lock()).last_activity = Some(Instant::now());
} }
} }
fn mode(&self) -> IpcMode { self.mode.lock().clone().into() } fn mode(&self) -> IpcMode {
let r = self.mode.lock().clone().into();
trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r);
r
}
fn set_mode(&self, mode: IpcMode) { fn set_mode(&self, new_mode: IpcMode) {
*self.mode.lock() = mode.clone().into(); trace!(target: "mode", "Client::set_mode({:?})", new_mode);
match mode { {
let mut mode = self.mode.lock();
*mode = new_mode.clone().into();
trace!(target: "mode", "Mode now {:?}", &*mode);
match *self.on_mode_change.lock() {
Some(ref mut f) => {
trace!(target: "mode", "Making callback...");
f(&*mode)
},
_ => {}
}
}
match new_mode {
IpcMode::Active => self.wake_up(), IpcMode::Active => self.wake_up(),
IpcMode::Off => self.sleep(), IpcMode::Off => self.sleep(),
_ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); } _ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); }
@ -1010,7 +1052,9 @@ impl BlockChainClient for Client {
transaction_hash: transaction_hash.clone(), transaction_hash: transaction_hash.clone(),
transaction_index: transaction_index, transaction_index: transaction_index,
log_index: i log_index: i
}).collect() }).collect(),
log_bloom: receipt.log_bloom,
state_root: receipt.state_root,
}) })
}, },
_ => None _ => None
@ -1176,24 +1220,27 @@ impl BlockChainClient for Client {
self.notify(|notify| notify.broadcast(new_message.clone())); self.notify(|notify| notify.broadcast(new_message.clone()));
} }
} }
fn signing_network_id(&self) -> Option<u8> {
self.engine.signing_network_id(&self.latest_env_info())
}
fn block_extra_info(&self, id: BlockID) -> Option<BTreeMap<String, String>> {
self.block_header(id)
.map(|block| decode(&block))
.map(|header| self.engine.extra_info(&header))
}
fn uncle_extra_info(&self, id: UncleID) -> Option<BTreeMap<String, String>> {
self.uncle(id)
.map(|header| self.engine.extra_info(&decode(&header)))
}
} }
impl MiningBlockChainClient for Client { impl MiningBlockChainClient for Client {
fn latest_schedule(&self) -> Schedule { fn latest_schedule(&self) -> Schedule {
let header_data = self.best_block_header(); self.engine.schedule(&self.latest_env_info())
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 { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {

View File

@ -16,6 +16,7 @@
use std::str::FromStr; use std::str::FromStr;
use std::path::Path; use std::path::Path;
use std::fmt::{Display, Formatter, Error as FmtError};
pub use std::time::Duration; pub use std::time::Duration;
pub use blockchain::Config as BlockChainConfig; pub use blockchain::Config as BlockChainConfig;
pub use trace::Config as TraceConfig; pub use trace::Config as TraceConfig;
@ -86,6 +87,17 @@ impl Default for Mode {
} }
} }
impl Display for Mode {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match *self {
Mode::Active => write!(f, "active"),
Mode::Passive(..) => write!(f, "passive"),
Mode::Dark(..) => write!(f, "dark"),
Mode::Off => write!(f, "offline"),
}
}
}
/// Client configuration. Includes configs for all sub-systems. /// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, PartialEq, Default)] #[derive(Debug, PartialEq, Default)]
pub struct ClientConfig { pub struct ClientConfig {

View File

@ -38,6 +38,7 @@ use evm::{Factory as EvmFactory, VMType, Schedule};
use miner::{Miner, MinerService, TransactionImportResult}; use miner::{Miner, MinerService, TransactionImportResult};
use spec::Spec; use spec::Spec;
use types::mode::Mode; use types::mode::Mode;
use views::BlockView;
use verification::queue::QueueInfo; use verification::queue::QueueInfo;
use block::{OpenBlock, SealedBlock}; use block::{OpenBlock, SealedBlock};
@ -227,7 +228,7 @@ impl TestBlockChainClient {
gas_price: U256::one(), gas_price: U256::one(),
nonce: U256::zero() nonce: U256::zero()
}; };
let signed_tx = tx.sign(keypair.secret()); let signed_tx = tx.sign(keypair.secret(), None);
txs.append(&signed_tx); txs.append(&signed_tx);
txs.out() txs.out()
}, },
@ -293,7 +294,7 @@ impl TestBlockChainClient {
gas_price: U256::one(), gas_price: U256::one(),
nonce: U256::zero() nonce: U256::zero()
}; };
let signed_tx = tx.sign(keypair.secret()); let signed_tx = tx.sign(keypair.secret(), None);
self.set_balance(signed_tx.sender().unwrap(), 10_000_000.into()); self.set_balance(signed_tx.sender().unwrap(), 10_000_000.into());
let res = self.miner.import_external_transactions(self, vec![signed_tx]); let res = self.miner.import_external_transactions(self, vec![signed_tx]);
let res = res.into_iter().next().unwrap().expect("Successful import"); let res = res.into_iter().next().unwrap().expect("Successful import");
@ -314,7 +315,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
impl MiningBlockChainClient for TestBlockChainClient { impl MiningBlockChainClient for TestBlockChainClient {
fn latest_schedule(&self) -> Schedule { fn latest_schedule(&self) -> Schedule {
Schedule::new_homestead_gas_fix() Schedule::new_post_eip150(true, true, true)
} }
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
@ -417,6 +418,10 @@ impl BlockChainClient for TestBlockChainClient {
None // Simple default. None // Simple default.
} }
fn uncle_extra_info(&self, _id: UncleID) -> Option<BTreeMap<String, String>> {
None
}
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> { fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
self.receipts.read().get(&id).cloned() self.receipts.read().get(&id).cloned()
} }
@ -459,6 +464,13 @@ impl BlockChainClient for TestBlockChainClient {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned()) self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned())
} }
fn block_extra_info(&self, id: BlockID) -> Option<BTreeMap<String, String>> {
self.block(id)
.map(|block| BlockView::new(&block).header())
.map(|header| self.spec.engine.extra_info(&header))
}
fn block_status(&self, id: BlockID) -> BlockStatus { fn block_status(&self, id: BlockID) -> BlockStatus {
match id { match id {
BlockID::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain, BlockID::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain,
@ -637,6 +649,8 @@ impl BlockChainClient for TestBlockChainClient {
self.miner.pending_transactions(self.chain_info().best_block_number) self.miner.pending_transactions(self.chain_info().best_block_number)
} }
fn signing_network_id(&self) -> Option<u8> { None }
fn mode(&self) -> Mode { Mode::Active } fn mode(&self) -> Mode { Mode::Active }
fn set_mode(&self, _: Mode) { unimplemented!(); } fn set_mode(&self, _: Mode) { unimplemented!(); }

View File

@ -29,13 +29,13 @@ use error::{ImportResult, CallError};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use evm::{Factory as EvmFactory, Schedule}; use evm::{Factory as EvmFactory, Schedule};
use types::ids::*;
use types::trace_filter::Filter as TraceFilter;
use executive::Executed; use executive::Executed;
use env_info::LastHashes; use env_info::LastHashes;
use types::call_analytics::CallAnalytics;
use block_import_error::BlockImportError; use block_import_error::BlockImportError;
use ipc::IpcConfig; use ipc::IpcConfig;
use types::ids::*;
use types::trace_filter::Filter as TraceFilter;
use types::call_analytics::CallAnalytics;
use types::blockchain_info::BlockChainInfo; use types::blockchain_info::BlockChainInfo;
use types::block_status::BlockStatus; use types::block_status::BlockStatus;
use types::mode::Mode; use types::mode::Mode;
@ -218,7 +218,7 @@ pub trait BlockChainClient : Sync + Send {
/// Calculate median gas price from recent blocks if they have any transactions. /// Calculate median gas price from recent blocks if they have any transactions.
fn gas_price_median(&self, sample_size: usize) -> Option<U256> { fn gas_price_median(&self, sample_size: usize) -> Option<U256> {
let corpus = self.gas_price_corpus(sample_size); let corpus = self.gas_price_corpus(sample_size);
corpus.get(corpus.len()/2).cloned() corpus.get(corpus.len() / 2).cloned()
} }
/// Get the gas price distribution based on recent blocks if they have any transactions. /// Get the gas price distribution based on recent blocks if they have any transactions.
@ -226,13 +226,24 @@ pub trait BlockChainClient : Sync + Send {
let raw_corpus = self.gas_price_corpus(sample_size); let raw_corpus = self.gas_price_corpus(sample_size);
let raw_len = raw_corpus.len(); let raw_len = raw_corpus.len();
// Throw out outliers. // Throw out outliers.
let (corpus, _) = raw_corpus.split_at(raw_len-raw_len/40); let (corpus, _) = raw_corpus.split_at(raw_len - raw_len / 40);
Histogram::new(corpus, bucket_number) Histogram::new(corpus, bucket_number)
} }
/// Get the preferred network ID to sign on
fn signing_network_id(&self) -> Option<u8>;
/// Get the mode.
fn mode(&self) -> Mode; fn mode(&self) -> Mode;
/// Set the mode.
fn set_mode(&self, mode: Mode); fn set_mode(&self, mode: Mode);
/// Returns engine-related extra info for `BlockID`.
fn block_extra_info(&self, id: BlockID) -> Option<BTreeMap<String, String>>;
/// Returns engine-related extra info for `UncleID`.
fn uncle_extra_info(&self, id: UncleID) -> Option<BTreeMap<String, String>>;
} }
/// Extended client interface used for mining /// Extended client interface used for mining

View File

@ -81,7 +81,7 @@ impl Engine for BasicAuthority {
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins } fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
/// Additional engine-specific information for the user/developer concerning `header`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { hash_map!["signature".to_owned() => "TODO".to_owned()] } fn extra_info(&self, _header: &Header) -> BTreeMap<String, String> { map!["signature".to_owned() => "TODO".to_owned()] }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
Schedule::new_homestead() Schedule::new_homestead()

View File

@ -55,7 +55,7 @@ impl Engine for InstantSeal {
} }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
Schedule::new_homestead() Schedule::new_post_eip150(false, false, false)
} }
fn is_sealer(&self, _author: &Address) -> Option<bool> { Some(true) } fn is_sealer(&self, _author: &Address) -> Option<bool> { Some(true) }
@ -79,7 +79,7 @@ mod tests {
let tap = AccountProvider::transient_provider(); let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap(); let addr = tap.insert_account("".sha3(), "").unwrap();
let spec = Spec::new_test_instant(); let spec = Spec::new_instant();
let engine = &*spec.engine; let engine = &*spec.engine;
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let mut db_result = get_temp_state_db(); let mut db_result = get_temp_state_db();
@ -95,7 +95,7 @@ mod tests {
#[test] #[test]
fn instant_cant_verify() { fn instant_cant_verify() {
let engine = Spec::new_test_instant().engine; let engine = Spec::new_instant().engine;
let mut header: Header = Header::default(); let mut header: Header = Header::default();
assert!(engine.verify_block_basic(&header, None).is_ok()); assert!(engine.verify_block_basic(&header, None).is_ok());

View File

@ -74,7 +74,7 @@ pub trait Engine : Sync + Send {
fn seal_fields(&self) -> usize { 0 } fn seal_fields(&self) -> usize { 0 }
/// Additional engine-specific information for the user/developer concerning `header`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() } fn extra_info(&self, _header: &Header) -> BTreeMap<String, String> { BTreeMap::new() }
/// Additional information. /// Additional information.
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() } fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
@ -135,6 +135,9 @@ pub trait Engine : Sync + Send {
/// Verify a particular transaction is valid. /// Verify a particular transaction is valid.
fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
/// The network ID that transactions should be signed with.
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u8> { None }
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods /// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer /// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
/// methods are needed for an Engine, this may be overridden. /// methods are needed for an Engine, this may be overridden.

View File

@ -77,6 +77,8 @@ pub enum TransactionError {
RecipientBanned, RecipientBanned,
/// Contract creation code is banned. /// Contract creation code is banned.
CodeBanned, CodeBanned,
/// Invalid network ID given.
InvalidNetworkId,
} }
impl fmt::Display for TransactionError { impl fmt::Display for TransactionError {
@ -100,6 +102,7 @@ impl fmt::Display for TransactionError {
SenderBanned => "Sender is temporarily banned.".into(), SenderBanned => "Sender is temporarily banned.".into(),
RecipientBanned => "Recipient is temporarily banned.".into(), RecipientBanned => "Recipient is temporarily banned.".into(),
CodeBanned => "Contract code is temporarily banned.".into(), CodeBanned => "Contract code is temporarily banned.".into(),
InvalidNetworkId => "Transaction of this network ID is not allowed on this chain.".into(),
}; };
f.write_fmt(format_args!("Transaction error ({})", msg)) f.write_fmt(format_args!("Transaction error ({})", msg))

View File

@ -14,14 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager, H256 as EH256}; use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager};
use util::*; use util::*;
use block::*; use block::*;
use builtin::Builtin; use builtin::Builtin;
use env_info::EnvInfo; use env_info::EnvInfo;
use error::{BlockError, Error}; use error::{BlockError, TransactionError, Error};
use header::Header; use header::Header;
use views::HeaderView; use views::HeaderView;
use state::CleanupMode;
use spec::CommonParams; use spec::CommonParams;
use transaction::SignedTransaction; use transaction::SignedTransaction;
use engines::Engine; use engines::Engine;
@ -61,8 +62,20 @@ pub struct EthashParams {
pub difficulty_hardfork_bound_divisor: U256, pub difficulty_hardfork_bound_divisor: U256,
/// Block on which there is no additional difficulty from the exponential bomb. /// Block on which there is no additional difficulty from the exponential bomb.
pub bomb_defuse_transition: u64, pub bomb_defuse_transition: u64,
/// Bad gas transition block number. /// Number of first block where EIP-150 rules begin.
pub eip150_transition: u64, pub eip150_transition: u64,
/// Number of first block where EIP-155 rules begin.
pub eip155_transition: u64,
/// Number of first block where EIP-160 rules begin.
pub eip160_transition: u64,
/// Number of first block where EIP-161.abc begin.
pub eip161abc_transition: u64,
/// Number of first block where EIP-161.d begins.
pub eip161d_transition: u64,
/// Number of first block where ECIP-1010 begins.
pub ecip1010_pause_transition: u64,
/// Number of first block where ECIP-1010 ends.
pub ecip1010_continue_transition: u64
} }
impl From<ethjson::spec::EthashParams> for EthashParams { impl From<ethjson::spec::EthashParams> for EthashParams {
@ -83,6 +96,12 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), 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), bomb_defuse_transition: p.bomb_defuse_transition.map_or(0x7fffffffffffffff, Into::into),
eip150_transition: p.eip150_transition.map_or(0, Into::into), eip150_transition: p.eip150_transition.map_or(0, Into::into),
eip155_transition: p.eip155_transition.map_or(0, Into::into),
eip160_transition: p.eip160_transition.map_or(0, Into::into),
eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into),
eip161d_transition: p.eip161d_transition.map_or(0x7fffffffffffffff, Into::into),
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(0x7fffffffffffffff, Into::into),
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(0x7fffffffffffffff, Into::into),
} }
} }
} }
@ -122,8 +141,8 @@ impl Engine for Ethash {
} }
/// Additional engine-specific information for the user/developer concerning `header`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, header: &Header) -> HashMap<String, String> { fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
hash_map!["nonce".to_owned() => format!("0x{}", header.nonce().hex()), "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())] map!["nonce".to_owned() => format!("0x{}", header.nonce().hex()), "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())]
} }
fn schedule(&self, env_info: &EnvInfo) -> Schedule { fn schedule(&self, env_info: &EnvInfo) -> Schedule {
@ -134,7 +153,19 @@ impl Engine for Ethash {
} else if env_info.number < self.ethash_params.eip150_transition { } else if env_info.number < self.ethash_params.eip150_transition {
Schedule::new_homestead() Schedule::new_homestead()
} else { } else {
Schedule::new_homestead_gas_fix() Schedule::new_post_eip150(
env_info.number >= self.ethash_params.eip160_transition,
env_info.number >= self.ethash_params.eip161abc_transition,
env_info.number >= self.ethash_params.eip161d_transition
)
}
}
fn signing_network_id(&self, env_info: &EnvInfo) -> Option<u8> {
if env_info.number >= self.ethash_params.eip155_transition && self.params().network_id < 127 {
Some(self.params().network_id as u8)
} else {
None
} }
} }
@ -171,7 +202,7 @@ impl Engine for Ethash {
let mut state = block.fields_mut().state; let mut state = block.fields_mut().state;
for child in &self.ethash_params.dao_hardfork_accounts { for child in &self.ethash_params.dao_hardfork_accounts {
let b = state.balance(child); let b = state.balance(child);
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b); state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b, CleanupMode::NoEmpty);
} }
// } // }
} }
@ -184,12 +215,12 @@ impl Engine for Ethash {
let fields = block.fields_mut(); let fields = block.fields_mut();
// Bestow block reward // Bestow block reward
fields.state.add_balance(fields.header.author(), &(reward + reward / U256::from(32) * U256::from(fields.uncles.len()))); fields.state.add_balance(fields.header.author(), &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())), CleanupMode::NoEmpty);
// Bestow uncle rewards // Bestow uncle rewards
let current_number = fields.header.number(); let current_number = fields.header.number();
for u in fields.uncles.iter() { for u in fields.uncles.iter() {
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8))); fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)), CleanupMode::NoEmpty);
} }
// Commit state so that we can actually figure out the state root. // Commit state so that we can actually figure out the state root.
@ -214,10 +245,10 @@ impl Engine for Ethash {
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() }))) return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() })))
} }
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty( let difficulty = Ethash::boundary_to_difficulty(&H256(quick_get_difficulty(
&Ethash::to_ethash(header.bare_hash()), &header.bare_hash().0,
header.nonce().low_u64(), header.nonce().low_u64(),
&Ethash::to_ethash(header.mix_hash()) &header.mix_hash().0
))); )));
if &difficulty < header.difficulty() { if &difficulty < header.difficulty() {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
@ -242,10 +273,10 @@ impl Engine for Ethash {
Mismatch { expected: self.seal_fields(), found: header.seal().len() } Mismatch { expected: self.seal_fields(), found: header.seal().len() }
))); )));
} }
let result = self.pow.compute_light(header.number() as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64()); let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, header.nonce().low_u64());
let mix = Ethash::from_ethash(result.mix_hash); let mix = H256(result.mix_hash);
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value)); let difficulty = Ethash::boundary_to_difficulty(&H256(result.value));
trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, Ethash::from_ethash(slow_get_seedhash(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), Ethash::from_ethash(result.mix_hash), Ethash::from_ethash(result.value)); trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, H256(slow_get_seedhash(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), H256(result.mix_hash), H256(result.value));
if mix != header.mix_hash() { if mix != header.mix_hash() {
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() }))); return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() })));
} }
@ -279,6 +310,13 @@ impl Engine for Ethash {
if header.number() >= self.ethash_params.homestead_transition { if header.number() >= self.ethash_params.homestead_transition {
try!(t.check_low_s()); try!(t.check_low_s());
} }
if let Some(n) = t.network_id() {
if header.number() < self.ethash_params.eip155_transition || n as usize != self.params().network_id {
return Err(TransactionError::InvalidNetworkId.into())
}
}
Ok(()) Ok(())
} }
@ -292,7 +330,7 @@ impl Engine for Ethash {
} }
} }
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self #[cfg_attr(feature="dev", allow(wrong_self_convention))]
impl Ethash { impl Ethash {
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
const EXP_DIFF_PERIOD: u64 = 100000; const EXP_DIFF_PERIOD: u64 = 100000;
@ -328,9 +366,20 @@ impl Ethash {
}; };
target = max(min_difficulty, target); target = max(min_difficulty, target);
if header.number() < self.ethash_params.bomb_defuse_transition { if header.number() < self.ethash_params.bomb_defuse_transition {
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize; if header.number() < self.ethash_params.ecip1010_pause_transition {
if period > 1 { let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
target = max(min_difficulty, target + (U256::from(1) << (period - 2))); if period > 1 {
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
}
}
else if header.number() < self.ethash_params.ecip1010_continue_transition {
let fixed_difficulty = ((self.ethash_params.ecip1010_pause_transition / EXP_DIFF_PERIOD) - 2) as usize;
target = max(min_difficulty, target + (U256::from(1) << fixed_difficulty));
}
else {
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
let delay = ((self.ethash_params.ecip1010_continue_transition - self.ethash_params.ecip1010_pause_transition) / EXP_DIFF_PERIOD) as usize;
target = max(min_difficulty, target + (U256::from(1) << (period - delay - 2)));
} }
} }
target target
@ -354,14 +403,6 @@ impl Ethash {
(((U256::one() << 255) / *difficulty) << 1).into() (((U256::one() << 255) / *difficulty) << 1).into()
} }
} }
fn to_ethash(hash: H256) -> EH256 {
unsafe { mem::transmute(hash) }
}
fn from_ethash(hash: EH256) -> H256 {
unsafe { mem::transmute(hash) }
}
} }
/// Check if a new block should replace the best blockchain block. /// Check if a new block should replace the best blockchain block.
@ -395,8 +436,8 @@ mod tests {
use env_info::EnvInfo; use env_info::EnvInfo;
use error::{BlockError, Error}; use error::{BlockError, Error};
use header::Header; use header::Header;
use super::super::new_morden; use super::super::{new_morden, new_homestead_test};
use super::Ethash; use super::{Ethash, EthashParams};
use rlp; use rlp;
#[test] #[test]
@ -618,5 +659,122 @@ mod tests {
assert_eq!(Ethash::difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap()); assert_eq!(Ethash::difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap());
} }
// TODO: difficulty test #[test]
fn difficulty_frontier() {
let spec = new_homestead_test();
let ethparams = get_default_ethash_params();
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
let mut parent_header = Header::default();
parent_header.set_number(1000000);
parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap());
parent_header.set_timestamp(1455404053);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(1455404058);
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
assert_eq!(U256::from_str("b6b4bbd735f").unwrap(), difficulty);
}
#[test]
fn difficulty_homestead() {
let spec = new_homestead_test();
let ethparams = get_default_ethash_params();
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
let mut parent_header = Header::default();
parent_header.set_number(1500000);
parent_header.set_difficulty(U256::from_str("1fd0fd70792b").unwrap());
parent_header.set_timestamp(1463003133);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(1463003177);
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty);
}
#[test]
fn difficulty_classic_bomb_delay() {
let spec = new_homestead_test();
let ethparams = EthashParams {
ecip1010_pause_transition: 3000000,
..get_default_ethash_params()
};
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
let mut parent_header = Header::default();
parent_header.set_number(3500000);
parent_header.set_difficulty(U256::from_str("6F62EAF8D3C").unwrap());
parent_header.set_timestamp(1452838500);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 20);
assert_eq!(
U256::from_str("6F55FE9B74B").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
header.set_timestamp(parent_header.timestamp() + 5);
assert_eq!(
U256::from_str("6F71D75632D").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
header.set_timestamp(parent_header.timestamp() + 80);
assert_eq!(
U256::from_str("6F02746B3A5").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
}
#[test]
fn test_difficulty_bomb_continue() {
let spec = new_homestead_test();
let ethparams = EthashParams {
ecip1010_pause_transition: 3000000,
ecip1010_continue_transition: 5000000,
..get_default_ethash_params()
};
let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new());
let mut parent_header = Header::default();
parent_header.set_number(5000102);
parent_header.set_difficulty(U256::from_str("14944397EE8B").unwrap());
parent_header.set_timestamp(1513175023);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 6);
assert_eq!(
U256::from_str("1496E6206188").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
parent_header.set_number(5100123);
parent_header.set_difficulty(U256::from_str("14D24B39C7CF").unwrap());
parent_header.set_timestamp(1514609324);
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 41);
assert_eq!(
U256::from_str("14CA9C5D9227").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
parent_header.set_number(6150001);
parent_header.set_difficulty(U256::from_str("305367B57227").unwrap());
parent_header.set_timestamp(1529664575);
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 105);
assert_eq!(
U256::from_str("309D09E0C609").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
parent_header.set_number(8000000);
parent_header.set_difficulty(U256::from_str("1180B36D4CE5B6A").unwrap());
parent_header.set_timestamp(1535431724);
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 420);
assert_eq!(
U256::from_str("5126FFD5BCBB9E7").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
}
} }

View File

@ -54,6 +54,9 @@ pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/ho
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier. /// 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")) } pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
pub fn new_eip161_test() -> Spec { load(include_bytes!("../../res/ethereum/eip161_test.json")) }
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8. /// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) } pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }

View File

@ -52,6 +52,12 @@ pub trait Ext {
/// Determine whether an account exists. /// Determine whether an account exists.
fn exists(&self, address: &Address) -> bool; fn exists(&self, address: &Address) -> bool;
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
fn exists_and_not_null(&self, address: &Address) -> bool;
/// Balance of the origin account.
fn origin_balance(&self) -> U256;
/// Returns address balance. /// Returns address balance.
fn balance(&self, address: &Address) -> U256; fn balance(&self, address: &Address) -> U256;

View File

@ -146,8 +146,13 @@ impl<Gas: CostType> Gasometer<Gas> {
instructions::SUICIDE => { instructions::SUICIDE => {
let mut gas = Gas::from(schedule.suicide_gas); let mut gas = Gas::from(schedule.suicide_gas);
let is_value_transfer = !ext.origin_balance().is_zero();
let address = u256_to_address(stack.peek(0)); let address = u256_to_address(stack.peek(0));
if !ext.exists(&address) { if (
!schedule.no_empty && !ext.exists(&address)
) || (
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)
) {
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
} }
@ -190,12 +195,19 @@ impl<Gas: CostType> Gasometer<Gas> {
); );
let address = u256_to_address(stack.peek(1)); let address = u256_to_address(stack.peek(1));
let is_value_transfer = !stack.peek(2).is_zero();
if instruction == instructions::CALL && !ext.exists(&address) { if instruction == instructions::CALL {
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into())); if (
!schedule.no_empty && !ext.exists(&address)
) || (
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)
) {
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
}
}; };
if !stack.peek(2).is_zero() { if is_value_transfer {
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into())); gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
}; };

View File

@ -93,6 +93,10 @@ pub struct Schedule {
/// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit. /// 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 /// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS
pub sub_gas_cap_divisor: Option<usize>, pub sub_gas_cap_divisor: Option<usize>,
/// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value.
pub no_empty: bool,
/// Kill empty accounts if touched.
pub kill_empty: bool,
} }
impl Schedule { impl Schedule {
@ -106,16 +110,16 @@ impl Schedule {
Self::new(true, true, 53000) Self::new(true, true, 53000)
} }
/// Schedule for the Homestead-era of the Ethereum main net. /// Schedule for the post-EIP-150-era of the Ethereum main net.
pub fn new_homestead_gas_fix() -> Schedule { pub fn new_post_eip150(fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule {
Schedule{ Schedule {
exceptional_failed_code_deposit: true, exceptional_failed_code_deposit: true,
have_delegate_call: true, have_delegate_call: true,
stack_limit: 1024, stack_limit: 1024,
max_depth: 1024, max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
exp_gas: 10, exp_gas: 10,
exp_byte_gas: 10, exp_byte_gas: if fix_exp {50} else {10},
sha3_gas: 30, sha3_gas: 30,
sha3_word_gas: 6, sha3_word_gas: 6,
sload_gas: 200, sload_gas: 200,
@ -146,11 +150,13 @@ impl Schedule {
suicide_gas: 5000, suicide_gas: 5000,
suicide_to_new_account_cost: 25000, suicide_to_new_account_cost: 25000,
sub_gas_cap_divisor: Some(64), sub_gas_cap_divisor: Some(64),
no_empty: no_empty,
kill_empty: kill_empty,
} }
} }
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
Schedule{ Schedule {
exceptional_failed_code_deposit: efcd, exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc, have_delegate_call: hdc,
stack_limit: 1024, stack_limit: 1024,
@ -188,6 +194,8 @@ impl Schedule {
suicide_gas: 0, suicide_gas: 0,
suicide_to_new_account_cost: 0, suicide_to_new_account_cost: 0,
sub_gas_cap_divisor: None, sub_gas_cap_divisor: None,
no_empty: false,
kill_empty: false,
} }
} }
} }

View File

@ -94,6 +94,14 @@ impl Ext for FakeExt {
self.balances.contains_key(address) self.balances.contains_key(address)
} }
fn exists_and_not_null(&self, address: &Address) -> bool {
self.balances.get(address).map_or(false, |b| !b.is_zero())
}
fn origin_balance(&self) -> U256 {
unimplemented!()
}
fn balance(&self, address: &Address) -> U256 { fn balance(&self, address: &Address) -> U256 {
*self.balances.get(address).unwrap() *self.balances.get(address).unwrap()
} }

View File

@ -17,7 +17,7 @@
//! Transaction Execution environment. //! Transaction Execution environment.
use util::*; use util::*;
use action_params::{ActionParams, ActionValue}; use action_params::{ActionParams, ActionValue};
use state::{State, Substate}; use state::{State, Substate, CleanupMode};
use engines::Engine; use engines::Engine;
use types::executed::CallType; use types::executed::CallType;
use env_info::EnvInfo; use env_info::EnvInfo;
@ -256,9 +256,11 @@ impl<'a> Executive<'a> {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.checkpoint(); self.state.checkpoint();
let schedule = self.engine.schedule(self.info);
// at first, transfer value to destination // at first, transfer value to destination
if let ActionValue::Transfer(val) = params.value { if let ActionValue::Transfer(val) = params.value {
self.state.transfer_balance(&params.sender, &params.address, &val); self.state.transfer_balance(&params.sender, &params.address, &val, substate.to_cleanup_mode(&schedule));
} }
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info); trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
@ -364,12 +366,14 @@ impl<'a> Executive<'a> {
let mut unconfirmed_substate = Substate::new(); let mut unconfirmed_substate = Substate::new();
// create contract and transfer value to it if necessary // create contract and transfer value to it if necessary
let schedule = self.engine.schedule(self.info);
let nonce_offset = if schedule.no_empty {1} else {0}.into();
let prev_bal = self.state.balance(&params.address); let prev_bal = self.state.balance(&params.address);
if let ActionValue::Transfer(val) = params.value { if let ActionValue::Transfer(val) = params.value {
self.state.sub_balance(&params.sender, &val); self.state.sub_balance(&params.sender, &val);
self.state.new_contract(&params.address, val + prev_bal); self.state.new_contract(&params.address, val + prev_bal, nonce_offset);
} else { } else {
self.state.new_contract(&params.address, prev_bal); self.state.new_contract(&params.address, prev_bal, nonce_offset);
} }
let trace_info = tracer.prepare_trace_create(&params); let trace_info = tracer.prepare_trace_create(&params);
@ -405,7 +409,7 @@ impl<'a> Executive<'a> {
fn finalize( fn finalize(
&mut self, &mut self,
t: &SignedTransaction, t: &SignedTransaction,
substate: Substate, mut substate: Substate,
result: evm::Result<U256>, result: evm::Result<U256>,
output: Bytes, output: Bytes,
trace: Vec<FlatTrace>, trace: Vec<FlatTrace>,
@ -440,15 +444,23 @@ impl<'a> Executive<'a> {
}; };
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender); trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender);
self.state.add_balance(&sender, &refund_value); // Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction
self.state.add_balance(&sender, &refund_value, CleanupMode::NoEmpty);
trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author); trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author);
self.state.add_balance(&self.info.author, &fees_value); self.state.add_balance(&self.info.author, &fees_value, substate.to_cleanup_mode(&schedule));
// perform suicides // perform suicides
for address in &substate.suicides { for address in &substate.suicides {
self.state.kill_account(address); self.state.kill_account(address);
} }
// perform garbage-collection
for address in &substate.garbage {
if self.state.exists(address) && !self.state.exists_and_not_null(address) {
self.state.kill_account(address);
}
}
match result { match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal), Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(_) => { Err(_) => {
@ -509,7 +521,7 @@ mod tests {
use env_info::EnvInfo; use env_info::EnvInfo;
use evm::{Factory, VMType}; use evm::{Factory, VMType};
use error::ExecutionError; use error::ExecutionError;
use state::Substate; use state::{Substate, CleanupMode};
use tests::helpers::*; use tests::helpers::*;
use trace::trace; use trace::trace;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
@ -538,7 +550,7 @@ mod tests {
params.value = ActionValue::Transfer(U256::from(0x7)); params.value = ActionValue::Transfer(U256::from(0x7));
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(0x100u64)); state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
let mut substate = Substate::new(); let mut substate = Substate::new();
@ -597,7 +609,7 @@ mod tests {
params.value = ActionValue::Transfer(U256::from(100)); params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
let mut substate = Substate::new(); let mut substate = Substate::new();
@ -656,7 +668,7 @@ mod tests {
params.call_type = CallType::Call; params.call_type = CallType::Call;
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(5); let engine = TestEngine::new(5);
let mut substate = Substate::new(); let mut substate = Substate::new();
@ -767,7 +779,7 @@ mod tests {
params.value = ActionValue::Transfer(100.into()); params.value = ActionValue::Transfer(100.into());
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(5); let engine = TestEngine::new(5);
let mut substate = Substate::new(); let mut substate = Substate::new();
@ -855,7 +867,7 @@ mod tests {
params.value = ActionValue::Transfer(U256::from(100)); params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
let mut substate = Substate::new(); let mut substate = Substate::new();
@ -907,7 +919,7 @@ mod tests {
params.value = ActionValue::Transfer(U256::from(100)); params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100)); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(1024); let engine = TestEngine::new(1024);
let mut substate = Substate::new(); let mut substate = Substate::new();
@ -967,7 +979,7 @@ mod tests {
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.init_code(&address_a, code_a.clone()); state.init_code(&address_a, code_a.clone());
state.init_code(&address_b, code_b.clone()); state.init_code(&address_b, code_b.clone());
state.add_balance(&sender, &U256::from(100_000)); state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
@ -1040,13 +1052,13 @@ mod tests {
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero() nonce: U256::zero()
}.sign(keypair.secret()); }.sign(keypair.secret(), None);
let sender = t.sender().unwrap(); let sender = t.sender().unwrap();
let contract = contract_address(&sender, &U256::zero()); let contract = contract_address(&sender, &U256::zero());
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(18)); state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty);
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = U256::from(100_000); info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
@ -1107,12 +1119,12 @@ mod tests {
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::one() nonce: U256::one()
}.sign(keypair.secret()); }.sign(keypair.secret(), None);
let sender = t.sender().unwrap(); let sender = t.sender().unwrap();
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(17)); state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty);
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = U256::from(100_000); info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
@ -1140,12 +1152,12 @@ mod tests {
gas: U256::from(80_001), gas: U256::from(80_001),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero() nonce: U256::zero()
}.sign(keypair.secret()); }.sign(keypair.secret(), None);
let sender = t.sender().unwrap(); let sender = t.sender().unwrap();
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(17)); state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty);
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_used = U256::from(20_000); info.gas_used = U256::from(20_000);
info.gas_limit = U256::from(100_000); info.gas_limit = U256::from(100_000);
@ -1175,12 +1187,12 @@ mod tests {
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::one(), gas_price: U256::one(),
nonce: U256::zero() nonce: U256::zero()
}.sign(keypair.secret()); }.sign(keypair.secret(), None);
let sender = t.sender().unwrap(); let sender = t.sender().unwrap();
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100_017)); state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty);
let mut info = EnvInfo::default(); let mut info = EnvInfo::default();
info.gas_limit = U256::from(100_000); info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
@ -1215,7 +1227,7 @@ mod tests {
params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap()); params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap());
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap()); state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty);
let info = EnvInfo::default(); let info = EnvInfo::default();
let engine = TestEngine::new(0); let engine = TestEngine::new(0);
let mut substate = Substate::new(); let mut substate = Substate::new();

View File

@ -114,6 +114,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
self.state.exists(address) self.state.exists(address)
} }
fn exists_and_not_null(&self, address: &Address) -> bool {
self.state.exists_and_not_null(address)
}
fn origin_balance(&self) -> U256 { self.balance(&self.origin_info.address) }
fn balance(&self, address: &Address) -> U256 { fn balance(&self, address: &Address) -> U256 {
self.state.balance(address) self.state.balance(address)
} }
@ -269,11 +275,11 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
let address = self.origin_info.address.clone(); let address = self.origin_info.address.clone();
let balance = self.balance(&address); let balance = self.balance(&address);
if &address == refund_address { if &address == refund_address {
// TODO [todr] To be consisted with CPP client we set balance to 0 in that case. // TODO [todr] To be consistent with CPP client we set balance to 0 in that case.
self.state.sub_balance(&address, &balance); self.state.sub_balance(&address, &balance);
} else { } else {
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance); trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
self.state.transfer_balance(&address, refund_address, &balance); self.state.transfer_balance(&address, refund_address, &balance, self.substate.to_cleanup_mode(&self.schedule));
} }
self.tracer.trace_suicide(address, balance, refund_address.clone()); self.tracer.trace_suicide(address, balance, refund_address.clone());

View File

@ -49,6 +49,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
ChainEra::Frontier => ethereum::new_frontier_test(), ChainEra::Frontier => ethereum::new_frontier_test(),
ChainEra::Homestead => ethereum::new_homestead_test(), ChainEra::Homestead => ethereum::new_homestead_test(),
ChainEra::Eip150 => ethereum::new_eip150_test(), ChainEra::Eip150 => ethereum::new_eip150_test(),
ChainEra::Eip161 => ethereum::new_eip161_test(),
ChainEra::TransitionTest => ethereum::new_transition_test(), ChainEra::TransitionTest => ethereum::new_transition_test(),
}; };
spec.set_genesis_state(state); spec.set_genesis_state(state);

View File

@ -0,0 +1,51 @@
// 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::Eip161)
}
declare_test!{StateTests_EIP158_stEIP158SpecificTest, "StateTests/EIP158/stEIP158SpecificTest"}
declare_test!{StateTests_EIP158_stNonZeroCallsTest, "StateTests/EIP158/stNonZeroCallsTest"}
declare_test!{StateTests_EIP158_stZeroCallsTest, "StateTests/EIP158/stZeroCallsTest"}
declare_test!{StateTests_EIP158_EIP150_stMemExpandingEIPCalls, "StateTests/EIP158/EIP150/stMemExpandingEIPCalls"}
declare_test!{StateTests_EIP158_EIP150_stEIPSpecificTest, "StateTests/EIP158/EIP150/stEIPSpecificTest"}
declare_test!{StateTests_EIP158_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP158/EIP150/stEIPsingleCodeGasPrices"}
declare_test!{StateTests_EIP158_EIP150_stChangedTests, "StateTests/EIP158/EIP150/stChangedTests"}
declare_test!{StateTests_EIP158_Homestead_stBoundsTest, "StateTests/EIP158/Homestead/stBoundsTest"}
declare_test!{StateTests_EIP158_Homestead_stCallCodes, "StateTests/EIP158/Homestead/stCallCodes"}
declare_test!{StateTests_EIP158_Homestead_stCallCreateCallCodeTest, "StateTests/EIP158/Homestead/stCallCreateCallCodeTest"}
declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodes, "StateTests/EIP158/Homestead/stCallDelegateCodes"}
declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodesCallCode, "StateTests/EIP158/Homestead/stCallDelegateCodesCallCode"}
declare_test!{StateTests_EIP158_Homestead_stDelegatecallTest, "StateTests/EIP158/Homestead/stDelegatecallTest"}
declare_test!{StateTests_EIP158_Homestead_stHomeSteadSpecific, "StateTests/EIP158/Homestead/stHomeSteadSpecific"}
declare_test!{StateTests_EIP158_Homestead_stInitCodeTest, "StateTests/EIP158/Homestead/stInitCodeTest"}
declare_test!{StateTests_EIP158_Homestead_stLogTests, "StateTests/EIP158/Homestead/stLogTests"}
declare_test!{heavy => StateTests_EIP158_Homestead_stMemoryTest, "StateTests/EIP158/Homestead/stMemoryTest"}
declare_test!{StateTests_EIP158_Homestead_stPreCompiledContracts, "StateTests/EIP158/Homestead/stPreCompiledContracts"}
declare_test!{heavy => StateTests_EIP158_Homestead_stQuadraticComplexityTest, "StateTests/EIP158/Homestead/stQuadraticComplexityTest"}
declare_test!{StateTests_EIP158_Homestead_stRecursiveCreate, "StateTests/EIP158/Homestead/stRecursiveCreate"}
declare_test!{StateTests_EIP158_Homestead_stRefundTest, "StateTests/EIP158/Homestead/stRefundTest"}
declare_test!{StateTests_EIP158_Homestead_stSpecialTest, "StateTests/EIP158/Homestead/stSpecialTest"}
declare_test!{StateTests_EIP158_Homestead_stSystemOperationsTest, "StateTests/EIP158/Homestead/stSystemOperationsTest"}
declare_test!{StateTests_EIP158_Homestead_stTransactionTest, "StateTests/EIP158/Homestead/stTransactionTest"}
declare_test!{StateTests_EIP158_Homestead_stWalletTest, "StateTests/EIP158/Homestead/stWalletTest"}

View File

@ -92,10 +92,18 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
self.ext.exists(address) self.ext.exists(address)
} }
fn exists_and_not_null(&self, address: &Address) -> bool {
self.ext.exists_and_not_null(address)
}
fn balance(&self, address: &Address) -> U256 { fn balance(&self, address: &Address) -> U256 {
self.ext.balance(address) self.ext.balance(address)
} }
fn origin_balance(&self) -> U256 {
self.ext.origin_balance()
}
fn blockhash(&self, number: &U256) -> H256 { fn blockhash(&self, number: &U256) -> H256 {
self.ext.blockhash(number) self.ext.blockhash(number)
} }

View File

@ -24,4 +24,5 @@ mod chain;
mod homestead_state; mod homestead_state;
mod homestead_chain; mod homestead_chain;
mod eip150_state; mod eip150_state;
mod eip161_state;
mod trie; mod trie;

View File

@ -29,6 +29,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
ChainEra::Frontier => ethereum::new_mainnet_like().engine, ChainEra::Frontier => ethereum::new_mainnet_like().engine,
ChainEra::Homestead => ethereum::new_homestead_test().engine, ChainEra::Homestead => ethereum::new_homestead_test().engine,
ChainEra::Eip150 => ethereum::new_eip150_test().engine, ChainEra::Eip150 => ethereum::new_eip150_test().engine,
ChainEra::Eip161 => ethereum::new_eip161_test().engine,
ChainEra::TransitionTest => ethereum::new_transition_test().engine, ChainEra::TransitionTest => ethereum::new_transition_test().engine,
}; };

View File

@ -26,8 +26,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
let old_schedule = evm::Schedule::new_frontier(); let old_schedule = evm::Schedule::new_frontier();
let new_schedule = evm::Schedule::new_homestead(); let new_schedule = evm::Schedule::new_homestead();
for (name, test) in tests.into_iter() { for (name, test) in tests.into_iter() {
let mut fail = false; let mut fail_unless = |cond: bool, title: &str| if !cond { failed.push(name.clone()); println!("Transaction failed: {:?}: {:?}", name, title); };
let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.clone()); println!("Transaction failed: {:?}", name); fail = true };
let number: Option<u64> = test.block_number.map(Into::into); let number: Option<u64> = test.block_number.map(Into::into);
let schedule = match number { let schedule = match number {
@ -35,27 +34,34 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
Some(x) if x < 1_150_000 => &old_schedule, Some(x) if x < 1_150_000 => &old_schedule,
Some(_) => &new_schedule Some(_) => &new_schedule
}; };
let allow_network_id_of_one = number.map_or(false, |n| n >= 3_500_000);
let rlp: Vec<u8> = test.rlp.into(); let rlp: Vec<u8> = test.rlp.into();
let res = UntrustedRlp::new(&rlp) let res = UntrustedRlp::new(&rlp)
.as_val() .as_val()
.map_err(From::from) .map_err(From::from)
.and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call)); .and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one));
fail_unless(test.transaction.is_none() == res.is_err()); fail_unless(test.transaction.is_none() == res.is_err(), "Validity different");
if let (Some(tx), Some(sender)) = (test.transaction, test.sender) { if let (Some(tx), Some(sender)) = (test.transaction, test.sender) {
let t = res.unwrap(); let t = res.unwrap();
fail_unless(t.sender().unwrap() == sender.into()); fail_unless(t.sender().unwrap() == sender.into(), "sender mismatch");
let is_acceptable_network_id = match t.network_id() {
None => true,
Some(1) if allow_network_id_of_one => true,
_ => false,
};
fail_unless(is_acceptable_network_id, "Network ID unacceptable");
let data: Vec<u8> = tx.data.into(); let data: Vec<u8> = tx.data.into();
fail_unless(t.data == data); fail_unless(t.data == data, "data mismatch");
fail_unless(t.gas_price == tx.gas_price.into()); fail_unless(t.gas_price == tx.gas_price.into(), "gas_price mismatch");
fail_unless(t.nonce == tx.nonce.into()); fail_unless(t.nonce == tx.nonce.into(), "nonce mismatch");
fail_unless(t.value == tx.value.into()); fail_unless(t.value == tx.value.into(), "value mismatch");
let to: Option<ethjson::hash::Address> = tx.to.into(); let to: Option<ethjson::hash::Address> = tx.to.into();
let to: Option<Address> = to.map(Into::into); let to: Option<Address> = to.map(Into::into);
match t.action { match t.action {
Action::Call(dest) => fail_unless(Some(dest) == to), Action::Call(dest) => fail_unless(Some(dest) == to, "call/destination mismatch"),
Action::Create => fail_unless(None == to), Action::Create => fail_unless(None == to, "create mismatch"),
} }
} }
} }
@ -73,3 +79,7 @@ declare_test!{TransactionTests_Homestead_ttTransactionTest, "TransactionTests/Ho
declare_test!{heavy => TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"} declare_test!{heavy => TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"}
declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"} declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"}
declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"} declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"}
declare_test!{TransactionTests_Homestead_ttTransactionTestEip155VitaliksTests, "TransactionTests/Homestead/ttTransactionTestEip155VitaliksTests"}
declare_test!{TransactionTests_EIP155_ttTransactionTest, "TransactionTests/EIP155/ttTransactionTest"}
declare_test!{TransactionTests_EIP155_ttTransactionTestEip155VitaliksTests, "TransactionTests/EIP155/ttTransactionTestEip155VitaliksTests"}
declare_test!{TransactionTests_EIP155_ttTransactionTestVRule, "TransactionTests/EIP155/ttTransactionTestVRule"}

View File

@ -137,6 +137,7 @@ pub mod miner;
pub mod snapshot; pub mod snapshot;
pub mod action_params; pub mod action_params;
pub mod db; pub mod db;
pub mod verification;
#[macro_use] pub mod evm; #[macro_use] pub mod evm;
mod cache_manager; mod cache_manager;
@ -150,7 +151,6 @@ mod account_db;
mod builtin; mod builtin;
mod executive; mod executive;
mod externalities; mod externalities;
mod verification;
mod blockchain; mod blockchain;
mod types; mod types;
mod factory; mod factory;

View File

@ -245,7 +245,7 @@ mod tests {
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::from(10), gas_price: U256::from(10),
nonce: U256::from(0), nonce: U256::from(0),
}.sign(keypair.secret()) }.sign(keypair.secret(), None)
} }
fn unwrap_err(res: Result<TransactionImportResult, Error>) -> TransactionError { fn unwrap_err(res: Result<TransactionImportResult, Error>) -> TransactionError {

View File

@ -21,7 +21,7 @@ use util::*;
use util::using_queue::{UsingQueue, GetAction}; use util::using_queue::{UsingQueue, GetAction};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use views::{BlockView, HeaderView}; use views::{BlockView, HeaderView};
use state::State; use state::{State, CleanupMode};
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
use executive::contract_address; use executive::contract_address;
use block::{ClosedBlock, SealedBlock, IsBlock, Block}; use block::{ClosedBlock, SealedBlock, IsBlock, Block};
@ -650,7 +650,7 @@ impl MinerService for Miner {
let needed_balance = t.value + t.gas * t.gas_price; let needed_balance = t.value + t.gas * t.gas_price;
if balance < needed_balance { if balance < needed_balance {
// give the sender a sufficient balance // give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance)); state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
} }
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options)); let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options));
@ -935,6 +935,8 @@ impl MinerService for Miner {
} }
}, },
logs: receipt.logs.clone(), logs: receipt.logs.clone(),
log_bloom: receipt.log_bloom,
state_root: receipt.state_root,
} }
}) })
} }
@ -1173,7 +1175,7 @@ mod tests {
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero(), nonce: U256::zero(),
}.sign(keypair.secret()) }.sign(keypair.secret(), None)
} }
#[test] #[test]
@ -1249,7 +1251,7 @@ mod tests {
#[test] #[test]
fn internal_seals_without_work() { fn internal_seals_without_work() {
let miner = Miner::with_spec(&Spec::new_test_instant()); let miner = Miner::with_spec(&Spec::new_instant());
let c = generate_dummy_client(2); let c = generate_dummy_client(2);
let client = c.reference().as_ref(); let client = c.reference().as_ref();

View File

@ -42,8 +42,8 @@
//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), //! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) }; //! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) };
//! //!
//! let st1 = t1.sign(&key.secret()); //! let st1 = t1.sign(&key.secret(), None);
//! let st2 = t2.sign(&key.secret()); //! let st2 = t2.sign(&key.secret(), None);
//! let default_account_details = |_a: &Address| AccountDetails { //! let default_account_details = |_a: &Address| AccountDetails {
//! nonce: U256::from(10), //! nonce: U256::from(10),
//! balance: U256::from(1_000_000), //! balance: U256::from(1_000_000),
@ -1114,12 +1114,12 @@ mod test {
fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction { fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret()) new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret(), None)
} }
fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction { fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret()) new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret(), None)
} }
fn new_tx_default() -> SignedTransaction { fn new_tx_default() -> SignedTransaction {
@ -1143,7 +1143,7 @@ mod test {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let secret = &keypair.secret(); let secret = &keypair.secret();
(tx1.sign(secret), tx2.sign(secret)) (tx1.sign(secret, None), tx2.sign(secret, None))
} }
/// Returns two consecutive transactions, both with increased gas price /// Returns two consecutive transactions, both with increased gas price
@ -1154,7 +1154,7 @@ mod test {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let secret = &keypair.secret(); let secret = &keypair.secret();
(tx1.sign(secret), tx2.sign(secret)) (tx1.sign(secret, None), tx2.sign(secret, None))
} }
fn new_tx_pair_default(nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { fn new_tx_pair_default(nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
@ -1808,9 +1808,9 @@ mod test {
let mut txq = TransactionQueue::default(); let mut txq = TransactionQueue::default();
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let secret = kp.secret(); let secret = kp.secret();
let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret); let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret, None);
let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret); let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None);
let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret); let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None);
txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap();
assert_eq!(txq.status().pending, 1); assert_eq!(txq.status().pending, 1);
@ -2048,11 +2048,11 @@ mod test {
// given // given
let mut txq = TransactionQueue::default(); let mut txq = TransactionQueue::default();
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret()); let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret(), None);
let tx2 = { let tx2 = {
let mut tx2 = (*tx).clone(); let mut tx2 = (*tx).clone();
tx2.gas_price = U256::from(200); tx2.gas_price = U256::from(200);
tx2.sign(keypair.secret()) tx2.sign(keypair.secret(), None)
}; };
// when // when
@ -2071,16 +2071,16 @@ mod test {
// given // given
let mut txq = TransactionQueue::default(); let mut txq = TransactionQueue::default();
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret()); let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret(), None);
let tx1 = { let tx1 = {
let mut tx1 = (*tx0).clone(); let mut tx1 = (*tx0).clone();
tx1.nonce = U256::from(124); tx1.nonce = U256::from(124);
tx1.sign(keypair.secret()) tx1.sign(keypair.secret(), None)
}; };
let tx2 = { let tx2 = {
let mut tx2 = (*tx1).clone(); let mut tx2 = (*tx1).clone();
tx2.gas_price = U256::from(200); tx2.gas_price = U256::from(200);
tx2.sign(keypair.secret()) tx2.sign(keypair.secret(), None)
}; };
// when // when
@ -2233,7 +2233,7 @@ mod test {
let tx3 = new_unsigned_tx(nonce + 2.into(), gas, 1.into()); let tx3 = new_unsigned_tx(nonce + 2.into(), gas, 1.into());
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret)) (tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None))
}; };
let sender = tx1.sender().unwrap(); let sender = tx1.sender().unwrap();
txq.add(tx1, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); txq.add(tx1, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap();

View File

@ -89,7 +89,7 @@ impl From<ethjson::blockchain::Account> for PodAccount {
let key: U256 = key.into(); let key: U256 = key.into();
let value: U256 = value.into(); let value: U256 = value.into();
(H256::from(key), H256::from(value)) (H256::from(key), H256::from(value))
}).collect() }).collect(),
} }
} }
} }
@ -99,8 +99,12 @@ impl From<ethjson::spec::Account> for PodAccount {
PodAccount { PodAccount {
balance: a.balance.map_or_else(U256::zero, Into::into), balance: a.balance.map_or_else(U256::zero, Into::into),
nonce: a.nonce.map_or_else(U256::zero, Into::into), nonce: a.nonce.map_or_else(U256::zero, Into::into),
code: a.code.map(Into::into).or_else(|| Some(Vec::new())), code: Some(a.code.map_or_else(Vec::new, Into::into)),
storage: BTreeMap::new() storage: a.storage.map_or_else(BTreeMap::new, |s| s.into_iter().map(|(key, value)| {
let key: U256 = key.into();
let value: U256 = value.into();
(H256::from(key), H256::from(value))
}).collect()),
} }
} }
} }
@ -112,7 +116,7 @@ impl fmt::Display for PodAccount {
self.nonce, self.nonce,
self.code.as_ref().map_or(0, |c| c.len()), self.code.as_ref().map_or(0, |c| c.len()),
self.code.as_ref().map_or_else(H256::new, |c| c.sha3()), self.code.as_ref().map_or_else(H256::new, |c| c.sha3()),
self.storage.len() self.storage.len(),
) )
} }
} }

View File

@ -19,11 +19,11 @@
use account_db::{AccountDB, AccountDBMut}; use account_db::{AccountDB, AccountDBMut};
use snapshot::Error; use snapshot::Error;
use util::{U256, FixedHash, H256, Bytes, HashDB, DBValue, SHA3_EMPTY, SHA3_NULL_RLP}; use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
use util::trie::{TrieDB, Trie}; use util::trie::{TrieDB, Trie};
use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View}; use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
use std::collections::{HashMap, HashSet}; use std::collections::HashSet;
// An empty account -- these are replaced with RLP null data for a space optimization. // An empty account -- these are replaced with RLP null data for a space optimization.
const ACC_EMPTY: Account = Account { const ACC_EMPTY: Account = Account {
@ -150,7 +150,6 @@ impl Account {
pub fn from_fat_rlp( pub fn from_fat_rlp(
acct_db: &mut AccountDBMut, acct_db: &mut AccountDBMut,
rlp: UntrustedRlp, rlp: UntrustedRlp,
code_map: &HashMap<H256, Bytes>,
) -> Result<(Self, Option<Bytes>), Error> { ) -> Result<(Self, Option<Bytes>), Error> {
use util::{TrieDBMut, TrieMut}; use util::{TrieDBMut, TrieMut};
@ -177,9 +176,6 @@ impl Account {
} }
CodeState::Hash => { CodeState::Hash => {
let code_hash = try!(rlp.val_at(3)); let code_hash = try!(rlp.val_at(3));
if let Some(code) = code_map.get(&code_hash) {
acct_db.emplace(code_hash.clone(), DBValue::from_slice(code));
}
(code_hash, None) (code_hash, None)
} }
@ -229,7 +225,7 @@ mod tests {
use util::{Address, FixedHash, H256, HashDB, DBValue}; use util::{Address, FixedHash, H256, HashDB, DBValue};
use rlp::{UntrustedRlp, View}; use rlp::{UntrustedRlp, View};
use std::collections::{HashSet, HashMap}; use std::collections::HashSet;
use super::{ACC_EMPTY, Account}; use super::{ACC_EMPTY, Account};
@ -250,7 +246,7 @@ mod tests {
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap(); let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
let fat_rlp = UntrustedRlp::new(&fat_rlp); let fat_rlp = UntrustedRlp::new(&fat_rlp);
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, &Default::default()).unwrap().0, account); assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
} }
#[test] #[test]
@ -275,7 +271,7 @@ mod tests {
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap(); let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
let fat_rlp = UntrustedRlp::new(&fat_rlp); let fat_rlp = UntrustedRlp::new(&fat_rlp);
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, &Default::default()).unwrap().0, account); assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
} }
#[test] #[test]
@ -318,12 +314,11 @@ mod tests {
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1); let fat_rlp1 = UntrustedRlp::new(&fat_rlp1);
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2); let fat_rlp2 = UntrustedRlp::new(&fat_rlp2);
let code_map = HashMap::new(); let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap();
let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, &code_map).unwrap();
assert!(maybe_code.is_none()); assert!(maybe_code.is_none());
assert_eq!(acc, account2); assert_eq!(acc, account2);
let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1, &code_map).unwrap(); let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap();
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
assert_eq!(acc, account1); assert_eq!(acc, account1);
} }
@ -332,9 +327,8 @@ mod tests {
fn encoding_empty_acc() { fn encoding_empty_acc() {
let mut db = get_temp_state_db(); let mut db = get_temp_state_db();
let mut used_code = HashSet::new(); let mut used_code = HashSet::new();
let code_map = HashMap::new();
assert_eq!(ACC_EMPTY.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec()); assert_eq!(ACC_EMPTY.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec());
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), &code_map).unwrap(), (ACC_EMPTY, None)); assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP)).unwrap(), (ACC_EMPTY, None));
} }
} }

View File

@ -45,6 +45,8 @@ pub enum Error {
MissingCode(Vec<H256>), MissingCode(Vec<H256>),
/// Unrecognized code encoding. /// Unrecognized code encoding.
UnrecognizedCodeState(u8), UnrecognizedCodeState(u8),
/// Restoration aborted.
RestorationAborted,
/// Trie error. /// Trie error.
Trie(TrieError), Trie(TrieError),
/// Decoder error. /// Decoder error.
@ -67,6 +69,7 @@ impl fmt::Display for Error {
a pruned database. Please re-run with the --pruning archive flag."), a pruned database. Please re-run with the --pruning archive flag."),
Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()),
Error::UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state), Error::UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state),
Error::RestorationAborted => write!(f, "Snapshot restoration aborted."),
Error::Io(ref err) => err.fmt(f), Error::Io(ref err) => err.fmt(f),
Error::Decoder(ref err) => err.fmt(f), Error::Decoder(ref err) => err.fmt(f),
Error::Trie(ref err) => err.fmt(f), Error::Trie(ref err) => err.fmt(f),

View File

@ -389,7 +389,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
pub struct StateRebuilder { pub struct StateRebuilder {
db: Box<JournalDB>, db: Box<JournalDB>,
state_root: H256, state_root: H256,
code_map: HashMap<H256, Bytes>, // maps code hashes to code itself. known_code: HashMap<H256, H256>, // code hashes mapped to first account with this code.
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code. missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
bloom: Bloom, bloom: Bloom,
} }
@ -400,43 +400,41 @@ impl StateRebuilder {
StateRebuilder { StateRebuilder {
db: journaldb::new(db.clone(), pruning, ::db::COL_STATE), db: journaldb::new(db.clone(), pruning, ::db::COL_STATE),
state_root: SHA3_NULL_RLP, state_root: SHA3_NULL_RLP,
code_map: HashMap::new(), known_code: HashMap::new(),
missing_code: HashMap::new(), missing_code: HashMap::new(),
bloom: StateDB::load_bloom(&*db), bloom: StateDB::load_bloom(&*db),
} }
} }
/// Feed an uncompressed state chunk into the rebuilder. /// Feed an uncompressed state chunk into the rebuilder.
pub fn feed(&mut self, chunk: &[u8]) -> Result<(), ::error::Error> { pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> {
let rlp = UntrustedRlp::new(chunk); let rlp = UntrustedRlp::new(chunk);
let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp(); let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp();
let account_fat_rlps: Vec<_> = rlp.iter().map(|r| r.as_raw()).collect();
let mut pairs = Vec::with_capacity(rlp.item_count()); let mut pairs = Vec::with_capacity(rlp.item_count());
// initialize the pairs vector with empty values so we have slots to write into. // initialize the pairs vector with empty values so we have slots to write into.
pairs.resize(rlp.item_count(), (H256::new(), Vec::new())); pairs.resize(rlp.item_count(), (H256::new(), Vec::new()));
let chunk_size = account_fat_rlps.len() / ::num_cpus::get() + 1; let status = try!(rebuild_accounts(
self.db.as_hashdb_mut(),
rlp,
&mut pairs,
&self.known_code,
flag
));
// new code contained within this chunk. for (addr_hash, code_hash) in status.missing_code {
let mut chunk_code = HashMap::new(); self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
let code_map = &self.code_map;
let status = try!(rebuild_accounts(self.db.as_hashdb_mut(), account_chunk, out_pairs_chunk, code_map));
chunk_code.extend(status.new_code);
for (addr_hash, code_hash) in status.missing_code {
self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
}
} }
// patch up all missing code. must be done after collecting all new missing code entries. // patch up all missing code. must be done after collecting all new missing code entries.
for (code_hash, code) in chunk_code { for (code_hash, code, first_with) in status.new_code {
for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) { for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) {
let mut db = AccountDBMut::from_hash(self.db.as_hashdb_mut(), addr_hash); let mut db = AccountDBMut::from_hash(self.db.as_hashdb_mut(), addr_hash);
db.emplace(code_hash, DBValue::from_slice(&code)); db.emplace(code_hash, DBValue::from_slice(&code));
} }
self.code_map.insert(code_hash, code); self.known_code.insert(code_hash, first_with);
} }
let backing = self.db.backing().clone(); let backing = self.db.backing().clone();
@ -450,6 +448,8 @@ impl StateRebuilder {
}; };
for (hash, thin_rlp) in pairs { for (hash, thin_rlp) in pairs {
if !flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
if &thin_rlp[..] != &empty_rlp[..] { if &thin_rlp[..] != &empty_rlp[..] {
self.bloom.set(&*hash); self.bloom.set(&*hash);
} }
@ -482,38 +482,55 @@ impl StateRebuilder {
#[derive(Default)] #[derive(Default)]
struct RebuiltStatus { struct RebuiltStatus {
new_code: Vec<(H256, Bytes)>, // new code that's become available. // new code that's become available. (code_hash, code, addr_hash)
new_code: Vec<(H256, Bytes, H256)>,
missing_code: Vec<(H256, H256)>, // accounts that are missing code. missing_code: Vec<(H256, H256)>, // accounts that are missing code.
} }
// rebuild a set of accounts and their storage. // rebuild a set of accounts and their storage.
// returns // returns a status detailing newly-loaded code and accounts missing code.
fn rebuild_accounts( fn rebuild_accounts(
db: &mut HashDB, db: &mut HashDB,
account_chunk: &[&[u8]], account_fat_rlps: UntrustedRlp,
out_chunk: &mut [(H256, Bytes)], out_chunk: &mut [(H256, Bytes)],
code_map: &HashMap<H256, Bytes> known_code: &HashMap<H256, H256>,
) -> Result<RebuiltStatus, ::error::Error> abort_flag: &AtomicBool,
{ ) -> Result<RebuiltStatus, ::error::Error> {
let mut status = RebuiltStatus::default(); let mut status = RebuiltStatus::default();
for (account_pair, out) in account_chunk.into_iter().zip(out_chunk) { for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk) {
let account_rlp = UntrustedRlp::new(account_pair); if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
let hash: H256 = try!(account_rlp.val_at(0)); let hash: H256 = try!(account_rlp.val_at(0));
let fat_rlp = try!(account_rlp.at(1)); let fat_rlp = try!(account_rlp.at(1));
let thin_rlp = { let thin_rlp = {
let mut acct_db = AccountDBMut::from_hash(db, hash);
// fill out the storage trie and code while decoding. // fill out the storage trie and code while decoding.
let (acc, maybe_code) = try!(Account::from_fat_rlp(&mut acct_db, fat_rlp, code_map)); let (acc, maybe_code) = {
let mut acct_db = AccountDBMut::from_hash(db, hash);
try!(Account::from_fat_rlp(&mut acct_db, fat_rlp))
};
let code_hash = acc.code_hash().clone(); let code_hash = acc.code_hash().clone();
match maybe_code { match maybe_code {
Some(code) => status.new_code.push((code_hash, code)), // new inline code
Some(code) => status.new_code.push((code_hash, code, hash)),
None => { None => {
if code_hash != ::util::SHA3_EMPTY && !code_map.contains_key(&code_hash) { if code_hash != ::util::SHA3_EMPTY {
status.missing_code.push((hash, code_hash)); // see if this code has already been included inline
match known_code.get(&code_hash) {
Some(&first_with) => {
// if so, load it from the database.
let code = try!(AccountDB::from_hash(db, first_with)
.get(&code_hash)
.ok_or_else(|| Error::MissingCode(vec![first_with])));
// and write it again under a different mangled key
AccountDBMut::from_hash(db, hash).emplace(code_hash, code);
}
// if not, queue it up to be filled later
None => status.missing_code.push((hash, code_hash)),
}
} }
} }
} }
@ -580,7 +597,7 @@ impl BlockRebuilder {
/// Feed the rebuilder an uncompressed block chunk. /// Feed the rebuilder an uncompressed block chunk.
/// Returns the number of blocks fed or any errors. /// Returns the number of blocks fed or any errors.
pub fn feed(&mut self, chunk: &[u8], engine: &Engine) -> Result<u64, ::error::Error> { pub fn feed(&mut self, chunk: &[u8], engine: &Engine, abort_flag: &AtomicBool) -> Result<u64, ::error::Error> {
use basic_types::Seal::With; use basic_types::Seal::With;
use util::U256; use util::U256;
use util::triehash::ordered_trie_root; use util::triehash::ordered_trie_root;
@ -601,6 +618,8 @@ impl BlockRebuilder {
let parent_total_difficulty = try!(rlp.val_at::<U256>(2)); let parent_total_difficulty = try!(rlp.val_at::<U256>(2));
for idx in 3..item_count { for idx in 3..item_count {
if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
let pair = try!(rlp.at(idx)); let pair = try!(rlp.at(idx));
let abridged_rlp = try!(pair.at(0)).as_raw().to_owned(); let abridged_rlp = try!(pair.at(0)).as_raw().to_owned();
let abridged_block = AbridgedBlock::from_raw(abridged_rlp); let abridged_block = AbridgedBlock::from_raw(abridged_rlp);

View File

@ -119,12 +119,12 @@ impl Restoration {
}) })
} }
// feeds a state chunk // feeds a state chunk, aborts early if `flag` becomes false.
fn feed_state(&mut self, hash: H256, chunk: &[u8]) -> Result<(), Error> { fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> {
if self.state_chunks_left.remove(&hash) { if self.state_chunks_left.remove(&hash) {
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer)); let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
try!(self.state.feed(&self.snappy_buffer[..len])); try!(self.state.feed(&self.snappy_buffer[..len], flag));
if let Some(ref mut writer) = self.writer.as_mut() { if let Some(ref mut writer) = self.writer.as_mut() {
try!(writer.write_state_chunk(hash, chunk)); try!(writer.write_state_chunk(hash, chunk));
@ -135,11 +135,11 @@ impl Restoration {
} }
// feeds a block chunk // feeds a block chunk
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine) -> Result<(), Error> { fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine, flag: &AtomicBool) -> Result<(), Error> {
if self.block_chunks_left.remove(&hash) { if self.block_chunks_left.remove(&hash) {
let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer)); let len = try!(snappy::decompress_into(chunk, &mut self.snappy_buffer));
try!(self.blocks.feed(&self.snappy_buffer[..len], engine)); try!(self.blocks.feed(&self.snappy_buffer[..len], engine, flag));
if let Some(ref mut writer) = self.writer.as_mut() { if let Some(ref mut writer) = self.writer.as_mut() {
try!(writer.write_block_chunk(hash, chunk)); try!(writer.write_block_chunk(hash, chunk));
} }
@ -225,6 +225,7 @@ pub struct Service {
db_restore: Arc<DatabaseRestore>, db_restore: Arc<DatabaseRestore>,
progress: super::Progress, progress: super::Progress,
taking_snapshot: AtomicBool, taking_snapshot: AtomicBool,
restoring_snapshot: AtomicBool,
} }
impl Service { impl Service {
@ -245,6 +246,7 @@ impl Service {
db_restore: params.db_restore, db_restore: params.db_restore,
progress: Default::default(), progress: Default::default(),
taking_snapshot: AtomicBool::new(false), taking_snapshot: AtomicBool::new(false),
restoring_snapshot: AtomicBool::new(false),
}; };
// create the root snapshot dir if it doesn't exist. // create the root snapshot dir if it doesn't exist.
@ -437,6 +439,8 @@ impl Service {
state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32, state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32,
block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32, block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32,
}; };
self.restoring_snapshot.store(true, Ordering::SeqCst);
Ok(()) Ok(())
} }
@ -491,8 +495,8 @@ impl Service {
}; };
(match is_state { (match is_state {
true => rest.feed_state(hash, chunk), true => rest.feed_state(hash, chunk, &self.restoring_snapshot),
false => rest.feed_blocks(hash, chunk, &*self.engine), false => rest.feed_blocks(hash, chunk, &*self.engine, &self.restoring_snapshot),
}.map(|_| rest.is_done()), rest.db.clone()) }.map(|_| rest.is_done()), rest.db.clone())
}; };
@ -574,6 +578,7 @@ impl SnapshotService for Service {
} }
fn abort_restore(&self) { fn abort_restore(&self) {
self.restoring_snapshot.store(false, Ordering::SeqCst);
*self.restoration.lock() = None; *self.restoration.lock() = None;
*self.status.lock() = RestorationStatus::Inactive; *self.status.lock() = RestorationStatus::Inactive;
} }

View File

@ -17,10 +17,11 @@
//! Block chunker and rebuilder tests. //! Block chunker and rebuilder tests.
use devtools::RandomTempPath; use devtools::RandomTempPath;
use error::Error;
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer}; use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
use blockchain::BlockChain; use blockchain::BlockChain;
use snapshot::{chunk_blocks, BlockRebuilder, Progress}; use snapshot::{chunk_blocks, BlockRebuilder, Error as SnapshotError, Progress};
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
use util::{Mutex, snappy}; use util::{Mutex, snappy};
@ -28,6 +29,7 @@ use util::kvdb::{Database, DatabaseConfig};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use spec::Spec; use spec::Spec;
@ -79,10 +81,11 @@ fn chunk_and_restore(amount: u64) {
let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), &manifest).unwrap(); let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), &manifest).unwrap();
let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); let reader = PackedReader::new(&snapshot_path).unwrap().unwrap();
let engine = ::engines::NullEngine::new(Default::default(), Default::default()); let engine = ::engines::NullEngine::new(Default::default(), Default::default());
let flag = AtomicBool::new(true);
for chunk_hash in &reader.manifest().block_hashes { for chunk_hash in &reader.manifest().block_hashes {
let compressed = reader.chunk(*chunk_hash).unwrap(); let compressed = reader.chunk(*chunk_hash).unwrap();
let chunk = snappy::decompress(&compressed).unwrap(); let chunk = snappy::decompress(&compressed).unwrap();
rebuilder.feed(&chunk, &engine).unwrap(); rebuilder.feed(&chunk, &engine, &flag).unwrap();
} }
rebuilder.finalize(HashMap::new()).unwrap(); rebuilder.finalize(HashMap::new()).unwrap();
@ -97,3 +100,46 @@ fn chunk_and_restore_500() { chunk_and_restore(500) }
#[test] #[test]
fn chunk_and_restore_40k() { chunk_and_restore(40000) } fn chunk_and_restore_40k() { chunk_and_restore(40000) }
#[test]
fn checks_flag() {
use ::rlp::{RlpStream, Stream};
use util::H256;
let mut stream = RlpStream::new_list(5);
stream.append(&100u64)
.append(&H256::default())
.append(&(!0u64));
stream.append_empty_data().append_empty_data();
let genesis = {
let mut canon_chain = ChainGenerator::default();
let mut finalizer = BlockFinalizer::default();
canon_chain.generate(&mut finalizer).unwrap()
};
let chunk = stream.out();
let path = RandomTempPath::create_dir();
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let db = Arc::new(Database::open(&db_cfg, path.as_str()).unwrap());
let chain = BlockChain::new(Default::default(), &genesis, db.clone());
let engine = ::engines::NullEngine::new(Default::default(), Default::default());
let manifest = ::snapshot::ManifestData {
state_hashes: Vec::new(),
block_hashes: Vec::new(),
state_root: ::util::sha3::SHA3_NULL_RLP,
block_number: 102,
block_hash: H256::default(),
};
let mut rebuilder = BlockRebuilder::new(chain, db.clone(), &manifest).unwrap();
match rebuilder.feed(&chunk, &engine, &AtomicBool::new(false)) {
Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {}
_ => panic!("Wrong result on abort flag set")
}
}

View File

@ -16,10 +16,13 @@
//! State snapshotting tests. //! State snapshotting tests.
use snapshot::{chunk_state, Progress, StateRebuilder}; use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder};
use snapshot::account::Account;
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
use super::helpers::{compare_dbs, StateProducer}; use super::helpers::{compare_dbs, StateProducer};
use error::Error;
use rand::{XorShiftRng, SeedableRng}; use rand::{XorShiftRng, SeedableRng};
use util::hash::H256; use util::hash::H256;
use util::journaldb::{self, Algorithm}; use util::journaldb::{self, Algorithm};
@ -28,7 +31,10 @@ use util::memorydb::MemoryDB;
use util::Mutex; use util::Mutex;
use devtools::RandomTempPath; use devtools::RandomTempPath;
use util::sha3::SHA3_NULL_RLP;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::AtomicBool;
#[test] #[test]
fn snap_and_restore() { fn snap_and_restore() {
@ -65,11 +71,13 @@ fn snap_and_restore() {
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive); let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
let reader = PackedReader::new(&snap_file).unwrap().unwrap(); let reader = PackedReader::new(&snap_file).unwrap().unwrap();
let flag = AtomicBool::new(true);
for chunk_hash in &reader.manifest().state_hashes { for chunk_hash in &reader.manifest().state_hashes {
let raw = reader.chunk(*chunk_hash).unwrap(); let raw = reader.chunk(*chunk_hash).unwrap();
let chunk = ::util::snappy::decompress(&raw).unwrap(); let chunk = ::util::snappy::decompress(&raw).unwrap();
rebuilder.feed(&chunk).unwrap(); rebuilder.feed(&chunk, &flag).unwrap();
} }
assert_eq!(rebuilder.state_root(), state_root); assert_eq!(rebuilder.state_root(), state_root);
@ -82,3 +90,104 @@ fn snap_and_restore() {
compare_dbs(&old_db, new_db.as_hashdb()); compare_dbs(&old_db, new_db.as_hashdb());
} }
#[test]
fn get_code_from_prev_chunk() {
use std::collections::HashSet;
use rlp::{RlpStream, Stream};
use util::{HashDB, H256, FixedHash, U256, Hashable};
use account_db::{AccountDBMut, AccountDB};
let code = b"this is definitely code";
let mut used_code = HashSet::new();
let mut acc_stream = RlpStream::new_list(4);
acc_stream.append(&U256::default())
.append(&U256::default())
.append(&SHA3_NULL_RLP)
.append(&code.sha3());
let (h1, h2) = (H256::random(), H256::random());
// two accounts with the same code, one per chunk.
// first one will have code inlined,
// second will just have its hash.
let thin_rlp = acc_stream.out();
let acc1 = Account::from_thin_rlp(&thin_rlp);
let acc2 = Account::from_thin_rlp(&thin_rlp);
let mut make_chunk = |acc: Account, hash| {
let mut db = MemoryDB::new();
AccountDBMut::from_hash(&mut db, hash).insert(&code[..]);
let fat_rlp = acc.to_fat_rlp(&AccountDB::from_hash(&db, hash), &mut used_code).unwrap();
let mut stream = RlpStream::new_list(1);
stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1);
stream.out()
};
let chunk1 = make_chunk(acc1, h1);
let chunk2 = make_chunk(acc2, h2);
let db_path = RandomTempPath::create_dir();
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
let mut rebuilder = StateRebuilder::new(new_db, Algorithm::Archive);
let flag = AtomicBool::new(true);
rebuilder.feed(&chunk1, &flag).unwrap();
rebuilder.feed(&chunk2, &flag).unwrap();
rebuilder.check_missing().unwrap();
}
#[test]
fn checks_flag() {
let mut producer = StateProducer::new();
let mut rng = XorShiftRng::from_seed([5, 6, 7, 8]);
let mut old_db = MemoryDB::new();
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
for _ in 0..10 {
producer.tick(&mut rng, &mut old_db);
}
let snap_dir = RandomTempPath::create_dir();
let mut snap_file = snap_dir.as_path().to_owned();
snap_file.push("SNAP");
let state_root = producer.state_root();
let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap());
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
writer.into_inner().finish(::snapshot::ManifestData {
state_hashes: state_hashes,
block_hashes: Vec::new(),
state_root: state_root,
block_number: 0,
block_hash: H256::default(),
}).unwrap();
let mut db_path = snap_dir.as_path().to_owned();
db_path.push("db");
{
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
let flag = AtomicBool::new(false);
for chunk_hash in &reader.manifest().state_hashes {
let raw = reader.chunk(*chunk_hash).unwrap();
let chunk = ::util::snappy::decompress(&raw).unwrap();
match rebuilder.feed(&chunk, &flag) {
Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {},
_ => panic!("unexpected result when feeding with flag off"),
}
}
}
}

View File

@ -38,7 +38,7 @@ pub struct CommonParams {
/// Maximum size of extra data. /// Maximum size of extra data.
pub maximum_extra_data_size: usize, pub maximum_extra_data_size: usize,
/// Network id. /// Network id.
pub network_id: U256, pub network_id: usize,
/// Main subprotocol name. /// Main subprotocol name.
pub subprotocol_name: String, pub subprotocol_name: String,
/// Minimum gas limit. /// Minimum gas limit.
@ -135,6 +135,12 @@ impl From<ethjson::spec::Spec> for Spec {
} }
} }
macro_rules! load_bundled {
($e:expr) => {
Spec::load(include_bytes!(concat!("../../res/", $e, ".json")) as &[u8]).expect(concat!("Chain spec ", $e, " is invalid."))
};
}
impl Spec { impl Spec {
/// Convert engine spec into a arc'd Engine of the right underlying type. /// Convert engine spec into a arc'd Engine of the right underlying type.
/// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
@ -161,9 +167,9 @@ impl Spec {
pub fn nodes(&self) -> &[String] { &self.nodes } pub fn nodes(&self) -> &[String] { &self.nodes }
/// Get the configured Network ID. /// Get the configured Network ID.
pub fn network_id(&self) -> U256 { self.params.network_id } pub fn network_id(&self) -> usize { self.params.network_id }
/// Get the configured Network ID. /// Get the configured subprotocol name.
pub fn subprotocol_name(&self) -> String { self.params.subprotocol_name.clone() } pub fn subprotocol_name(&self) -> String { self.params.subprotocol_name.clone() }
/// Get the configured network fork block. /// Get the configured network fork block.
@ -251,7 +257,7 @@ impl Spec {
} }
trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root); trace!(target: "spec", "ensure_db_good: Populated sec trie; root is {}", root);
for (address, account) in self.genesis_state.get().iter() { for (address, account) in self.genesis_state.get().iter() {
db.note_account_bloom(address); db.note_non_null_account(address);
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address)); account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address));
} }
assert!(db.as_hashdb().contains(&self.state_root())); assert!(db.as_hashdb().contains(&self.state_root()));
@ -268,25 +274,17 @@ impl Spec {
} }
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus. /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus.
pub fn new_test() -> Self { pub fn new_test() -> Spec { load_bundled!("null_morden") }
Spec::load(include_bytes!("../../res/null_morden.json") as &[u8]).expect("null_morden.json is invalid")
}
/// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is sha3(''). /// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is sha3('').
pub fn new_null() -> Self { pub fn new_null() -> Spec { load_bundled!("null") }
Spec::load(include_bytes!("../../res/null.json") as &[u8]).expect("null.json is invalid")
}
/// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring work). /// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring work).
pub fn new_test_instant() -> Self { pub fn new_instant() -> Spec { load_bundled!("instant_seal") }
Spec::load(include_bytes!("../../res/instant_seal.json") as &[u8]).expect("instant_seal.json is invalid")
}
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work). /// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work).
/// Account "0".sha3() and "1".sha3() are a authorities. /// Account "0".sha3() and "1".sha3() are a authorities.
pub fn new_test_tendermint() -> Self { pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") }
Spec::load(include_bytes!("../../res/tendermint.json") as &[u8]).expect("tendermint.json is invalid")
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -300,11 +300,17 @@ impl Account {
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() } pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
/// Check if account has zero nonce, balance, no code and no storage. /// Check if account has zero nonce, balance, no code and no storage.
///
/// NOTE: Will panic if `!self.storage_is_clean()`
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.storage_changes.is_empty() && assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean.");
self.is_null() && self.storage_root == SHA3_NULL_RLP
}
/// Check if account has zero nonce, balance, no code.
pub fn is_null(&self) -> bool {
self.balance.is_zero() && self.balance.is_zero() &&
self.nonce.is_zero() && self.nonce.is_zero() &&
self.storage_root == SHA3_NULL_RLP &&
self.code_hash == SHA3_EMPTY self.code_hash == SHA3_EMPTY
} }

View File

@ -199,6 +199,13 @@ enum RequireCache {
Code, Code,
} }
#[derive(PartialEq)]
pub enum CleanupMode<'a> {
ForceCreate,
NoEmpty,
KillEmpty(&'a mut HashSet<Address>),
}
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
Therefore creating a SecTrieDB with this state's root will not fail."; Therefore creating a SecTrieDB with this state's root will not fail.";
@ -329,8 +336,8 @@ impl State {
/// Create a new contract at address `contract`. If there is already an account at the address /// Create a new contract at address `contract`. If there is already an account at the address
/// it will have its code reset, ready for `init_code()`. /// it will have its code reset, ready for `init_code()`.
pub fn new_contract(&mut self, contract: &Address, balance: U256) { pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) {
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce)))); self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce + nonce_offset))));
} }
/// Remove an existing account. /// Remove an existing account.
@ -341,10 +348,15 @@ impl State {
/// Determine whether an account exists. /// Determine whether an account exists.
pub fn exists(&self, a: &Address) -> bool { pub fn exists(&self, a: &Address) -> bool {
// Bloom filter does not contain empty accounts, so it is important here to // Bloom filter does not contain empty accounts, so it is important here to
// check if account exists in the database directly before EIP-158 is in effect. // check if account exists in the database directly before EIP-161 is in effect.
self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) self.ensure_cached(a, RequireCache::None, false, |a| a.is_some())
} }
/// Determine whether an account exists and if not empty.
pub fn exists_and_not_null(&self, a: &Address) -> bool {
self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null()))
}
/// Get the balance of account `a`. /// Get the balance of account `a`.
pub fn balance(&self, a: &Address) -> U256 { pub fn balance(&self, a: &Address) -> U256 {
self.ensure_cached(a, RequireCache::None, true, self.ensure_cached(a, RequireCache::None, true,
@ -399,7 +411,7 @@ impl State {
} }
// check bloom before any requests to trie // check bloom before any requests to trie
if !self.db.check_account_bloom(address) { return H256::zero() } if !self.db.check_non_null_bloom(address) { return H256::zero() }
// account is not found in the global cache, get from the DB and insert into local // account is not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
@ -433,10 +445,18 @@ impl State {
} }
/// Add `incr` to the balance of account `a`. /// Add `incr` to the balance of account `a`.
pub fn add_balance(&mut self, a: &Address, incr: &U256) { pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) {
trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)); trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a));
if !incr.is_zero() || !self.exists(a) { let is_value_transfer = !incr.is_zero();
if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)) {
self.require(a, false).add_balance(incr); self.require(a, false).add_balance(incr);
} else {
match cleanup_mode {
CleanupMode::KillEmpty(set) => if !is_value_transfer && self.exists(a) && !self.exists_and_not_null(a) {
set.insert(a.clone());
},
_ => {}
}
} }
} }
@ -449,9 +469,9 @@ impl State {
} }
/// Subtracts `by` from the balance of `from` and adds it to that of `to`. /// Subtracts `by` from the balance of `from` and adds it to that of `to`.
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) { pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, cleanup_mode: CleanupMode) {
self.sub_balance(from, by); self.sub_balance(from, by);
self.add_balance(to, by); self.add_balance(to, by, cleanup_mode);
} }
/// Increment the nonce of account `a` by 1. /// Increment the nonce of account `a` by 1.
@ -507,13 +527,15 @@ impl State {
// first, commit the sub trees. // first, commit the sub trees.
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
if let Some(ref mut account) = a.account { if let Some(ref mut account) = a.account {
if !account.is_empty() {
db.note_account_bloom(address);
}
let addr_hash = account.address_hash(address); let addr_hash = account.address_hash(address);
let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash); {
account.commit_storage(&factories.trie, account_db.as_hashdb_mut()); let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash);
account.commit_code(account_db.as_hashdb_mut()); account.commit_storage(&factories.trie, account_db.as_hashdb_mut());
account.commit_code(account_db.as_hashdb_mut());
}
if !account.is_empty() {
db.note_non_null_account(address);
}
} }
} }
@ -653,7 +675,7 @@ impl State {
Some(r) => r, Some(r) => r,
None => { None => {
// first check bloom if it is not in database for sure // first check bloom if it is not in database for sure
if check_bloom && !self.db.check_account_bloom(a) { return f(None); } if check_bloom && !self.db.check_non_null_bloom(a) { return f(None); }
// not found in the global cache, get from the DB and insert into local // not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
@ -687,7 +709,7 @@ impl State {
match self.db.get_cached_account(a) { match self.db.get_cached_account(a) {
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
None => { None => {
let maybe_acc = if self.db.check_account_bloom(a) { let maybe_acc = if self.db.check_non_null_bloom(a) {
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
match db.get(a) { match db.get(a) {
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))), Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
@ -793,9 +815,9 @@ fn should_apply_create_transaction() {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -853,9 +875,9 @@ fn should_trace_failed_create_transaction() {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: FromHex::from_hex("5b600056").unwrap(), data: FromHex::from_hex("5b600056").unwrap(),
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -890,10 +912,10 @@ fn should_trace_call_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -933,9 +955,9 @@ fn should_trace_basic_call_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -975,7 +997,7 @@ fn should_trace_call_transaction_to_builtin() {
action: Action::Call(0x1.into()), action: Action::Call(0x1.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
let result = state.apply(&info, engine, &t, true).unwrap(); let result = state.apply(&info, engine, &t, true).unwrap();
@ -1017,7 +1039,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
let result = state.apply(&info, engine, &t, true).unwrap(); let result = state.apply(&info, engine, &t, true).unwrap();
@ -1060,7 +1082,7 @@ fn should_not_trace_callcode() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
@ -1122,7 +1144,7 @@ fn should_not_trace_delegatecall() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
@ -1181,10 +1203,10 @@ fn should_trace_failed_call_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1221,11 +1243,11 @@ fn should_trace_call_with_subcall_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
@ -1281,10 +1303,10 @@ fn should_trace_call_with_basic_subcall_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1336,10 +1358,10 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1379,11 +1401,11 @@ fn should_trace_failed_subcall_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![],//600480600b6000396000f35b600056 data: vec![],//600480600b6000396000f35b600056
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1435,12 +1457,12 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1510,12 +1532,12 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![],//600480600b6000396000f35b600056 data: vec![],//600480600b6000396000f35b600056
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
@ -1583,11 +1605,11 @@ fn should_trace_suicide() {
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3(), None);
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
state.add_balance(&0xa.into(), &50.into()); state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty);
state.add_balance(t.sender().as_ref().unwrap(), &100.into()); state.add_balance(t.sender().as_ref().unwrap(), &100.into(), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap(); let result = state.apply(&info, &engine, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1658,7 +1680,7 @@ fn get_from_database() {
let (root, db) = { let (root, db) = {
let mut state = get_temp_state_in(temp.as_path()); let mut state = get_temp_state_in(temp.as_path());
state.inc_nonce(&a); state.inc_nonce(&a);
state.add_balance(&a, &U256::from(69u64)); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
state.commit().unwrap(); state.commit().unwrap();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.drop() state.drop()
@ -1675,27 +1697,47 @@ fn remove() {
let mut state_result = get_temp_state(); let mut state_result = get_temp_state();
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
assert_eq!(state.exists(&a), false); assert_eq!(state.exists(&a), false);
assert_eq!(state.exists_and_not_null(&a), false);
state.inc_nonce(&a); state.inc_nonce(&a);
assert_eq!(state.exists(&a), true); assert_eq!(state.exists(&a), true);
assert_eq!(state.exists_and_not_null(&a), true);
assert_eq!(state.nonce(&a), U256::from(1u64)); assert_eq!(state.nonce(&a), U256::from(1u64));
state.kill_account(&a); state.kill_account(&a);
assert_eq!(state.exists(&a), false); assert_eq!(state.exists(&a), false);
assert_eq!(state.exists_and_not_null(&a), false);
assert_eq!(state.nonce(&a), U256::from(0u64)); assert_eq!(state.nonce(&a), U256::from(0u64));
} }
#[test] #[test]
fn empty_account_exists() { fn empty_account_is_not_created() {
let a = Address::zero(); let a = Address::zero();
let path = RandomTempPath::new(); let path = RandomTempPath::new();
let db = get_temp_state_db_in(path.as_path()); let db = get_temp_state_db_in(path.as_path());
let (root, db) = { let (root, db) = {
let mut state = State::new(db, U256::from(0), Default::default()); let mut state = State::new(db, U256::from(0), Default::default());
state.add_balance(&a, &U256::default()); // create an empty account state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty); // create an empty account
state.commit().unwrap();
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert!(!state.exists(&a));
assert!(!state.exists_and_not_null(&a));
}
#[test]
fn empty_account_exists_when_creation_forced() {
let a = Address::zero();
let path = RandomTempPath::new();
let db = get_temp_state_db_in(path.as_path());
let (root, db) = {
let mut state = State::new(db, U256::from(0), Default::default());
state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate); // create an empty account
state.commit().unwrap(); state.commit().unwrap();
state.drop() state.drop()
}; };
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert!(state.exists(&a)); assert!(state.exists(&a));
assert!(!state.exists_and_not_null(&a));
} }
#[test] #[test]
@ -1733,7 +1775,7 @@ fn alter_balance() {
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
let a = Address::zero(); let a = Address::zero();
let b = 1u64.into(); let b = 1u64.into();
state.add_balance(&a, &U256::from(69u64)); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.commit().unwrap(); state.commit().unwrap();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
@ -1741,7 +1783,7 @@ fn alter_balance() {
assert_eq!(state.balance(&a), U256::from(27u64)); assert_eq!(state.balance(&a), U256::from(27u64));
state.commit().unwrap(); state.commit().unwrap();
assert_eq!(state.balance(&a), U256::from(27u64)); assert_eq!(state.balance(&a), U256::from(27u64));
state.transfer_balance(&a, &b, &U256::from(18u64)); state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty);
assert_eq!(state.balance(&a), U256::from(9u64)); assert_eq!(state.balance(&a), U256::from(9u64));
assert_eq!(state.balance(&b), U256::from(18u64)); assert_eq!(state.balance(&b), U256::from(18u64));
state.commit().unwrap(); state.commit().unwrap();
@ -1794,12 +1836,12 @@ fn checkpoint_basic() {
let mut state = state_result.reference_mut(); let mut state = state_result.reference_mut();
let a = Address::zero(); let a = Address::zero();
state.checkpoint(); state.checkpoint();
state.add_balance(&a, &U256::from(69u64)); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.discard_checkpoint(); state.discard_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.checkpoint(); state.checkpoint();
state.add_balance(&a, &U256::from(1u64)); state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty);
assert_eq!(state.balance(&a), U256::from(70u64)); assert_eq!(state.balance(&a), U256::from(70u64));
state.revert_to_checkpoint(); state.revert_to_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
@ -1812,7 +1854,7 @@ fn checkpoint_nested() {
let a = Address::zero(); let a = Address::zero();
state.checkpoint(); state.checkpoint();
state.checkpoint(); state.checkpoint();
state.add_balance(&a, &U256::from(69u64)); state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
state.discard_checkpoint(); state.discard_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
@ -1835,7 +1877,7 @@ fn should_not_panic_on_state_diff_with_storage() {
let a: Address = 0xa.into(); let a: Address = 0xa.into();
state.init_code(&a, b"abcdefg".to_vec()); state.init_code(&a, b"abcdefg".to_vec());
state.add_balance(&a, &256.into()); state.add_balance(&a, &256.into(), CleanupMode::NoEmpty);
state.set_storage(&a, 0xb.into(), 0xc.into()); state.set_storage(&a, 0xb.into(), 0xc.into());
let mut new_state = state.clone(); let mut new_state = state.clone();

View File

@ -18,6 +18,8 @@
use std::collections::HashSet; use std::collections::HashSet;
use util::{Address, U256}; use util::{Address, U256};
use log_entry::LogEntry; use log_entry::LogEntry;
use evm::Schedule;
use super::CleanupMode;
/// State changes which should be applied in finalize, /// State changes which should be applied in finalize,
/// after transaction is fully executed. /// after transaction is fully executed.
@ -26,6 +28,9 @@ pub struct Substate {
/// Any accounts that have suicided. /// Any accounts that have suicided.
pub suicides: HashSet<Address>, pub suicides: HashSet<Address>,
/// Any accounts that are tagged for garbage collection.
pub garbage: HashSet<Address>,
/// Any logs. /// Any logs.
pub logs: Vec<LogEntry>, pub logs: Vec<LogEntry>,
@ -45,10 +50,20 @@ impl Substate {
/// Merge secondary substate `s` into self, accruing each element correspondingly. /// Merge secondary substate `s` into self, accruing each element correspondingly.
pub fn accrue(&mut self, s: Substate) { pub fn accrue(&mut self, s: Substate) {
self.suicides.extend(s.suicides.into_iter()); self.suicides.extend(s.suicides.into_iter());
self.garbage.extend(s.garbage.into_iter());
self.logs.extend(s.logs.into_iter()); self.logs.extend(s.logs.into_iter());
self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count; self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count;
self.contracts_created.extend(s.contracts_created.into_iter()); self.contracts_created.extend(s.contracts_created.into_iter());
} }
/// Get the cleanup mode object from this.
pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode {
match (schedule.no_empty, schedule.kill_empty) {
(false, _) => CleanupMode::ForceCreate,
(true, false) => CleanupMode::NoEmpty,
(true, true) => CleanupMode::KillEmpty(&mut self.garbage),
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -165,13 +165,13 @@ impl StateDB {
bloom bloom
} }
pub fn check_account_bloom(&self, address: &Address) -> bool { pub fn check_non_null_bloom(&self, address: &Address) -> bool {
trace!(target: "account_bloom", "Check account bloom: {:?}", address); trace!(target: "account_bloom", "Check account bloom: {:?}", address);
let bloom = self.account_bloom.lock(); let bloom = self.account_bloom.lock();
bloom.check(&*address.sha3()) bloom.check(&*address.sha3())
} }
pub fn note_account_bloom(&self, address: &Address) { pub fn note_non_null_account(&self, address: &Address) {
trace!(target: "account_bloom", "Note account bloom: {:?}", address); trace!(target: "account_bloom", "Note account bloom: {:?}", address);
let mut bloom = self.account_bloom.lock(); let mut bloom = self.account_bloom.lock();
bloom.set(&*address.sha3()); bloom.set(&*address.sha3());

View File

@ -16,6 +16,7 @@
use io::IoChannel; use io::IoChannel;
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID}; use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID};
use state::CleanupMode;
use ethereum; use ethereum;
use block::IsBlock; use block::IsBlock;
use tests::helpers::*; use tests::helpers::*;
@ -217,7 +218,7 @@ fn can_generate_gas_price_histogram() {
let client = client_result.reference(); let client = client_result.reference();
let hist = client.gas_price_histogram(20, 5).unwrap(); let hist = client.gas_price_histogram(20, 5).unwrap();
let correct_hist = Histogram { bucket_bounds: vec_into![643,2293,3943,5593,7243,8893], counts: vec![4,2,4,6,3] }; let correct_hist = Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] };
assert_eq!(hist, correct_hist); assert_eq!(hist, correct_hist);
} }
@ -272,7 +273,7 @@ fn change_history_size() {
let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected(), &db_config).unwrap(); let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected(), &db_config).unwrap();
for _ in 0..20 { for _ in 0..20 {
let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]); let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]);
b.block_mut().fields_mut().state.add_balance(&address, &5.into()); b.block_mut().fields_mut().state.add_balance(&address, &5.into(), CleanupMode::NoEmpty);
b.block_mut().fields_mut().state.commit().unwrap(); b.block_mut().fields_mut().state.commit().unwrap();
let b = b.close_and_lock().seal(&*test_spec.engine, vec![]).unwrap(); let b = b.close_and_lock().seal(&*test_spec.engine, vec![]).unwrap();
client.import_sealed_block(b).unwrap(); // account change is in the journal overlay client.import_sealed_block(b).unwrap(); // account change is in the journal overlay

View File

@ -28,6 +28,7 @@ use evm::Schedule;
use engines::Engine; use engines::Engine;
use env_info::EnvInfo; use env_info::EnvInfo;
use ethereum; use ethereum;
use ethereum::ethash::EthashParams;
use devtools::*; use devtools::*;
use miner::Miner; use miner::Miner;
use header::Header; use header::Header;
@ -40,6 +41,7 @@ pub enum ChainEra {
Frontier, Frontier,
Homestead, Homestead,
Eip150, Eip150,
Eip161,
TransitionTest, TransitionTest,
} }
@ -194,7 +196,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
action: Action::Create, action: Action::Create,
data: vec![], data: vec![],
value: U256::zero(), value: U256::zero(),
}.sign(kp.secret()), None).unwrap(); }.sign(kp.secret(), None), None).unwrap();
n += 1; n += 1;
} }
@ -420,3 +422,29 @@ pub fn get_bad_state_dummy_block() -> Bytes {
create_test_block(&block_header) create_test_block(&block_header)
} }
pub fn get_default_ethash_params() -> EthashParams{
EthashParams {
gas_limit_bound_divisor: U256::from(1024),
minimum_difficulty: U256::from(131072),
difficulty_bound_divisor: U256::from(2048),
difficulty_increment_divisor: 10,
duration_limit: 13,
block_reward: U256::from(0),
registrar: "0000000000000000000000000000000000000001".into(),
homestead_transition: 1150000,
dao_hardfork_transition: 0x7fffffffffffffff,
dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(),
dao_hardfork_accounts: vec![],
difficulty_hardfork_transition: 0x7fffffffffffffff,
difficulty_hardfork_bound_divisor: U256::from(0),
bomb_defuse_transition: 0x7fffffffffffffff,
eip150_transition: 0x7fffffffffffffff,
eip155_transition: 0x7fffffffffffffff,
eip160_transition: 0x7fffffffffffffff,
eip161abc_transition: 0x7fffffffffffffff,
eip161d_transition: 0x7fffffffffffffff,
ecip1010_pause_transition: 0x7fffffffffffffff,
ecip1010_continue_transition: 0x7fffffffffffffff
}
}

View File

@ -55,7 +55,7 @@ pub struct TraceId {
} }
/// Uniquely identifies Uncle. /// Uniquely identifies Uncle.
#[derive(Debug, Binary)] #[derive(Debug, PartialEq, Eq, Copy, Clone, Binary)]
pub struct UncleID { pub struct UncleID {
/// Block id. /// Block id.
pub block: BlockID, pub block: BlockID,

View File

@ -33,4 +33,4 @@ pub mod transaction_import;
pub mod block_import_error; pub mod block_import_error;
pub mod restoration_status; pub mod restoration_status;
pub mod snapshot_manifest; pub mod snapshot_manifest;
pub mod mode; pub mod mode;

View File

@ -20,7 +20,7 @@ pub use std::time::Duration;
use client::Mode as ClientMode; use client::Mode as ClientMode;
/// IPC-capable shadow-type for client::config::Mode /// IPC-capable shadow-type for client::config::Mode
#[derive(Clone, Binary)] #[derive(Clone, Binary, Debug)]
pub enum Mode { pub enum Mode {
/// Same as ClientMode::Off. /// Same as ClientMode::Off.
Off, Off,

View File

@ -93,6 +93,10 @@ pub struct RichReceipt {
pub contract_address: Option<Address>, pub contract_address: Option<Address>,
/// Logs /// Logs
pub logs: Vec<LogEntry>, pub logs: Vec<LogEntry>,
/// Logs bloom
pub log_bloom: LogBloom,
/// State root
pub state_root: H256,
} }
/// Receipt with additional info. /// Receipt with additional info.
@ -114,6 +118,10 @@ pub struct LocalizedReceipt {
pub contract_address: Option<Address>, pub contract_address: Option<Address>,
/// Logs /// Logs
pub logs: Vec<LocalizedLogEntry>, pub logs: Vec<LocalizedLogEntry>,
/// Logs bloom
pub log_bloom: LogBloom,
/// State root
pub state_root: H256,
} }
#[test] #[test]

View File

@ -72,8 +72,8 @@ pub struct Transaction {
impl Transaction { impl Transaction {
/// Append object with a without signature into RLP stream /// Append object with a without signature into RLP stream
pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream) { pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option<u8>) {
s.begin_list(6); s.begin_list(if let None = network_id { 6 } else { 9 });
s.append(&self.nonce); s.append(&self.nonce);
s.append(&self.gas_price); s.append(&self.gas_price);
s.append(&self.gas); s.append(&self.gas);
@ -83,6 +83,11 @@ impl Transaction {
}; };
s.append(&self.value); s.append(&self.value);
s.append(&self.data); s.append(&self.data);
if let Some(n) = network_id {
s.append(&n);
s.append(&0u8);
s.append(&0u8);
}
} }
} }
@ -105,7 +110,7 @@ impl From<ethjson::state::Transaction> for SignedTransaction {
}, },
value: t.value.into(), value: t.value.into(),
data: t.data.into(), data: t.data.into(),
}.sign(&t.secret.into()) }.sign(&t.secret.into(), None)
} }
} }
@ -135,26 +140,26 @@ impl From<ethjson::transaction::Transaction> for SignedTransaction {
impl Transaction { impl Transaction {
/// The message hash of the transaction. /// The message hash of the transaction.
pub fn hash(&self) -> H256 { pub fn hash(&self, network_id: Option<u8>) -> H256 {
let mut stream = RlpStream::new(); let mut stream = RlpStream::new();
self.rlp_append_unsigned_transaction(&mut stream); self.rlp_append_unsigned_transaction(&mut stream, network_id);
stream.out().sha3() stream.out().sha3()
} }
/// Signs the transaction as coming from `sender`. /// Signs the transaction as coming from `sender`.
pub fn sign(self, secret: &Secret) -> SignedTransaction { pub fn sign(self, secret: &Secret, network_id: Option<u8>) -> SignedTransaction {
let sig = ::ethkey::sign(secret, &self.hash()) let sig = ::ethkey::sign(secret, &self.hash(network_id))
.expect("data is valid and context has signing capabilities; qed"); .expect("data is valid and context has signing capabilities; qed");
self.with_signature(sig) self.with_signature(sig, network_id)
} }
/// Signs the transaction with signature. /// Signs the transaction with signature.
pub fn with_signature(self, sig: Signature) -> SignedTransaction { pub fn with_signature(self, sig: Signature, network_id: Option<u8>) -> SignedTransaction {
SignedTransaction { SignedTransaction {
unsigned: self, unsigned: self,
r: sig.r().into(), r: sig.r().into(),
s: sig.s().into(), s: sig.s().into(),
v: sig.v() + 27, v: sig.v() + if let Some(n) = network_id { 35 + n * 2 } else { 27 },
hash: Cell::new(None), hash: Cell::new(None),
sender: Cell::new(None), sender: Cell::new(None),
} }
@ -204,7 +209,8 @@ impl Transaction {
pub struct SignedTransaction { pub struct SignedTransaction {
/// Plain Transaction. /// Plain Transaction.
unsigned: Transaction, unsigned: Transaction,
/// The V field of the signature, either 27 or 28; helps describe the point on the curve. /// The V field of the signature; the LS bit described which half of the curve our point falls
/// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks.
v: u8, v: u8,
/// The R field of the signature; helps describe the point on the curve. /// The R field of the signature; helps describe the point on the curve.
r: U256, r: U256,
@ -266,7 +272,7 @@ impl HeapSizeOf for SignedTransaction {
impl SignedTransaction { impl SignedTransaction {
/// Append object with a signature into RLP stream /// Append object with a signature into RLP stream
pub fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
s.begin_list(9); s.begin_list(9);
s.append(&self.nonce); s.append(&self.nonce);
s.append(&self.gas_price); s.append(&self.gas_price);
@ -295,8 +301,16 @@ impl SignedTransaction {
} }
} }
/// 0 is `v` is 27, 1 if 28, and 4 otherwise. /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid.
pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } } pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => (v - 1) % 2, _ => 4 } }
/// The network ID, or `None` if this is a global transaction.
pub fn network_id(&self) -> Option<u8> {
match self.v {
v if v > 36 => Some((v - 35) / 2),
_ => None,
}
}
/// Construct a signature object from the sig. /// Construct a signature object from the sig.
pub fn signature(&self) -> Signature { pub fn signature(&self) -> Signature {
@ -327,20 +341,25 @@ impl SignedTransaction {
/// Returns the public key of the sender. /// Returns the public key of the sender.
pub fn public_key(&self) -> Result<Public, Error> { pub fn public_key(&self) -> Result<Public, Error> {
Ok(try!(recover(&self.signature(), &self.unsigned.hash()))) Ok(try!(recover(&self.signature(), &self.unsigned.hash(self.network_id()))))
} }
/// Do basic validation, checking for valid signature and minimum gas, /// Do basic validation, checking for valid signature and minimum gas,
// TODO: consider use in block validation. // TODO: consider use in block validation.
#[cfg(test)] #[cfg(test)]
#[cfg(feature = "json-tests")] #[cfg(feature = "json-tests")]
pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<SignedTransaction, Error> { pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool) -> Result<SignedTransaction, Error> {
if require_low && !self.signature().is_low_s() { if require_low && !self.signature().is_low_s() {
return Err(EthkeyError::InvalidSignature.into()) return Err(EthkeyError::InvalidSignature.into())
} }
match self.network_id() {
None => {},
Some(1) if allow_network_id_of_one => {},
_ => return Err(TransactionError::InvalidNetworkId.into()),
}
try!(self.sender()); try!(self.sender());
if self.gas < U256::from(self.gas_required(&schedule)) { if self.gas < U256::from(self.gas_required(&schedule)) {
Err(From::from(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}))) Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into())
} else { } else {
Ok(self) Ok(self)
} }
@ -380,6 +399,7 @@ fn sender_test() {
} else { panic!(); } } else { panic!(); }
assert_eq!(t.value, U256::from(0x0au64)); assert_eq!(t.value, U256::from(0x0au64));
assert_eq!(t.sender().unwrap(), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into()); assert_eq!(t.sender().unwrap(), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into());
assert_eq!(t.network_id(), None);
} }
#[test] #[test]
@ -394,8 +414,9 @@ fn signing() {
gas: U256::from(50_000), gas: U256::from(50_000),
value: U256::from(1), value: U256::from(1),
data: b"Hello!".to_vec() data: b"Hello!".to_vec()
}.sign(&key.secret()); }.sign(&key.secret(), None);
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap()); assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
assert_eq!(t.network_id(), None);
} }
#[test] #[test]
@ -409,7 +430,48 @@ fn fake_signing() {
data: b"Hello!".to_vec() data: b"Hello!".to_vec()
}.fake_sign(Address::from(0x69)); }.fake_sign(Address::from(0x69));
assert_eq!(Address::from(0x69), t.sender().unwrap()); assert_eq!(Address::from(0x69), t.sender().unwrap());
assert_eq!(t.network_id(), None);
let t = t.clone(); let t = t.clone();
assert_eq!(Address::from(0x69), t.sender().unwrap()); assert_eq!(Address::from(0x69), t.sender().unwrap());
assert_eq!(t.network_id(), None);
} }
#[test]
fn should_recover_from_network_specific_signing() {
use ethkey::{Random, Generator};
let key = Random.generate().unwrap();
let t = Transaction {
action: Action::Create,
nonce: U256::from(42),
gas_price: U256::from(3000),
gas: U256::from(50_000),
value: U256::from(1),
data: b"Hello!".to_vec()
}.sign(&key.secret(), Some(69));
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
assert_eq!(t.network_id(), Some(69));
}
#[test]
fn should_agree_with_vitalik() {
use rustc_serialize::hex::FromHex;
let test_vector = |tx_data: &str, address: &'static str| {
let signed: SignedTransaction = decode(&FromHex::from_hex(tx_data).unwrap());
signed.check_low_s().unwrap();
assert_eq!(signed.sender().unwrap(), address.into());
flushln!("networkid: {:?}", signed.network_id());
};
test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce")
test_vector("f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112")
test_vector("f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be")
test_vector("f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0")
test_vector("f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554")
test_vector("f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4")
test_vector("f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35")
test_vector("f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332")
test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029")
test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f")
}

View File

@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Canonical verifier.
use blockchain::BlockProvider; use blockchain::BlockProvider;
use engines::Engine; use engines::Engine;
use error::Error; use error::Error;
@ -21,6 +23,7 @@ use header::Header;
use super::Verifier; use super::Verifier;
use super::verification; use super::verification;
/// A canonial verifier -- this does full verification.
pub struct CanonVerifier; pub struct CanonVerifier;
impl Verifier for CanonVerifier { impl Verifier for CanonVerifier {

View File

@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Block verification utilities.
pub mod verification; pub mod verification;
pub mod verifier; pub mod verifier;
pub mod queue; pub mod queue;
@ -44,6 +46,7 @@ impl Default for VerifierType {
} }
} }
/// Create a new verifier based on type.
pub fn new(v: VerifierType) -> Box<Verifier> { pub fn new(v: VerifierType) -> Box<Verifier> {
match v { match v {
VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier),

View File

@ -14,12 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! No-op verifier.
use blockchain::BlockProvider; use blockchain::BlockProvider;
use engines::Engine; use engines::Engine;
use error::Error; use error::Error;
use header::Header; use header::Header;
use super::Verifier; use super::Verifier;
/// A no-op verifier -- this will verify everything it's given immediately.
#[allow(dead_code)] #[allow(dead_code)]
pub struct NoopVerifier; pub struct NoopVerifier;

View File

@ -14,12 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
/// Block and transaction verification functions //! Block and transaction verification functions
/// //!
/// Block verification is done in 3 steps //! Block verification is done in 3 steps
/// 1. Quick verification upon adding to the block queue //! 1. Quick verification upon adding to the block queue
/// 2. Signatures verification done in the queue. //! 2. Signatures verification done in the queue.
/// 3. Final verification against the blockchain done before enactment. //! 3. Final verification against the blockchain done before enactment.
use util::*; use util::*;
use engines::Engine; use engines::Engine;
@ -395,7 +395,7 @@ mod tests {
gas: U256::from(30_000), gas: U256::from(30_000),
gas_price: U256::from(40_000), gas_price: U256::from(40_000),
nonce: U256::one() nonce: U256::one()
}.sign(keypair.secret()); }.sign(keypair.secret(), None);
let tr2 = Transaction { let tr2 = Transaction {
action: Action::Create, action: Action::Create,
@ -404,7 +404,7 @@ mod tests {
gas: U256::from(30_000), gas: U256::from(30_000),
gas_price: U256::from(40_000), gas_price: U256::from(40_000),
nonce: U256::from(2) nonce: U256::from(2)
}.sign(keypair.secret()); }.sign(keypair.secret(), None);
let good_transactions = [ tr1.clone(), tr2.clone() ]; let good_transactions = [ tr1.clone(), tr2.clone() ];

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