Merge branch 'master' into adaptive_queue_threads
This commit is contained in:
commit
ada9d2b760
2
.gitignore
vendored
2
.gitignore
vendored
@ -30,3 +30,5 @@
|
|||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
311
.gitlab-ci.yml
311
.gitlab-ci.yml
@ -1,29 +1,40 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
|
||||||
- test
|
- test
|
||||||
|
- js-build
|
||||||
|
- build
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: "3"
|
GIT_DEPTH: "3"
|
||||||
SIMPLECOV: "true"
|
SIMPLECOV: "true"
|
||||||
RUST_BACKTRACE: "1"
|
RUST_BACKTRACE: "1"
|
||||||
|
RUSTFLAGS: ""
|
||||||
|
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 --verbose
|
- cargo build --release $CARGOFLAGS
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
- md5sum target/release/parity >> checksum
|
- md5sum target/release/parity > parity.md5
|
||||||
|
- sh scripts/deb-build.sh amd64
|
||||||
|
- cp target/release/parity deb/usr/bin/parity
|
||||||
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
|
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||||
|
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-stable
|
- rust-stable
|
||||||
@ -31,39 +42,16 @@ 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 --verbose
|
|
||||||
- strip target/release/parity
|
|
||||||
- md5sum target/release/parity >> checksum
|
|
||||||
- aws configure set aws_access_key_id $s3_key
|
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity --body target/release/parity
|
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/checksum --body checksum
|
|
||||||
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 --verbose
|
- cargo build --release $CARGOFLAGS
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
@ -77,12 +65,12 @@ 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 --verbose
|
- cargo build --release $CARGOFLAGS
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
@ -96,20 +84,20 @@ 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 --verbose
|
- cargo build --release $CARGOFLAGS
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
- md5sum target/release/parity >> checksum
|
- md5sum target/release/parity > parity.md5
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_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
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-centos
|
- rust-centos
|
||||||
@ -117,27 +105,71 @@ 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 CXX=arm-linux-gnueabihf-g++
|
||||||
|
- export HOST_CC=gcc
|
||||||
|
- export HOST_CXX=g++
|
||||||
- rm -rf .cargo
|
- rm -rf .cargo
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
||||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
- cargo build --target armv7-unknown-linux-gnueabihf --release $CARGOFLAGS
|
||||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||||
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> checksum
|
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity > parity.md5
|
||||||
|
- sh scripts/deb-build.sh armhf
|
||||||
|
- cp target/armv7-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity
|
||||||
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
|
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||||
|
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -150,23 +182,34 @@ 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 CXX=arm-linux-gnueabihf-g++
|
||||||
|
- export HOST_CC=gcc
|
||||||
|
- export HOST_CXX=g++
|
||||||
- rm -rf .cargo
|
- rm -rf .cargo
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
||||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
- cargo build --target arm-unknown-linux-gnueabihf --release $CARGOFLAGS
|
||||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> checksum
|
- md5sum target/arm-unknown-linux-gnueabihf/release/parity > parity.md5
|
||||||
|
- sh scripts/deb-build.sh armhf
|
||||||
|
- cp target/arm-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity
|
||||||
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
|
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||||
|
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -179,23 +222,27 @@ linux-armv6:
|
|||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-armv6:latest
|
image: ethcore/rust-armv6:latest
|
||||||
only:
|
only:
|
||||||
- master
|
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
|
- triggers
|
||||||
script:
|
script:
|
||||||
|
- export CC=arm-linux-gnueabi-gcc
|
||||||
|
- export CXX=arm-linux-gnueabi-g++
|
||||||
|
- export HOST_CC=gcc
|
||||||
|
- export HOST_CXX=g++
|
||||||
- rm -rf .cargo
|
- rm -rf .cargo
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
||||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
- cargo build --target arm-unknown-linux-gnueabi --release $CARGOFLAGS
|
||||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> checksum
|
- md5sum target/arm-unknown-linux-gnueabi/release/parity > parity.md5
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_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
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity.md5 --body parity.md5
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -208,23 +255,34 @@ 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 CXX=aarch64-linux-gnu-g++
|
||||||
|
- export HOST_CC=gcc
|
||||||
|
- export HOST_CXX=g++
|
||||||
- rm -rf .cargo
|
- rm -rf .cargo
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
||||||
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
- cargo build --target aarch64-unknown-linux-gnu --release $CARGOFLAGS
|
||||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||||
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> checksum
|
- md5sum target/aarch64-unknown-linux-gnu/release/parity > parity.md5
|
||||||
|
- sh scripts/deb-build.sh arm64
|
||||||
|
- cp target/aarch64-unknown-linux-gnu/release/parity deb/usr/bin/parity
|
||||||
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
|
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
||||||
|
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||||
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -236,17 +294,18 @@ linux-aarch64:
|
|||||||
darwin:
|
darwin:
|
||||||
stage: build
|
stage: build
|
||||||
only:
|
only:
|
||||||
- master
|
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
|
- triggers
|
||||||
script:
|
script:
|
||||||
- cargo build --release --verbose
|
- cargo build --release $CARGOFLAGS
|
||||||
- md5sum target/release/parity >> checksum
|
- rm -rf 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
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity.md5 --body parity.md5
|
||||||
tags:
|
tags:
|
||||||
- osx
|
- osx
|
||||||
artifacts:
|
artifacts:
|
||||||
@ -254,39 +313,155 @@ 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%
|
||||||
- rustup default stable-x86_64-pc-windows-msvc
|
- rustup default stable-x86_64-pc-windows-msvc
|
||||||
- cargo build --release --verbose
|
- cargo build --release %CARGOFLAGS%
|
||||||
- md5sum target/release/parity >> checksum
|
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||||
|
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||||
|
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||||
|
- 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
|
||||||
|
- makensis.exe installer.nsi
|
||||||
|
- copy installer.exe InstallParity.exe
|
||||||
|
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
|
||||||
|
- md5sums InstallParity.exe > InstallParity.exe.md5
|
||||||
|
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||||
|
- md5sums win-installer.zip > win-installer.zip.md5
|
||||||
|
- cd ..\target\release\
|
||||||
|
- md5sums parity.exe parity.pdb > parity.md5
|
||||||
|
- md5sums parity.exe > parity.exe.md5
|
||||||
|
- zip parity.zip parity.exe parity.pdb parity.md5
|
||||||
|
- md5sums parity.zip > parity.zip.md5
|
||||||
|
- cd ..\..
|
||||||
- aws configure set aws_access_key_id %s3_key%
|
- aws configure set aws_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-pc-windows-msvc/parity --body target/release/parity.exe
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target/release/parity.pdb
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/checksum --body checksum
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
|
||||||
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
|
||||||
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
|
||||||
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
||||||
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
||||||
|
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
||||||
tags:
|
tags:
|
||||||
- rust-windows
|
- rust-windows
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity.exe
|
- target/release/parity.exe
|
||||||
- target/release/parity.pdb
|
- target/release/parity.pdb
|
||||||
|
- nsis/InstallParity.exe
|
||||||
name: "x86_64-pc-windows-msvc_parity"
|
name: "x86_64-pc-windows-msvc_parity"
|
||||||
test-linux:
|
test-darwin:
|
||||||
stage: test
|
stage: test
|
||||||
|
only:
|
||||||
|
- triggers
|
||||||
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 --verbose
|
- ./test.sh $CARGOFLAGS --no-release
|
||||||
tags:
|
tags:
|
||||||
- rust-test
|
- osx
|
||||||
dependencies:
|
allow_failure: true
|
||||||
- linux-stable
|
test-windows:
|
||||||
|
stage: test
|
||||||
|
only:
|
||||||
|
- triggers
|
||||||
|
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-rust-stable:
|
||||||
|
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:
|
||||||
|
- ./js/scripts/install-deps.sh
|
||||||
|
script:
|
||||||
|
- ./js/scripts/lint.sh
|
||||||
|
- ./js/scripts/test.sh
|
||||||
|
- ./js/scripts/build.sh
|
||||||
|
tags:
|
||||||
|
- 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
|
||||||
|
12
.travis.yml
12
.travis.yml
@ -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"
|
||||||
@ -31,6 +31,8 @@ env:
|
|||||||
- RUN_COVERAGE="false"
|
- RUN_COVERAGE="false"
|
||||||
- RUN_DOCS="false"
|
- RUN_DOCS="false"
|
||||||
- TEST_OPTIONS=""
|
- TEST_OPTIONS=""
|
||||||
|
- RUSTFLAGS="-D warnings"
|
||||||
|
- TRAVIS_NODE_VERSION="6"
|
||||||
# GH_TOKEN for documentation
|
# GH_TOKEN for documentation
|
||||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||||
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov"
|
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov"
|
||||||
@ -40,6 +42,7 @@ cache:
|
|||||||
directories:
|
directories:
|
||||||
- $TRAVIS_BUILD_DIR/target
|
- $TRAVIS_BUILD_DIR/target
|
||||||
- $TRAVIS_BUILD_DIR/kcov-master
|
- $TRAVIS_BUILD_DIR/kcov-master
|
||||||
|
- $TRAVIS_BUILD_DIR/js/node_modules
|
||||||
- $HOME/.cargo
|
- $HOME/.cargo
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
@ -63,9 +66,14 @@ install:
|
|||||||
make && make install DESTDIR=../tmp &&
|
make && make install DESTDIR=../tmp &&
|
||||||
cd
|
cd
|
||||||
)
|
)
|
||||||
|
- nvm install $TRAVIS_NODE_VERSION && nvm use $TRAVIS_NODE_VERSION && ./js/scripts/install-deps.sh
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- if [ "$RUN_TESTS" = "true" ]; then ./test.sh $TEST_OPTIONS --verbose; fi
|
- if [ "$RUN_TESTS" = "true" ]; then
|
||||||
|
./js/scripts/lint.sh &&
|
||||||
|
./js/scripts/test.sh &&
|
||||||
|
./test.sh $TEST_OPTIONS --verbose;
|
||||||
|
fi
|
||||||
- if [ "$RUN_COVERAGE" = "true" ]; then ./scripts/cov.sh "$KCOV_CMD"; fi
|
- if [ "$RUN_COVERAGE" = "true" ]; then ./scripts/cov.sh "$KCOV_CMD"; fi
|
||||||
|
|
||||||
after_success: |
|
after_success: |
|
||||||
|
412
Cargo.lock
generated
412
Cargo.lock
generated
@ -1,28 +1,28 @@
|
|||||||
[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.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
|
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
|
||||||
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
||||||
@ -145,15 +145,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.0.90"
|
version = "0.0.96"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy_lints"
|
name = "clippy_lints"
|
||||||
version = "0.0.90"
|
version = "0.0.96"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"matches 0.1.2 (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]]
|
||||||
@ -222,8 +222,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elastic-array"
|
name = "elastic-array"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/ethcore/elastic-array#70e4012e691b732c7c4cb04e9232799e6aa268bc"
|
||||||
|
dependencies = [
|
||||||
|
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
@ -240,8 +243,8 @@ version = "0.5.4"
|
|||||||
source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
|
source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
|
||||||
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.28 (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)",
|
||||||
]
|
]
|
||||||
@ -264,29 +267,29 @@ name = "ethash"
|
|||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha3 0.1.0",
|
"sha3 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[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)",
|
||||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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",
|
||||||
@ -295,7 +298,7 @@ dependencies = [
|
|||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"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)",
|
||||||
"lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -304,11 +307,12 @@ dependencies = [
|
|||||||
"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)",
|
||||||
"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)",
|
||||||
"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)",
|
||||||
|
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-bigint"
|
name = "ethcore-bigint"
|
||||||
version = "0.1.0"
|
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)",
|
||||||
@ -319,32 +323,36 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-bloom-journal"
|
name = "ethcore-bloom-journal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-dapps"
|
name = "ethcore-dapps"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
||||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-ui 1.4.0",
|
||||||
"parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
|
||||||
"parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
|
||||||
"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)",
|
||||||
"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)",
|
||||||
@ -359,12 +367,12 @@ 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.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
"mio 0.6.1 (git+https://github.com/carllerche/mio)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -373,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)",
|
||||||
]
|
]
|
||||||
@ -399,6 +407,7 @@ dependencies = [
|
|||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -419,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)",
|
||||||
@ -427,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)",
|
||||||
@ -440,19 +449,20 @@ 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)",
|
||||||
"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.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
"mio 0.6.1 (git+https://github.com/carllerche/mio)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.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)",
|
||||||
@ -464,24 +474,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-rpc"
|
name = "ethcore-rpc"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethash 1.4.0",
|
"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)",
|
||||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.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)",
|
||||||
@ -494,20 +504,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-signer"
|
name = "ethcore-signer"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parity-ui 1.4.0",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -519,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)",
|
||||||
@ -530,24 +541,26 @@ 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)",
|
||||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
|
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
|
||||||
"ethcore-bigint 0.1.0",
|
"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)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
|
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -567,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.0",
|
"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)",
|
||||||
@ -577,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)",
|
||||||
@ -590,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.0",
|
"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)",
|
||||||
@ -606,7 +619,8 @@ 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)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -619,20 +633,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethsync"
|
name = "ethsync"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"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)",
|
||||||
@ -650,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]]
|
||||||
@ -668,14 +682,17 @@ 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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcc"
|
name = "gcc"
|
||||||
version = "0.3.28"
|
version = "0.3.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
@ -786,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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -803,7 +820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "json-ipc-server"
|
name = "json-ipc-server"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
source = "git+https://github.com/ethcore/json-ipc-server.git#5fbd0253750d3097b9a8fb27effa84c18d630bbb"
|
source = "git+https://github.com/ethcore/json-ipc-server.git#4642cd03ec1d23db89df80d22d5a88e7364ab885"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -835,7 +852,7 @@ version = "3.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 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)",
|
||||||
@ -843,8 +860,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "6.1.0"
|
version = "6.1.1"
|
||||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#2766c6708f66f6f4e667211461d220b49c0d9fdf"
|
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -871,14 +888,19 @@ name = "lazy_static"
|
|||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[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]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.0.9"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -893,10 +915,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru-cache"
|
name = "lru-cache"
|
||||||
version = "0.0.7"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -909,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]]
|
||||||
@ -936,8 +958,8 @@ name = "miniz-sys"
|
|||||||
version = "0.1.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.28 (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]]
|
||||||
@ -946,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)",
|
||||||
@ -962,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)",
|
||||||
@ -975,10 +997,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.6.0-dev"
|
version = "0.6.0-dev"
|
||||||
source = "git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d#62ec763c9cc34d8a452ed0392c575c50ddd5fc8d"
|
source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972"
|
||||||
dependencies = [
|
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)",
|
||||||
@ -987,6 +1009,22 @@ dependencies = [
|
|||||||
"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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "git+https://github.com/carllerche/mio#56f8663510196fdca04bdf7c5f4d60b24297826f"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miow"
|
name = "miow"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -1013,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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1022,8 +1060,8 @@ name = "nanomsg-sys"
|
|||||||
version = "0.5.0"
|
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.28 (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]]
|
||||||
@ -1033,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)",
|
||||||
]
|
]
|
||||||
@ -1044,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]]
|
||||||
@ -1054,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)",
|
||||||
@ -1144,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]]
|
||||||
@ -1161,9 +1212,14 @@ version = "0.2.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps"
|
name = "owning_ref"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parity-dapps-glue"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1175,44 +1231,57 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-home"
|
name = "parity-ui"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-ui-dev 1.4.0",
|
||||||
|
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-signer"
|
name = "parity-ui-dev"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-status"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
source = "git+https://github.com/ethcore/js-precompiled.git#957c5a66c33f3b06a7ae804ac5edc59c20e4535b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parity-dapps-wallet"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
|
||||||
dependencies = [
|
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.2.6"
|
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)",
|
||||||
|
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.16 (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)",
|
||||||
]
|
]
|
||||||
@ -1350,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]]
|
||||||
@ -1393,8 +1462,8 @@ dependencies = [
|
|||||||
name = "rlp"
|
name = "rlp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
|
||||||
"ethcore-bigint 0.1.0",
|
"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)",
|
||||||
]
|
]
|
||||||
@ -1402,19 +1471,19 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb"
|
name = "rocksdb"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb-sys"
|
name = "rocksdb-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.28 (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]]
|
||||||
@ -1434,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)",
|
||||||
]
|
]
|
||||||
@ -1444,8 +1513,8 @@ name = "rust-crypto"
|
|||||||
version = "0.2.36"
|
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.28 (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)",
|
||||||
@ -1537,9 +1606,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
name = "sha3"
|
name = "sha3"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -1555,6 +1629,11 @@ name = "slab"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@ -1606,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)",
|
||||||
@ -1628,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)",
|
||||||
@ -1641,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)",
|
||||||
@ -1682,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]]
|
||||||
@ -1691,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]]
|
||||||
@ -1708,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)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1846,13 +1925,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
version = "0.5.2"
|
version = "0.5.3"
|
||||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#afbff59776ce16ccec5ee9e218b8891830ee6fdf"
|
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)",
|
||||||
"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-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)",
|
"mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
|
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
|
||||||
@ -1912,8 +1991,8 @@ dependencies = [
|
|||||||
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
||||||
"checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "<none>"
|
"checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "<none>"
|
||||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||||
"checksum clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "d19bda68c3db98e3a780342f6101b44312fef20a5f13ce756d1202a35922b01b"
|
"checksum clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "6eacf01b0aad84a0817703498f72d252df7c0faf6a5b86d0be4265f1829e459f"
|
||||||
"checksum clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4ed67c69b9bb35169be2538691d290a3aa0cbfd4b9f0bfb7c221fc1d399a96"
|
"checksum clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "a49960c9aab544ce86b004dcb61620e8b898fea5fc0f697a028f460f48221ed6"
|
||||||
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
||||||
"checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc"
|
"checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc"
|
||||||
"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "<none>"
|
"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "<none>"
|
||||||
@ -1921,12 +2000,12 @@ dependencies = [
|
|||||||
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
||||||
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
|
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
|
||||||
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
|
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
|
||||||
"checksum elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bc9250a632e7c001b741eb0ec6cee93c9a5b6d5f1879696a4b94d62b012210a"
|
"checksum elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
|
||||||
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
||||||
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
||||||
"checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f"
|
"checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f"
|
||||||
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
|
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
|
||||||
"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
|
"checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312"
|
||||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||||
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
|
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
|
||||||
"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
|
"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
|
||||||
@ -1942,15 +2021,16 @@ dependencies = [
|
|||||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
||||||
"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "<none>"
|
"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "<none>"
|
||||||
"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414"
|
"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414"
|
||||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
"checksum 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 libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
||||||
"checksum linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "83f7ff3baae999fdf921cccf54b61842bb3b26868d50d02dff48052ebec8dd79"
|
"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.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"
|
||||||
"checksum lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "42d50dcb5d9f145df83b1043207e1ac0c37c9c779c4e128ca4655abc3f3cbf8c"
|
"checksum lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656fa4dfcb02bcf1063c592ba3ff6a5303ee1f2afe98c8a889e8b1a77c6dfdb7"
|
||||||
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
||||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||||
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
||||||
@ -1958,7 +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-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)" = "<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>"
|
||||||
@ -1966,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"
|
||||||
@ -1978,12 +2060,12 @@ dependencies = [
|
|||||||
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
||||||
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
||||||
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
||||||
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
|
||||||
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab"
|
||||||
"checksum parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "<none>"
|
||||||
"checksum parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6"
|
||||||
"checksum parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc5847584161f273e69edc63c1a86254a22f570a0b5dd87aa6f9773f6f7d125"
|
||||||
"checksum parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0fd1be2c3cf5fef20a6d18fec252c4f3c87c14fc3039002eb7d4ed91e436826"
|
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||||
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
||||||
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
||||||
"checksum phf_generator 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "db005608fd99800c8c74106a7c894cf582055b689aa14a79462cefdcb7dc1cc3"
|
"checksum phf_generator 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "db005608fd99800c8c74106a7c894cf582055b689aa14a79462cefdcb7dc1cc3"
|
||||||
@ -2020,9 +2102,11 @@ dependencies = [
|
|||||||
"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21"
|
"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21"
|
||||||
"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc"
|
"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc"
|
||||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||||
|
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"
|
||||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||||
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
||||||
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
||||||
|
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||||
@ -2060,7 +2144,7 @@ dependencies = [
|
|||||||
"checksum webpki 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "813503a5985585e0812d430cd1328ee322f47f66629c8ed4ecab939cf9e92f91"
|
"checksum webpki 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "813503a5985585e0812d430cd1328ee322f47f66629c8ed4ecab939cf9e92f91"
|
||||||
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
||||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
"checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||||
"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"
|
"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"
|
||||||
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
|
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
|
||||||
|
38
Cargo.toml
38
Cargo.toml
@ -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"
|
||||||
@ -26,7 +26,11 @@ lazy_static = "0.2"
|
|||||||
regex = "0.1"
|
regex = "0.1"
|
||||||
isatty = "0.1"
|
isatty = "0.1"
|
||||||
toml = "0.2"
|
toml = "0.2"
|
||||||
|
serde = "0.8.0"
|
||||||
|
serde_json = "0.8.0"
|
||||||
|
hyper = { version = "0.9", default-features = false }
|
||||||
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
||||||
|
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||||
fdlimit = { path = "util/fdlimit" }
|
fdlimit = { path = "util/fdlimit" }
|
||||||
ethcore = { path = "ethcore" }
|
ethcore = { path = "ethcore" }
|
||||||
ethcore-util = { path = "util" }
|
ethcore-util = { path = "util" }
|
||||||
@ -40,12 +44,9 @@ ethcore-ipc = { path = "ipc/rpc" }
|
|||||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||||
ethcore-logger = { path = "logger" }
|
ethcore-logger = { path = "logger" }
|
||||||
rlp = { path = "util/rlp" }
|
rlp = { path = "util/rlp" }
|
||||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
|
||||||
ethcore-dapps = { path = "dapps", optional = true }
|
|
||||||
clippy = { version = "0.0.90", optional = true}
|
|
||||||
ethcore-stratum = { path = "stratum" }
|
ethcore-stratum = { path = "stratum" }
|
||||||
serde = "0.8.0"
|
ethcore-dapps = { path = "dapps", optional = true }
|
||||||
serde_json = "0.8.0"
|
clippy = { version = "0.0.96", optional = true}
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.2"
|
winapi = "0.2"
|
||||||
@ -53,23 +54,32 @@ winapi = "0.2"
|
|||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
daemonize = "0.2"
|
daemonize = "0.2"
|
||||||
|
|
||||||
[dependencies.hyper]
|
|
||||||
version = "0.9"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ui", "use-precompiled-js", "ipc"]
|
default = ["ui-precompiled"]
|
||||||
ui = ["dapps", "ethcore-signer/ui"]
|
|
||||||
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
ui = [
|
||||||
|
"dapps",
|
||||||
|
"ethcore-dapps/ui",
|
||||||
|
"ethcore-signer/ui",
|
||||||
|
]
|
||||||
|
ui-precompiled = [
|
||||||
|
"dapps",
|
||||||
|
"ethcore-signer/ui-precompiled",
|
||||||
|
"ethcore-dapps/ui-precompiled",
|
||||||
|
]
|
||||||
|
|
||||||
dapps = ["ethcore-dapps"]
|
dapps = ["ethcore-dapps"]
|
||||||
ipc = ["ethcore/ipc"]
|
ipc = ["ethcore/ipc", "ethsync/ipc"]
|
||||||
jit = ["ethcore/jit"]
|
jit = ["ethcore/jit"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||||
json-tests = ["ethcore/json-tests"]
|
json-tests = ["ethcore/json-tests"]
|
||||||
|
test-heavy = ["ethcore/test-heavy"]
|
||||||
stratum = ["ipc"]
|
stratum = ["ipc"]
|
||||||
ethkey-cli = ["ethcore/ethkey-cli"]
|
ethkey-cli = ["ethcore/ethkey-cli"]
|
||||||
ethstore-cli = ["ethcore/ethstore-cli"]
|
ethstore-cli = ["ethcore/ethstore-cli"]
|
||||||
evm-debug = ["ethcore/evm-debug"]
|
evm-debug = ["ethcore/evm-debug"]
|
||||||
|
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||||
|
slow-blocks = ["ethcore/slow-blocks"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
path = "parity/main.rs"
|
path = "parity/main.rs"
|
||||||
|
41
README.md
41
README.md
@ -1,10 +1,16 @@
|
|||||||
# [Parity](https://ethcore.io/parity.html)
|
# [Parity](https://ethcore.io/parity.html)
|
||||||
### Fast, light, and robust Ethereum implementation
|
### Fast, light, and robust Ethereum implementation
|
||||||
|
|
||||||
[![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]
|
[![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]
|
||||||
|
|
||||||
|
### 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]
|
||||||
|
|
||||||
|
|
||||||
Be sure to check out [our wiki][wiki-url] for more information.
|
Be sure to check out [our wiki][wiki-url] for more information.
|
||||||
|
|
||||||
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
|
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
|
||||||
@ -18,19 +24,25 @@ 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
|
||||||
|
|
||||||
|
**Parity requires Rust version 1.12.0 to build**
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
## About Parity
|
## About Parity
|
||||||
|
|
||||||
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!
|
||||||
@ -51,6 +63,13 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
|||||||
$ 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
|
||||||
@ -96,9 +115,9 @@ and Parity will begin syncing the Ethereum blockchain.
|
|||||||
### Using systemd service file
|
### Using systemd service file
|
||||||
To start Parity as a regular user using systemd init:
|
To start Parity as a regular user using systemd init:
|
||||||
|
|
||||||
1. Copy ```parity/scripts/parity.service``` to your
|
1. Copy `parity/scripts/parity.service` to your
|
||||||
systemd user directory (usually ```~/.config/systemd/user```).
|
systemd user directory (usually `~/.config/systemd/user`).
|
||||||
2. To pass any argument to Parity, write a ```~/.parity/parity.conf``` file this way:
|
2. To pass any argument to Parity, write a `~/.parity/parity.conf` file this way:
|
||||||
```ARGS="ARG1 ARG2 ARG3"```.
|
`ARGS="ARG1 ARG2 ARG3"`.
|
||||||
|
|
||||||
Example: ```ARGS="ui --geth --identity MyMachine"```.
|
Example: `ARGS="ui --geth --identity MyMachine"`.
|
||||||
|
@ -6,6 +6,7 @@ environment:
|
|||||||
certpass:
|
certpass:
|
||||||
secure: 0BgXJqxq9Ei34/hZ7121FQ==
|
secure: 0BgXJqxq9Ei34/hZ7121FQ==
|
||||||
keyfile: C:\users\appveyor\Certificates.p12
|
keyfile: C:\users\appveyor\Certificates.p12
|
||||||
|
RUSTFLAGS: -Zorbit=off -D warnings
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
@ -18,10 +19,10 @@ branches:
|
|||||||
install:
|
install:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
- ps: Install-Product node 6
|
- ps: Install-Product node 6
|
||||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.10.0-x86_64-pc-windows-msvc.exe"
|
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.12.0-x86_64-pc-windows-msvc.exe"
|
||||||
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll
|
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll
|
||||||
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe
|
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe
|
||||||
- rust-1.10.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
- rust-1.12.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
|
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
|
||||||
- rustc -V
|
- rustc -V
|
||||||
- cargo -V
|
- cargo -V
|
||||||
@ -37,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 }
|
||||||
|
|
||||||
|
@ -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"
|
||||||
@ -11,6 +11,7 @@ build = "build.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.3.14"
|
rand = "0.3.14"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
|
env_logger = "0.3"
|
||||||
jsonrpc-core = "3.0"
|
jsonrpc-core = "3.0"
|
||||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||||
@ -19,33 +20,29 @@ url = "1.0"
|
|||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
serde = "0.8"
|
serde = "0.8"
|
||||||
serde_json = "0.8"
|
serde_json = "0.8"
|
||||||
serde_macros = { version = "0.8", optional = true }
|
|
||||||
zip = { version = "0.1", default-features = false }
|
|
||||||
ethabi = "0.2.2"
|
ethabi = "0.2.2"
|
||||||
linked-hash-map = "0.3"
|
linked-hash-map = "0.3"
|
||||||
|
parity-dapps-glue = "1.4"
|
||||||
|
mime = "0.2"
|
||||||
|
time = "0.1.35"
|
||||||
|
serde_macros = { version = "0.8", optional = true }
|
||||||
|
zip = { version = "0.1", default-features = false }
|
||||||
ethcore-devtools = { path = "../devtools" }
|
ethcore-devtools = { path = "../devtools" }
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
fetch = { path = "../util/fetch" }
|
fetch = { path = "../util/fetch" }
|
||||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-ui = { path = "./ui" }
|
||||||
# List of apps
|
|
||||||
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
|
||||||
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
|
||||||
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true }
|
|
||||||
mime_guess = { version = "1.6.1" }
|
mime_guess = { version = "1.6.1" }
|
||||||
clippy = { version = "0.0.90", optional = true}
|
clippy = { version = "0.0.96", optional = true}
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.8", optional = true }
|
serde_codegen = { version = "0.8", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serde_codegen", "extra-dapps"]
|
default = ["serde_codegen"]
|
||||||
extra-dapps = ["parity-dapps-wallet"]
|
|
||||||
nightly = ["serde_macros"]
|
nightly = ["serde_macros"]
|
||||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||||
|
|
||||||
use-precompiled-js = [
|
ui = ["parity-ui/no-precompiled-js"]
|
||||||
"parity-dapps-status/use-precompiled-js",
|
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
||||||
"parity-dapps-home/use-precompiled-js",
|
|
||||||
"parity-dapps-wallet/use-precompiled-js"
|
|
||||||
]
|
|
||||||
|
31
dapps/js-glue/Cargo.toml
Normal file
31
dapps/js-glue/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
description = "Base Package for all Parity built-in dapps"
|
||||||
|
name = "parity-dapps-glue"
|
||||||
|
version = "1.4.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
quasi_codegen = { version = "0.11", optional = true }
|
||||||
|
syntex = { version = "0.33", optional = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
glob = { version = "0.2.11" }
|
||||||
|
mime_guess = { version = "1.6.1" }
|
||||||
|
aster = { version = "0.17", default-features = false }
|
||||||
|
quasi = { version = "0.11", default-features = false }
|
||||||
|
quasi_macros = { version = "0.11", optional = true }
|
||||||
|
syntex = { version = "0.33", optional = true }
|
||||||
|
syntex_syntax = { version = "0.33", optional = true }
|
||||||
|
clippy = { version = "0.0.90", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dev = ["clippy"]
|
||||||
|
default = ["with-syntex"]
|
||||||
|
nightly = ["quasi_macros"]
|
||||||
|
nightly-testing = ["clippy"]
|
||||||
|
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||||
|
use-precompiled-js = []
|
||||||
|
|
||||||
|
|
65
dapps/js-glue/README.md
Normal file
65
dapps/js-glue/README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Parity Dapps (JS-glue)
|
||||||
|
|
||||||
|
Code generator to simplify creating a built-in Parity Dapp
|
||||||
|
|
||||||
|
# How to create new builtin Dapp.
|
||||||
|
1. Clone this repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git clone https://github.com/ethcore/parity.git
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Create a new directory for your Dapp. (`./myapp`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ mkdir -p ./parity/dapps/myapp/src/web
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy your frontend files to `./dapps/myapp/src/web` (bundled ones)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cp -r ./myapp-src/* ./parity/dapps/myapp/src/web
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Instead of creating `web3` in your app. Load (as the first script tag in `head`):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="/parity-utils/inject.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
||||||
|
|
||||||
|
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/ethcore/parity-ui/blob/master/status/Cargo.toml).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git clone https://github.com/ethcore/parity-ui.git
|
||||||
|
$ cd ./parity-ui/
|
||||||
|
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||||
|
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||||
|
$ cp ./home/src/lib.rs ../parity/dapps/myapp/src/lib.rs
|
||||||
|
$ cp ./home/src/lib.rs.in ../parity/dapps/myapp/src/lib.rs.in
|
||||||
|
# And edit the details of your app
|
||||||
|
$ vim ../parity/dapps/myapp/Cargo.toml # Edit the details
|
||||||
|
$ vim ./parity/dapps/myapp/src/lib.rs.in # Edit the details
|
||||||
|
```
|
||||||
|
# How to include your Dapp into `Parity`?
|
||||||
|
1. Edit `dapps/Cargo.toml` and add dependency to your application (it can be optional)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Use git repo and version
|
||||||
|
parity-dapps-myapp = { path="./myapp" }
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `dapps/src/apps.rs` and add your application to `all_pages` (if it's optional you need to specify two functions - see `parity-dapps-wallet` example)
|
||||||
|
|
||||||
|
1. Compile parity.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo build --release # While inside `parity`
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Commit the results.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add myapp && git commit -am "My first Parity Dapp".
|
||||||
|
```
|
45
dapps/js-glue/build.rs
Normal file
45
dapps/js-glue/build.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
mod inner {
|
||||||
|
extern crate syntex;
|
||||||
|
extern crate quasi_codegen;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
quasi_codegen::register(&mut registry);
|
||||||
|
|
||||||
|
let src = Path::new("src/lib.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("lib.rs");
|
||||||
|
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
mod inner {
|
||||||
|
pub fn main() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
inner::main();
|
||||||
|
}
|
66
dapps/js-glue/src/build.rs
Normal file
66
dapps/js-glue/src/build.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
pub mod inner {
|
||||||
|
use syntex;
|
||||||
|
use codegen;
|
||||||
|
use syntax::{ast, fold};
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
||||||
|
/// Helper folder that strips the serde attributes after the extensions have been expanded.
|
||||||
|
struct StripAttributeFolder;
|
||||||
|
|
||||||
|
impl fold::Folder for StripAttributeFolder {
|
||||||
|
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||||
|
match attr.node.value.node {
|
||||||
|
ast::MetaItemKind::List(ref n, _) if n == &"webapp" => { return None; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||||
|
fold::noop_fold_mac(mac, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(reg: &mut syntex::Registry) {
|
||||||
|
reg.add_attr("feature(custom_derive)");
|
||||||
|
reg.add_attr("feature(custom_attribute)");
|
||||||
|
|
||||||
|
reg.add_decorator("derive_WebAppFiles", codegen::expand_webapp_implementation);
|
||||||
|
reg.add_post_expansion_pass(strip_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
register(&mut registry);
|
||||||
|
|
||||||
|
let src = Path::new("src/lib.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("lib.rs");
|
||||||
|
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
pub mod inner {
|
||||||
|
use codegen;
|
||||||
|
|
||||||
|
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||||
|
reg.register_syntax_extension(
|
||||||
|
syntax::parse::token::intern("derive_WebAppFiles"),
|
||||||
|
syntax::ext::base::MultiDecorator(
|
||||||
|
Box::new(codegen::expand_webapp_implementation)));
|
||||||
|
|
||||||
|
reg.register_attribute("webapp".to_owned(), AttributeType::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate() {}
|
||||||
|
}
|
189
dapps/js-glue/src/codegen.rs
Normal file
189
dapps/js-glue/src/codegen.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate aster;
|
||||||
|
extern crate glob;
|
||||||
|
extern crate mime_guess;
|
||||||
|
|
||||||
|
use self::mime_guess::guess_mime_type;
|
||||||
|
use std::path::{self, Path, PathBuf};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use syntax::ast::{MetaItem, Item};
|
||||||
|
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::attr;
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
|
use syntax::ptr::P;
|
||||||
|
use syntax::print::pprust::{lit_to_string};
|
||||||
|
use syntax::parse::token::{InternedString};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn expand_webapp_implementation(
|
||||||
|
cx: &mut ExtCtxt,
|
||||||
|
span: Span,
|
||||||
|
meta_item: &MetaItem,
|
||||||
|
annotatable: &Annotatable,
|
||||||
|
push: &mut FnMut(Annotatable)
|
||||||
|
) {
|
||||||
|
let item = match *annotatable {
|
||||||
|
Annotatable::Item(ref item) => item,
|
||||||
|
_ => {
|
||||||
|
cx.span_err(meta_item.span, "`#[derive(WebAppFiles)]` may only be applied to struct implementations");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let builder = aster::AstBuilder::new().span(span);
|
||||||
|
implement_webapp(cx, &builder, &item, push);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
||||||
|
let static_files_dir = extract_path(cx, item);
|
||||||
|
|
||||||
|
let src = Path::new("src");
|
||||||
|
let static_files = {
|
||||||
|
let mut buf = src.to_path_buf();
|
||||||
|
buf.push(static_files_dir.deref());
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
|
||||||
|
let search_location = {
|
||||||
|
let mut buf = static_files.to_path_buf();
|
||||||
|
buf.push("**");
|
||||||
|
buf.push("*");
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
|
||||||
|
let files = glob::glob(search_location.to_str().expect("Valid UTF8 path"))
|
||||||
|
.expect("The sources directory is missing.")
|
||||||
|
.collect::<Result<Vec<PathBuf>, glob::GlobError>>()
|
||||||
|
.expect("There should be no error when reading a list of files.");
|
||||||
|
|
||||||
|
let statements = files
|
||||||
|
.iter()
|
||||||
|
.filter(|path_buf| path_buf.is_file())
|
||||||
|
.map(|path_buf| {
|
||||||
|
let path = path_buf.as_path();
|
||||||
|
let filename = path.file_name().and_then(|s| s.to_str()).expect("Only UTF8 paths.");
|
||||||
|
let mime_type = guess_mime_type(filename).to_string();
|
||||||
|
let file_path = as_uri(path.strip_prefix(&static_files).ok().expect("Prefix is always there, cause it's absolute path;qed"));
|
||||||
|
let file_path_in_source = path.to_str().expect("Only UTF8 paths.");
|
||||||
|
|
||||||
|
let path_lit = builder.expr().str(file_path.as_str());
|
||||||
|
let mime_lit = builder.expr().str(mime_type.as_str());
|
||||||
|
let web_path_lit = builder.expr().str(file_path_in_source);
|
||||||
|
let separator_lit = builder.expr().str(path::MAIN_SEPARATOR.to_string().as_str());
|
||||||
|
let concat_id = builder.id("concat!");
|
||||||
|
let env_id = builder.id("env!");
|
||||||
|
let macro_id = builder.id("include_bytes!");
|
||||||
|
|
||||||
|
let content = quote_expr!(
|
||||||
|
cx,
|
||||||
|
$macro_id($concat_id($env_id("CARGO_MANIFEST_DIR"), $separator_lit, $web_path_lit))
|
||||||
|
);
|
||||||
|
quote_stmt!(
|
||||||
|
cx,
|
||||||
|
files.insert($path_lit, File { path: $path_lit, content_type: $mime_lit, content: $content });
|
||||||
|
).expect("The statement is always ok, because it just uses literals.")
|
||||||
|
}).collect::<Vec<ast::Stmt>>();
|
||||||
|
|
||||||
|
let type_name = item.ident;
|
||||||
|
|
||||||
|
let files_impl = quote_item!(cx,
|
||||||
|
impl $type_name {
|
||||||
|
fn files() -> ::std::collections::HashMap<&'static str, File> {
|
||||||
|
let mut files = ::std::collections::HashMap::new();
|
||||||
|
$statements
|
||||||
|
files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
push(Annotatable::Item(files_impl));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||||
|
for meta_items in item.attrs().iter().filter_map(webapp_meta_items) {
|
||||||
|
for meta_item in meta_items {
|
||||||
|
match meta_item.node {
|
||||||
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"path" => {
|
||||||
|
if let Some(s) = get_str_from_lit(cx, name, lit) {
|
||||||
|
return s.deref().to_owned();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default
|
||||||
|
"web".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<InternedString> {
|
||||||
|
match lit.node {
|
||||||
|
ast::LitKind::Str(ref s, _) => Some(s.clone()),
|
||||||
|
_ => {
|
||||||
|
cx.span_err(
|
||||||
|
lit.span,
|
||||||
|
&format!("webapp annotation `{}` must be a string, not `{}`",
|
||||||
|
name,
|
||||||
|
lit_to_string(lit)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn webapp_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
||||||
|
match attr.node.value.node {
|
||||||
|
ast::MetaItemKind::List(ref name, ref items) if name == &"webapp" => {
|
||||||
|
attr::mark_used(&attr);
|
||||||
|
Some(items)
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_uri(path: &Path) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
for component in path.iter() {
|
||||||
|
s.push_str(component.to_str().expect("Only UTF-8 filenames are supported."));
|
||||||
|
s.push('/');
|
||||||
|
}
|
||||||
|
s[0..s.len()-1].into()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_convert_path_separators_on_all_platforms() {
|
||||||
|
// given
|
||||||
|
let p = {
|
||||||
|
let mut p = PathBuf::new();
|
||||||
|
p.push("web");
|
||||||
|
p.push("src");
|
||||||
|
p.push("index.html");
|
||||||
|
p
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
let path = as_uri(&p);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(path, "web/src/index.html".to_owned());
|
||||||
|
}
|
89
dapps/js-glue/src/js.rs
Normal file
89
dapps/js-glue/src/js.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![cfg_attr(feature="use-precompiled-js", allow(dead_code))]
|
||||||
|
#![cfg_attr(feature="use-precompiled-js", allow(unused_imports))]
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
mod platform {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub static NPM_CMD: &'static str = "npm";
|
||||||
|
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod platform {
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
pub static NPM_CMD: &'static str = "npm.cmd";
|
||||||
|
// NOTE [ToDr] For some reason on windows
|
||||||
|
// We cannot have any file descriptors open when running a child process
|
||||||
|
// during build phase.
|
||||||
|
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||||
|
cmd.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
|
||||||
|
panic!("Error: {}: {:?}", s, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use-precompiled-js")]
|
||||||
|
pub fn test(_path: &str) {
|
||||||
|
}
|
||||||
|
#[cfg(feature = "use-precompiled-js")]
|
||||||
|
pub fn build(_path: &str, _dest: &str) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use-precompiled-js"))]
|
||||||
|
pub fn build(path: &str, dest: &str) {
|
||||||
|
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||||
|
.arg("install")
|
||||||
|
.arg("--no-progress")
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
|
||||||
|
assert!(child.success(), "There was an error installing dependencies.");
|
||||||
|
|
||||||
|
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||||
|
.arg("run")
|
||||||
|
.arg("build")
|
||||||
|
.env("NODE_ENV", "production")
|
||||||
|
.env("BUILD_DEST", dest)
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|e| die("Building JS code", e));
|
||||||
|
assert!(child.success(), "There was an error build JS code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use-precompiled-js"))]
|
||||||
|
pub fn test(path: &str) {
|
||||||
|
let child = Command::new(platform::NPM_CMD)
|
||||||
|
.arg("run")
|
||||||
|
.arg("test")
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|e| die("Running test command", e));
|
||||||
|
assert!(child.success(), "There was an error while running JS tests.");
|
||||||
|
}
|
39
dapps/js-glue/src/lib.rs
Normal file
39
dapps/js-glue/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
||||||
|
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
extern crate syntex;
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate syntex_syntax as syntax;
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate syntax;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
extern crate rustc_plugin;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
include!("lib.rs.in");
|
46
dapps/js-glue/src/lib.rs.in
Normal file
46
dapps/js-glue/src/lib.rs.in
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate quasi;
|
||||||
|
|
||||||
|
mod codegen;
|
||||||
|
mod build;
|
||||||
|
pub mod js;
|
||||||
|
pub use build::inner::generate;
|
||||||
|
|
||||||
|
use std::default::Default;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct File {
|
||||||
|
pub path: &'static str,
|
||||||
|
pub content: &'static [u8],
|
||||||
|
// TODO: use strongly-typed MIME.
|
||||||
|
pub content_type: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Info {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: &'static str,
|
||||||
|
pub author: &'static str,
|
||||||
|
pub description: &'static str,
|
||||||
|
pub icon_url: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WebApp : Default + Send + Sync {
|
||||||
|
fn file(&self, path: &str) -> Option<&File>;
|
||||||
|
fn info(&self) -> Info;
|
||||||
|
}
|
@ -15,24 +15,31 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use unicase::UniCase;
|
||||||
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
||||||
|
use hyper::header;
|
||||||
|
use hyper::method::Method;
|
||||||
|
use hyper::header::AccessControlAllowOrigin;
|
||||||
|
|
||||||
use api::types::{App, ApiError};
|
use api::types::{App, ApiError};
|
||||||
use api::response::{as_json, as_json_error, ping_response};
|
use api::response;
|
||||||
|
use apps::fetcher::ContentFetcher;
|
||||||
|
|
||||||
use handlers::extract_url;
|
use handlers::extract_url;
|
||||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||||
use apps::fetcher::ContentFetcher;
|
use jsonrpc_http_server::cors;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RestApi {
|
pub struct RestApi {
|
||||||
local_domain: String,
|
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
|
||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
fetcher: Arc<ContentFetcher>,
|
fetcher: Arc<ContentFetcher>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(local_domain: String, endpoints: Arc<Endpoints>, fetcher: Arc<ContentFetcher>) -> Box<Endpoint> {
|
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<ContentFetcher>) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
local_domain: local_domain,
|
cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
fetcher: fetcher,
|
fetcher: fetcher,
|
||||||
})
|
})
|
||||||
@ -53,6 +60,7 @@ impl Endpoint for RestApi {
|
|||||||
|
|
||||||
struct RestApiRouter {
|
struct RestApiRouter {
|
||||||
api: RestApi,
|
api: RestApi,
|
||||||
|
origin: Option<String>,
|
||||||
path: Option<EndpointPath>,
|
path: Option<EndpointPath>,
|
||||||
control: Option<Control>,
|
control: Option<Control>,
|
||||||
handler: Box<Handler>,
|
handler: Box<Handler>,
|
||||||
@ -62,9 +70,10 @@ impl RestApiRouter {
|
|||||||
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
||||||
RestApiRouter {
|
RestApiRouter {
|
||||||
path: Some(path),
|
path: Some(path),
|
||||||
|
origin: None,
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
api: api,
|
api: api,
|
||||||
handler: as_json_error(&ApiError {
|
handler: response::as_json_error(&ApiError {
|
||||||
code: "404".into(),
|
code: "404".into(),
|
||||||
title: "Not Found".into(),
|
title: "Not Found".into(),
|
||||||
detail: "Resource you requested has not been found.".into(),
|
detail: "Resource you requested has not been found.".into(),
|
||||||
@ -80,11 +89,40 @@ impl RestApiRouter {
|
|||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns basic headers for a response (it may be overwritten by the handler)
|
||||||
|
fn response_headers(&self) -> header::Headers {
|
||||||
|
let mut headers = header::Headers::new();
|
||||||
|
headers.set(header::AccessControlAllowCredentials);
|
||||||
|
headers.set(header::AccessControlAllowMethods(vec![
|
||||||
|
Method::Options,
|
||||||
|
Method::Post,
|
||||||
|
Method::Get,
|
||||||
|
]));
|
||||||
|
headers.set(header::AccessControlAllowHeaders(vec![
|
||||||
|
UniCase("origin".to_owned()),
|
||||||
|
UniCase("content-type".to_owned()),
|
||||||
|
UniCase("accept".to_owned()),
|
||||||
|
]));
|
||||||
|
|
||||||
|
if let Some(cors_header) = cors::get_cors_header(&self.api.cors_domains, &self.origin) {
|
||||||
|
headers.set(cors_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||||
|
|
||||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||||
|
self.origin = cors::read_origin(&request);
|
||||||
|
|
||||||
|
if let Method::Options = *request.method() {
|
||||||
|
self.handler = response::empty();
|
||||||
|
return Next::write();
|
||||||
|
}
|
||||||
|
|
||||||
let url = extract_url(&request);
|
let url = extract_url(&request);
|
||||||
if url.is_none() {
|
if url.is_none() {
|
||||||
// Just return 404 if we can't parse URL
|
// Just return 404 if we can't parse URL
|
||||||
@ -92,15 +130,18 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let url = url.expect("Check for None early-exists above; qed");
|
let url = url.expect("Check for None early-exists above; qed");
|
||||||
let path = self.path.take().expect("on_request called only once, and path is always defined in new; qed");
|
let mut path = self.path.take().expect("on_request called only once, and path is always defined in new; qed");
|
||||||
let control = self.control.take().expect("on_request called only once, and control is always defined in new; qed");
|
let control = self.control.take().expect("on_request called only once, and control is always defined in new; qed");
|
||||||
|
|
||||||
let endpoint = url.path.get(1).map(|v| v.as_str());
|
let endpoint = url.path.get(1).map(|v| v.as_str());
|
||||||
let hash = url.path.get(2).map(|v| v.as_str());
|
let hash = url.path.get(2).map(|v| v.as_str());
|
||||||
|
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
|
||||||
|
// we will try and retrieve 'api' as the hash when doing the /api/content route
|
||||||
|
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
|
||||||
|
|
||||||
let handler = endpoint.and_then(|v| match v {
|
let handler = endpoint.and_then(|v| match v {
|
||||||
"apps" => Some(as_json(&self.api.list_apps())),
|
"apps" => Some(response::as_json(&self.api.list_apps())),
|
||||||
"ping" => Some(ping_response(&self.api.local_domain)),
|
"ping" => Some(response::ping()),
|
||||||
"content" => self.resolve_content(hash, path, control),
|
"content" => self.resolve_content(hash, path, control),
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
@ -118,6 +159,7 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
|
*res.headers_mut() = self.response_headers();
|
||||||
self.handler.on_response(res)
|
self.handler.on_response(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0
dapps/src/api/cors.rs
Normal file
0
dapps/src/api/cors.rs
Normal file
@ -19,18 +19,22 @@ use serde_json;
|
|||||||
use endpoint::Handler;
|
use endpoint::Handler;
|
||||||
use handlers::{ContentHandler, EchoHandler};
|
use handlers::{ContentHandler, EchoHandler};
|
||||||
|
|
||||||
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
pub fn empty() -> Box<Handler> {
|
||||||
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
let json = serde_json::to_string(val)
|
||||||
|
.expect("serialization to string is infallible; qed");
|
||||||
|
Box::new(ContentHandler::ok(json, mime!(Application/Json)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ping_response(local_domain: &str) -> Box<Handler> {
|
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
|
||||||
Box::new(EchoHandler::cors(vec![
|
let json = serde_json::to_string(val)
|
||||||
format!("http://{}", local_domain),
|
.expect("serialization to string is infallible; qed");
|
||||||
// Allow CORS calls also for localhost
|
Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
|
||||||
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
|
}
|
||||||
]))
|
|
||||||
|
pub fn ping() -> Box<Handler> {
|
||||||
|
Box::new(EchoHandler::default())
|
||||||
}
|
}
|
||||||
|
@ -47,15 +47,17 @@ impl ContentCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
||||||
let mut len = self.cache.len();
|
let len = self.cache.len();
|
||||||
|
|
||||||
if len <= expected_size {
|
if len <= expected_size {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut removed = Vec::with_capacity(len - expected_size);
|
let mut removed = Vec::with_capacity(len - expected_size);
|
||||||
while len > expected_size {
|
|
||||||
let entry = self.cache.pop_front().unwrap();
|
while self.cache.len() > expected_size {
|
||||||
|
let entry = self.cache.pop_front().expect("expected_size bounded at 0, len is greater; qed");
|
||||||
|
|
||||||
match entry.1 {
|
match entry.1 {
|
||||||
ContentStatus::Fetching(ref fetch) => {
|
ContentStatus::Fetching(ref fetch) => {
|
||||||
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
||||||
@ -64,16 +66,15 @@ impl ContentCache {
|
|||||||
},
|
},
|
||||||
ContentStatus::Ready(ref endpoint) => {
|
ContentStatus::Ready(ref endpoint) => {
|
||||||
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
||||||
// Remove path
|
// Remove path (dir or file)
|
||||||
let res = fs::remove_dir_all(&endpoint.path());
|
let res = fs::remove_dir_all(&endpoint.path()).or_else(|_| fs::remove_file(&endpoint.path()));
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
warn!(target: "dapps", "Unable to remove dapp: {:?}", e);
|
warn!(target: "dapps", "Unable to remove dapp/content from cache: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removed.push(entry);
|
removed.push(entry);
|
||||||
len -= 1;
|
|
||||||
}
|
}
|
||||||
removed
|
removed
|
||||||
}
|
}
|
||||||
|
@ -32,20 +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_on: Option<(String, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: URLHint> Drop for ContentFetcher<R> {
|
impl<R: URLHint> Drop for ContentFetcher<R> {
|
||||||
@ -57,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>) -> 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());
|
||||||
|
|
||||||
@ -66,9 +68,20 @@ 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_on: embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
|
||||||
|
Box::new(ContentHandler::error(
|
||||||
|
StatusCode::ServiceUnavailable,
|
||||||
|
"Sync In Progress",
|
||||||
|
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
||||||
|
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
||||||
|
address,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
||||||
self.cache.lock().insert(content_id.to_owned(), status);
|
self.cache.lock().insert(content_id.to_owned(), status);
|
||||||
@ -84,12 +97,10 @@ impl<R: URLHint> ContentFetcher<R> {
|
|||||||
}
|
}
|
||||||
// fallback to resolver
|
// fallback to resolver
|
||||||
if let Ok(content_id) = content_id.from_hex() {
|
if let Ok(content_id) = content_id.from_hex() {
|
||||||
// if app_id is valid, but we are syncing always return true.
|
|
||||||
if self.sync.is_major_syncing() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// else try to resolve the app_id
|
// else try to resolve the app_id
|
||||||
self.resolver.resolve(content_id).is_some()
|
let has_content = self.resolver.resolve(content_id).is_some();
|
||||||
|
// if there is content or we are syncing return true
|
||||||
|
has_content || self.sync.is_major_importing()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -99,30 +110,21 @@ impl<R: URLHint> ContentFetcher<R> {
|
|||||||
let mut cache = self.cache.lock();
|
let mut cache = self.cache.lock();
|
||||||
let content_id = path.app_id.clone();
|
let content_id = path.app_id.clone();
|
||||||
|
|
||||||
if self.sync.is_major_syncing() {
|
|
||||||
return Box::new(ContentHandler::error(
|
|
||||||
StatusCode::ServiceUnavailable,
|
|
||||||
"Sync In Progress",
|
|
||||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
|
||||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (new_status, handler) = {
|
let (new_status, handler) = {
|
||||||
let status = cache.get(&content_id);
|
let status = cache.get(&content_id);
|
||||||
match status {
|
match status {
|
||||||
// Just server dapp
|
// Just serve the content
|
||||||
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
||||||
(None, endpoint.to_async_handler(path, control))
|
(None, endpoint.to_async_handler(path, control))
|
||||||
},
|
},
|
||||||
// App is already being fetched
|
// Content is already being fetched
|
||||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) => {
|
Some(&mut ContentStatus::Fetching(ref fetch_control)) => {
|
||||||
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
||||||
(None, fetch_control.to_handler(control))
|
(None, fetch_control.to_handler(control))
|
||||||
},
|
},
|
||||||
// We need to start fetching app
|
// We need to start fetching the content
|
||||||
None => {
|
None => {
|
||||||
trace!(target: "dapps", "Content unavailable. Fetching...");
|
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
|
||||||
let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||||
let content = self.resolver.resolve(content_hex);
|
let content = self.resolver.resolve(content_hex);
|
||||||
|
|
||||||
@ -141,16 +143,21 @@ impl<R: URLHint> ContentFetcher<R> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
|
// Don't serve dapps if we are still syncing (but serve content)
|
||||||
|
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
|
||||||
|
(None, Self::still_syncing(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_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>)
|
||||||
@ -159,17 +166,20 @@ 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, 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
|
||||||
// `contains` and `to_handler`
|
// `contains` and `to_handler`
|
||||||
@ -177,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>)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -247,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();
|
||||||
@ -258,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>) {
|
||||||
@ -271,6 +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_on: Option<(String, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DappInstaller {
|
impl DappInstaller {
|
||||||
@ -363,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());
|
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))
|
||||||
@ -396,14 +419,14 @@ mod tests {
|
|||||||
fn should_true_if_contains_the_app() {
|
fn should_true_if_contains_the_app() {
|
||||||
// given
|
// given
|
||||||
let path = env::temp_dir();
|
let path = env::temp_dir();
|
||||||
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false));
|
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None);
|
||||||
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
||||||
name: "fake".into(),
|
name: "fake".into(),
|
||||||
description: "".into(),
|
description: "".into(),
|
||||||
version: "".into(),
|
version: "".into(),
|
||||||
author: "".into(),
|
author: "".into(),
|
||||||
icon_url: "".into(),
|
icon_url: "".into(),
|
||||||
});
|
}, Default::default(), None);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
fetcher.set_status("test", ContentStatus::Ready(handler));
|
fetcher.set_status("test", ContentStatus::Ready(handler));
|
||||||
@ -415,4 +438,3 @@ mod tests {
|
|||||||
assert_eq!(fetcher.contains("test3"), false);
|
assert_eq!(fetcher.contains("test3"), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) -> 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))
|
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pages
|
pages
|
||||||
|
@ -25,56 +25,38 @@ pub mod urlhint;
|
|||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
pub mod manifest;
|
pub mod manifest;
|
||||||
|
|
||||||
extern crate parity_dapps_status;
|
extern crate parity_ui;
|
||||||
extern crate parity_dapps_home;
|
|
||||||
|
|
||||||
|
pub const HOME_PAGE: &'static str = "home";
|
||||||
pub const DAPPS_DOMAIN : &'static str = ".parity";
|
pub const DAPPS_DOMAIN : &'static str = ".parity";
|
||||||
pub const RPC_PATH : &'static str = "rpc";
|
pub const RPC_PATH : &'static str = "rpc";
|
||||||
pub const API_PATH : &'static str = "api";
|
pub const API_PATH : &'static str = "api";
|
||||||
pub const UTILS_PATH : &'static str = "parity-utils";
|
pub const UTILS_PATH : &'static str = "parity-utils";
|
||||||
|
|
||||||
pub fn main_page() -> &'static str {
|
|
||||||
"home"
|
|
||||||
}
|
|
||||||
pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String {
|
|
||||||
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_dapps_home::App::default(), UTILS_PATH.to_owned()))
|
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_endpoints(dapps_path: String) -> Endpoints {
|
pub fn all_endpoints(dapps_path: String, signer_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);
|
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
|
||||||
// Home page needs to be safe embed
|
|
||||||
// because we use Cross-Origin LocalStorage.
|
|
||||||
// TODO [ToDr] Account naming should be moved to parity.
|
|
||||||
pages.insert("home".into(), Box::new(
|
|
||||||
PageEndpoint::new_safe_to_embed(parity_dapps_home::App::default())
|
|
||||||
));
|
|
||||||
pages.insert("proxy".into(), ProxyPac::boxed());
|
|
||||||
insert::<parity_dapps_status::App>(&mut pages, "parity");
|
|
||||||
insert::<parity_dapps_status::App>(&mut pages, "status");
|
|
||||||
|
|
||||||
// Optional dapps
|
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||||
wallet_page(&mut pages);
|
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
|
||||||
|
pages.insert("proxy".into(), ProxyPac::boxed(signer_address));
|
||||||
|
|
||||||
pages
|
pages
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "parity-dapps-wallet")]
|
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
|
||||||
fn wallet_page(pages: &mut Endpoints) {
|
pages.insert(id.to_owned(), Box::new(match embed_at {
|
||||||
extern crate parity_dapps_wallet;
|
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
|
||||||
insert::<parity_dapps_wallet::App>(pages, "wallet");
|
Embeddable::No => PageEndpoint::new(T::default()),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "parity-dapps-wallet"))]
|
|
||||||
fn wallet_page(_pages: &mut Endpoints) {}
|
|
||||||
|
|
||||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) {
|
enum Embeddable {
|
||||||
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default())));
|
Yes(Option<(String, u16)>),
|
||||||
|
#[allow(dead_code)]
|
||||||
|
No,
|
||||||
}
|
}
|
||||||
|
@ -156,9 +156,9 @@ impl URLHintContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut it = vec.into_iter();
|
let mut it = vec.into_iter();
|
||||||
let account_slash_repo = it.next().unwrap();
|
let account_slash_repo = it.next().expect("element 0 of 3-len vector known to exist; qed");
|
||||||
let commit = it.next().unwrap();
|
let commit = it.next().expect("element 1 of 3-len vector known to exist; qed");
|
||||||
let owner = it.next().unwrap();
|
let owner = it.next().expect("element 2 of 3-len vector known to exist qed");
|
||||||
|
|
||||||
match (account_slash_repo, commit, owner) {
|
match (account_slash_repo, commit, owner) {
|
||||||
(Token::String(account_slash_repo), Token::FixedBytes(commit), Token::Address(owner)) => {
|
(Token::String(account_slash_repo), Token::FixedBytes(commit), Token::Address(owner)) => {
|
||||||
|
@ -19,57 +19,56 @@
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use hyper::{header, server, Decoder, Encoder, Next};
|
use hyper::{header, server, Decoder, Encoder, Next};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
|
use hyper::mime::Mime;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use util::version;
|
use util::version;
|
||||||
|
|
||||||
|
use handlers::add_security_headers;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ContentHandler {
|
pub struct ContentHandler {
|
||||||
code: StatusCode,
|
code: StatusCode,
|
||||||
content: String,
|
content: String,
|
||||||
mimetype: String,
|
mimetype: Mime,
|
||||||
write_pos: usize,
|
write_pos: usize,
|
||||||
|
safe_to_embed_on: Option<(String, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentHandler {
|
impl ContentHandler {
|
||||||
pub fn ok(content: String, mimetype: String) -> Self {
|
pub fn ok(content: String, mimetype: Mime) -> Self {
|
||||||
ContentHandler {
|
Self::new(StatusCode::Ok, content, mimetype)
|
||||||
code: StatusCode::Ok,
|
|
||||||
content: content,
|
|
||||||
mimetype: mimetype,
|
|
||||||
write_pos: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_found(content: String, mimetype: String) -> Self {
|
pub fn not_found(content: String, mimetype: Mime) -> Self {
|
||||||
ContentHandler {
|
Self::new(StatusCode::NotFound, content, mimetype)
|
||||||
code: StatusCode::NotFound,
|
|
||||||
content: content,
|
|
||||||
mimetype: mimetype,
|
|
||||||
write_pos: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn html(code: StatusCode, content: String) -> Self {
|
pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
|
||||||
Self::new(code, content, "text/html".into())
|
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::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_on)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
|
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
|
||||||
|
Self::new_embeddable(code, content, mimetype, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_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_on: embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +84,8 @@ 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.parse().unwrap()));
|
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||||
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,82 +17,25 @@
|
|||||||
//! Echo Handler
|
//! Echo Handler
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use hyper::{header, server, Decoder, Encoder, Next};
|
use hyper::{server, Decoder, Encoder, Next};
|
||||||
use hyper::method::Method;
|
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use unicase::UniCase;
|
|
||||||
use super::ContentHandler;
|
use super::ContentHandler;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Default)]
|
||||||
/// Type of Cross-Origin request
|
|
||||||
enum Cors {
|
|
||||||
/// Not a Cross-Origin request - no headers needed
|
|
||||||
No,
|
|
||||||
/// Cross-Origin request with valid Origin
|
|
||||||
Allowed(String),
|
|
||||||
/// Cross-Origin request with invalid Origin
|
|
||||||
Forbidden,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EchoHandler {
|
pub struct EchoHandler {
|
||||||
safe_origins: Vec<String>,
|
|
||||||
content: String,
|
content: String,
|
||||||
cors: Cors,
|
|
||||||
handler: Option<ContentHandler>,
|
handler: Option<ContentHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EchoHandler {
|
|
||||||
|
|
||||||
pub fn cors(safe_origins: Vec<String>) -> Self {
|
|
||||||
EchoHandler {
|
|
||||||
safe_origins: safe_origins,
|
|
||||||
content: String::new(),
|
|
||||||
cors: Cors::Forbidden,
|
|
||||||
handler: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cors_header(&self, origin: Option<String>) -> Cors {
|
|
||||||
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
|
|
||||||
for safe in safe_origins {
|
|
||||||
if origin.starts_with(safe) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
match origin {
|
|
||||||
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
|
|
||||||
Cors::Allowed(origin.clone())
|
|
||||||
},
|
|
||||||
None => Cors::No,
|
|
||||||
_ => Cors::Forbidden,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl server::Handler<HttpStream> for EchoHandler {
|
impl server::Handler<HttpStream> for EchoHandler {
|
||||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
fn on_request(&mut self, _: server::Request<HttpStream>) -> Next {
|
||||||
let origin = request.headers().get_raw("origin")
|
Next::read()
|
||||||
.and_then(|list| list.get(0))
|
|
||||||
.and_then(|origin| String::from_utf8(origin.clone()).ok());
|
|
||||||
|
|
||||||
self.cors = self.cors_header(origin);
|
|
||||||
|
|
||||||
// Don't even read the payload if origin is forbidden!
|
|
||||||
if let Cors::Forbidden = self.cors {
|
|
||||||
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
|
|
||||||
Next::write()
|
|
||||||
} else {
|
|
||||||
Next::read()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
match decoder.read_to_string(&mut self.content) {
|
match decoder.read_to_string(&mut self.content) {
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
|
self.handler = Some(ContentHandler::ok(self.content.clone(), mime!(Application/Json)));
|
||||||
Next::write()
|
Next::write()
|
||||||
},
|
},
|
||||||
Ok(_) => Next::read(),
|
Ok(_) => Next::read(),
|
||||||
@ -104,45 +47,14 @@ impl server::Handler<HttpStream> for EchoHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
if let Cors::Allowed(ref domain) = self.cors {
|
self.handler.as_mut()
|
||||||
let mut headers = res.headers_mut();
|
.expect("handler always set in on_request, which is before now; qed")
|
||||||
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
|
.on_response(res)
|
||||||
headers.set(header::AccessControlAllowHeaders(vec![
|
|
||||||
UniCase("origin".to_owned()),
|
|
||||||
UniCase("content-type".to_owned()),
|
|
||||||
UniCase("accept".to_owned()),
|
|
||||||
]));
|
|
||||||
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
|
|
||||||
}
|
|
||||||
self.handler.as_mut().unwrap().on_response(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
self.handler.as_mut().unwrap().on_response_writable(encoder)
|
self.handler.as_mut()
|
||||||
|
.expect("handler always set in on_request, which is before now; qed")
|
||||||
|
.on_response_writable(encoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_correct_cors_value() {
|
|
||||||
// given
|
|
||||||
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
|
|
||||||
let cut = EchoHandler {
|
|
||||||
safe_origins: safe_origins,
|
|
||||||
content: String::new(),
|
|
||||||
cors: Cors::No,
|
|
||||||
handler: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// when
|
|
||||||
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
|
|
||||||
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
|
|
||||||
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
|
|
||||||
let res4 = cut.cors_header(None);
|
|
||||||
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(res1, Cors::Forbidden);
|
|
||||||
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
|
|
||||||
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
|
|
||||||
assert_eq!(res4, Cors::No);
|
|
||||||
}
|
|
||||||
|
@ -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())
|
||||||
},
|
},
|
||||||
|
@ -30,7 +30,26 @@ 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 address;
|
||||||
|
|
||||||
|
/// Adds security-related headers to the Response.
|
||||||
|
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-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||||
|
|
||||||
|
// Embedding header:
|
||||||
|
if let Some(embeddable_on) = embeddable_on {
|
||||||
|
headers.set_raw(
|
||||||
|
"X-Frame-Options",
|
||||||
|
vec![format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// TODO [ToDr] Should we be more strict here (DENY?)?
|
||||||
|
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts URL part from the Request.
|
||||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||||
match *req.uri() {
|
match *req.uri() {
|
||||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||||
|
@ -43,10 +43,9 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
extern crate url as url_lib;
|
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate time;
|
||||||
|
extern crate url as url_lib;
|
||||||
extern crate unicase;
|
extern crate unicase;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
@ -57,13 +56,21 @@ extern crate jsonrpc_core;
|
|||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate parity_dapps;
|
|
||||||
extern crate ethcore_rpc;
|
extern crate ethcore_rpc;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate linked_hash_map;
|
extern crate linked_hash_map;
|
||||||
extern crate fetch;
|
extern crate fetch;
|
||||||
|
extern crate parity_dapps_glue as parity_dapps;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate mime;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate env_logger;
|
||||||
|
|
||||||
|
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod apps;
|
mod apps;
|
||||||
@ -87,16 +94,16 @@ use jsonrpc_core::{IoHandler, IoDelegate};
|
|||||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||||
use ethcore_rpc::Extendable;
|
use ethcore_rpc::Extendable;
|
||||||
|
|
||||||
static DAPPS_DOMAIN : &'static str = ".parity";
|
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
|
||||||
|
|
||||||
/// Indicates sync status
|
/// Indicates sync status
|
||||||
pub trait SyncStatus: Send + Sync {
|
pub trait SyncStatus: Send + Sync {
|
||||||
/// Returns true if there is a major sync happening.
|
/// Returns true if there is a major sync happening.
|
||||||
fn is_major_syncing(&self) -> bool;
|
fn is_major_importing(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
||||||
fn is_major_syncing(&self) -> bool { self() }
|
fn is_major_importing(&self) -> bool { self() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Webapps HTTP+RPC server build.
|
/// Webapps HTTP+RPC server build.
|
||||||
@ -105,6 +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_address: Option<(String, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extendable for ServerBuilder {
|
impl Extendable for ServerBuilder {
|
||||||
@ -121,6 +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_address: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +138,11 @@ impl ServerBuilder {
|
|||||||
self.sync_status = status;
|
self.sync_status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change default signer port.
|
||||||
|
pub fn with_signer_address(&mut self, signer_address: Option<(String, u16)>) {
|
||||||
|
self.signer_address = signer_address;
|
||||||
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// returns result with `Server` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||||
@ -138,6 +152,7 @@ impl ServerBuilder {
|
|||||||
NoAuth,
|
NoAuth,
|
||||||
self.handler.clone(),
|
self.handler.clone(),
|
||||||
self.dapps_path.clone(),
|
self.dapps_path.clone(),
|
||||||
|
self.signer_address.clone(),
|
||||||
self.registrar.clone(),
|
self.registrar.clone(),
|
||||||
self.sync_status.clone(),
|
self.sync_status.clone(),
|
||||||
)
|
)
|
||||||
@ -152,6 +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_address.clone(),
|
||||||
self.registrar.clone(),
|
self.registrar.clone(),
|
||||||
self.sync_status.clone(),
|
self.sync_status.clone(),
|
||||||
)
|
)
|
||||||
@ -180,26 +196,40 @@ impl Server {
|
|||||||
Some(allowed)
|
Some(allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of CORS domains for API endpoint.
|
||||||
|
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
|
||||||
|
match signer_address {
|
||||||
|
Some(signer_address) => vec![
|
||||||
|
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||||
|
format!("http://{}", address(signer_address)),
|
||||||
|
],
|
||||||
|
None => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static>(
|
fn start_http<A: Authorization + 'static>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
hosts: Option<Vec<String>>,
|
hosts: Option<Vec<String>>,
|
||||||
authorization: A,
|
authorization: A,
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
|
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));
|
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));
|
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone()));
|
||||||
|
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();
|
||||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||||
special.insert(
|
special.insert(
|
||||||
router::SpecialEndpoint::Api,
|
router::SpecialEndpoint::Api,
|
||||||
api::RestApi::new(format!("{}", addr), endpoints.clone(), content_fetcher.clone())
|
api::RestApi::new(cors_domains, endpoints.clone(), content_fetcher.clone())
|
||||||
);
|
);
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
@ -208,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,
|
||||||
apps::main_page(),
|
signer_address.clone(),
|
||||||
content_fetcher.clone(),
|
content_fetcher.clone(),
|
||||||
endpoints.clone(),
|
endpoints.clone(),
|
||||||
special.clone(),
|
special.clone(),
|
||||||
@ -272,6 +302,10 @@ pub fn random_filename() -> String {
|
|||||||
rng.gen_ascii_chars().take(12).collect()
|
rng.gen_ascii_chars().take(12).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn address(address: (String, u16)) -> String {
|
||||||
|
format!("{}:{}", address.0, address.1)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod util_tests {
|
mod util_tests {
|
||||||
use super::Server;
|
use super::Server;
|
||||||
@ -291,4 +325,17 @@ mod util_tests {
|
|||||||
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
||||||
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_cors_domains() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let none = Server::cors_domains(None);
|
||||||
|
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(none, Vec::<String>::new());
|
||||||
|
assert_eq!(some, vec!["http://home.parity".to_owned(), "http://127.0.0.1:18180".into()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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: bool,
|
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: false,
|
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: false,
|
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) -> 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: true,
|
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: Default::default(),
|
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
|
||||||
safe_to_embed: self.safe_to_embed,
|
cache: PageCache::Disabled,
|
||||||
|
safe_to_embed_on: self.safe_to_embed_on.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
@ -22,7 +24,7 @@ use hyper::net::HttpStream;
|
|||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
use hyper::{Decoder, Encoder, Next};
|
use hyper::{Decoder, Encoder, Next};
|
||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::ContentHandler;
|
use handlers::{ContentHandler, add_security_headers};
|
||||||
|
|
||||||
/// Represents a file that can be sent to client.
|
/// Represents a file that can be sent to client.
|
||||||
/// Implementation should keep track of bytes already sent internally.
|
/// Implementation should keep track of bytes already sent internally.
|
||||||
@ -57,17 +59,31 @@ pub enum ServedFile<T: Dapp> {
|
|||||||
Error(ContentHandler),
|
Error(ContentHandler),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> Default for ServedFile<T> {
|
impl<T: Dapp> ServedFile<T> {
|
||||||
fn default() -> Self {
|
pub fn new(embeddable_on: Option<(String, u16)>) -> Self {
|
||||||
ServedFile::Error(ContentHandler::error(
|
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_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.
|
||||||
@ -81,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: bool,
|
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> {
|
||||||
@ -115,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::default(), |f| ServedFile::File(f));
|
}.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f));
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,10 +145,24 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
match self.file {
|
match self.file {
|
||||||
ServedFile::File(ref f) => {
|
ServedFile::File(ref f) => {
|
||||||
res.set_status(StatusCode::Ok);
|
res.set_status(StatusCode::Ok);
|
||||||
res.headers_mut().set(header::ContentType(f.content_type().parse().unwrap()));
|
|
||||||
if !self.safe_to_embed {
|
if let PageCache::Enabled = self.cache {
|
||||||
res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
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() {
|
||||||
|
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
||||||
|
Err(()) => debug!(target: "dapps", "invalid MIME type: {}", f.content_type()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Security headers:
|
||||||
|
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) => {
|
||||||
@ -212,8 +244,9 @@ fn should_extract_path_with_appid() {
|
|||||||
port: 8080,
|
port: 8080,
|
||||||
using_dapps_domains: true,
|
using_dapps_domains: true,
|
||||||
},
|
},
|
||||||
file: Default::default(),
|
file: ServedFile::new(None),
|
||||||
safe_to_embed: true,
|
cache: Default::default(),
|
||||||
|
safe_to_embed_on: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -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,22 +26,28 @@ pub struct LocalPageEndpoint {
|
|||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
mime: Option<String>,
|
mime: Option<String>,
|
||||||
info: Option<EndpointInfo>,
|
info: Option<EndpointInfo>,
|
||||||
|
cache: PageCache,
|
||||||
|
embeddable_on: Option<(String, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalPageEndpoint {
|
impl LocalPageEndpoint {
|
||||||
pub fn new(path: PathBuf, info: EndpointInfo) -> 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),
|
||||||
|
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,
|
||||||
|
cache: cache,
|
||||||
|
embeddable_on: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,16 +67,18 @@ impl Endpoint for LocalPageEndpoint {
|
|||||||
app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() },
|
app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() },
|
||||||
prefix: None,
|
prefix: None,
|
||||||
path: path,
|
path: path,
|
||||||
file: Default::default(),
|
file: handler::ServedFile::new(None),
|
||||||
safe_to_embed: false,
|
safe_to_embed_on: self.embeddable_on.clone(),
|
||||||
|
cache: self.cache,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Box::new(handler::PageHandler {
|
Box::new(handler::PageHandler {
|
||||||
app: LocalDapp { path: self.path.clone() },
|
app: LocalDapp { path: self.path.clone() },
|
||||||
prefix: None,
|
prefix: None,
|
||||||
path: path,
|
path: path,
|
||||||
file: Default::default(),
|
file: handler::ServedFile::new(None),
|
||||||
safe_to_embed: false,
|
safe_to_embed_on: self.embeddable_on.clone(),
|
||||||
|
cache: self.cache,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -18,31 +18,46 @@
|
|||||||
|
|
||||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
use apps::DAPPS_DOMAIN;
|
use apps::{HOME_PAGE, DAPPS_DOMAIN};
|
||||||
|
use address;
|
||||||
|
|
||||||
pub struct ProxyPac;
|
pub struct ProxyPac {
|
||||||
|
signer_address: Option<(String, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ProxyPac {
|
impl ProxyPac {
|
||||||
pub fn boxed() -> Box<Endpoint> {
|
pub fn boxed(signer_address: Option<(String, u16)>) -> Box<Endpoint> {
|
||||||
Box::new(ProxyPac)
|
Box::new(ProxyPac {
|
||||||
|
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_address.clone()
|
||||||
|
.map(address)
|
||||||
|
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
r#"
|
r#"
|
||||||
function FindProxyForURL(url, host) {{
|
function FindProxyForURL(url, host) {{
|
||||||
if (shExpMatch(host, "*{0}"))
|
if (shExpMatch(host, "{0}{1}"))
|
||||||
{{
|
{{
|
||||||
return "PROXY {1}:{2}";
|
return "PROXY {4}";
|
||||||
|
}}
|
||||||
|
|
||||||
|
if (shExpMatch(host, "*{1}"))
|
||||||
|
{{
|
||||||
|
return "PROXY {2}:{3}";
|
||||||
}}
|
}}
|
||||||
|
|
||||||
return "DIRECT";
|
return "DIRECT";
|
||||||
}}
|
}}
|
||||||
"#,
|
"#,
|
||||||
DAPPS_DOMAIN, path.host, path.port);
|
HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
|
||||||
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
|
|
||||||
|
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 => {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
use DAPPS_DOMAIN;
|
use apps::DAPPS_DOMAIN;
|
||||||
use hyper::{server, header, StatusCode};
|
use hyper::{server, header, StatusCode};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
|
|
||||||
@ -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,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,13 @@
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
mod host_validation;
|
mod host_validation;
|
||||||
|
|
||||||
use DAPPS_DOMAIN;
|
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};
|
||||||
use hyper::{self, server, Next, Encoder, Decoder, Control, StatusCode};
|
use hyper::{self, server, Next, Encoder, Decoder, Control, StatusCode};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use apps;
|
use apps::{self, DAPPS_DOMAIN};
|
||||||
use apps::fetcher::ContentFetcher;
|
use apps::fetcher::ContentFetcher;
|
||||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||||
use handlers::{Redirection, extract_url, ContentHandler};
|
use handlers::{Redirection, extract_url, ContentHandler};
|
||||||
@ -43,7 +43,7 @@ pub enum SpecialEndpoint {
|
|||||||
|
|
||||||
pub struct Router<A: Authorization + 'static> {
|
pub struct Router<A: Authorization + 'static> {
|
||||||
control: Option<Control>,
|
control: Option<Control>,
|
||||||
main_page: &'static str,
|
signer_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>>>,
|
||||||
@ -61,54 +61,87 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
let endpoint = extract_endpoint(&url);
|
let endpoint = extract_endpoint(&url);
|
||||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||||
|
|
||||||
|
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
|
||||||
|
|
||||||
// Validate Host header
|
// Validate Host header
|
||||||
if let Some(ref hosts) = self.allowed_hosts {
|
if let Some(ref hosts) = self.allowed_hosts {
|
||||||
|
trace!(target: "dapps", "Validating host headers against: {:?}", hosts);
|
||||||
let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect());
|
let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect());
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
|
debug!(target: "dapps", "Rejecting invalid host header.");
|
||||||
self.handler = host_validation::host_invalid_response();
|
self.handler = host_validation::host_invalid_response();
|
||||||
return self.handler.on_request(req);
|
return self.handler.on_request(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!(target: "dapps", "Checking authorization.");
|
||||||
// Check authorization
|
// Check authorization
|
||||||
let auth = self.authorization.is_authorized(&req);
|
let auth = self.authorization.is_authorized(&req);
|
||||||
if let Authorized::No(handler) = auth {
|
if let Authorized::No(handler) = auth {
|
||||||
|
debug!(target: "dapps", "Authorization denied.");
|
||||||
self.handler = handler;
|
self.handler = handler;
|
||||||
return self.handler.on_request(req);
|
return self.handler.on_request(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
|
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
|
||||||
|
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
||||||
self.handler = match endpoint {
|
self.handler = match endpoint {
|
||||||
// First check special endpoints
|
// First check special endpoints
|
||||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||||
self.special.get(endpoint).unwrap().to_async_handler(path.clone().unwrap_or_default(), control)
|
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||||
|
self.special.get(endpoint)
|
||||||
|
.expect("special known to contain key; qed")
|
||||||
|
.to_async_handler(path.clone().unwrap_or_default(), control)
|
||||||
},
|
},
|
||||||
// Then delegate to dapp
|
// Then delegate to dapp
|
||||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||||
self.endpoints.get(&path.app_id).unwrap().to_async_handler(path.clone(), control)
|
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||||
|
self.endpoints.get(&path.app_id)
|
||||||
|
.expect("special known to contain key; qed")
|
||||||
|
.to_async_handler(path.clone(), control)
|
||||||
},
|
},
|
||||||
// Try to resolve and fetch the dapp
|
// Try to resolve and fetch the dapp
|
||||||
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
||||||
|
trace!(target: "dapps", "Resolving to fetchable content.");
|
||||||
self.fetch.to_async_handler(path.clone(), control)
|
self.fetch.to_async_handler(path.clone(), control)
|
||||||
},
|
},
|
||||||
|
// NOTE [todr] /home is redirected to home page since some users may have the redirection cached
|
||||||
|
// (in the past we used 301 instead of 302)
|
||||||
|
// It should be safe to remove it in (near) future.
|
||||||
|
//
|
||||||
// 404 for non-existent content
|
// 404 for non-existent content
|
||||||
(Some(ref path), _) if *req.method() == hyper::method::Method::Get => {
|
(Some(ref path), _) if *req.method() == hyper::Method::Get && path.app_id != "home" => {
|
||||||
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
trace!(target: "dapps", "Resolving to 404.");
|
||||||
Box::new(ContentHandler::error(
|
Box::new(ContentHandler::error(
|
||||||
StatusCode::NotFound,
|
StatusCode::NotFound,
|
||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
"Requested content was not found.",
|
"Requested content was not found.",
|
||||||
Some(&format!("Go back to the <a href=\"{}\">Home Page</a>.", address))
|
None,
|
||||||
|
self.signer_address.clone(),
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
// Redirect any GET request to home.
|
// Redirect any other GET request to signer.
|
||||||
_ if *req.method() == hyper::method::Method::Get => {
|
_ if *req.method() == hyper::Method::Get => {
|
||||||
let address = apps::redirection_address(false, self.main_page);
|
if let Some(signer_address) = self.signer_address.clone() {
|
||||||
Redirection::boxed(address.as_str())
|
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||||
|
Redirection::boxed(&format!("http://{}", address(signer_address)))
|
||||||
|
} else {
|
||||||
|
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||||
|
Box::new(ContentHandler::error(
|
||||||
|
StatusCode::NotFound,
|
||||||
|
"404 Not Found",
|
||||||
|
"Your homepage is not available when Trusted Signer is disabled.",
|
||||||
|
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
|
||||||
|
self.signer_address.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// RPC by default
|
// RPC by default
|
||||||
_ => {
|
_ => {
|
||||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_async_handler(EndpointPath::default(), control)
|
trace!(target: "dapps", "Resolving to RPC call.");
|
||||||
|
self.special.get(&SpecialEndpoint::Rpc)
|
||||||
|
.expect("RPC endpoint always stored; qed")
|
||||||
|
.to_async_handler(EndpointPath::default(), control)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,7 +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,
|
||||||
main_page: &'static str,
|
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>>>,
|
||||||
@ -143,10 +176,12 @@ impl<A: Authorization> Router<A> {
|
|||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Utils).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Utils)
|
||||||
|
.expect("Utils endpoint always stored; qed")
|
||||||
|
.to_handler(EndpointPath::default());
|
||||||
Router {
|
Router {
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
main_page: main_page,
|
signer_address: signer_address,
|
||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
fetch: content_fetcher,
|
fetch: content_fetcher,
|
||||||
special: special,
|
special: special,
|
||||||
|
@ -24,7 +24,7 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
|
|||||||
Box::new(RpcEndpoint {
|
Box::new(RpcEndpoint {
|
||||||
handler: handler,
|
handler: handler,
|
||||||
panic_handler: panic_handler,
|
panic_handler: panic_handler,
|
||||||
cors_domain: Some(vec![AccessControlAllowOrigin::Null]),
|
cors_domain: None,
|
||||||
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
|
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
|
||||||
allowed_hosts: None,
|
allowed_hosts: None,
|
||||||
})
|
})
|
||||||
|
@ -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, serve_with_registrar, request};
|
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_error() {
|
fn should_return_error() {
|
||||||
@ -34,8 +34,9 @@ fn should_return_error() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
||||||
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -56,8 +57,9 @@ fn should_serve_apps() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
||||||
assert!(response.body.contains("Parity Home Screen"), response.body);
|
assert!(response.body.contains("Parity UI"), response.body);
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -78,8 +80,9 @@ fn should_handle_ping() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
||||||
assert_eq!(response.body, "0\n\n".to_owned());
|
assert_eq!(response.body, "0\n\n".to_owned());
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,5 +104,57 @@ fn should_try_to_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_signer_port_cors_headers() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /api/ping HTTP/1.1\r\n\
|
||||||
|
Host: localhost:8080\r\n\
|
||||||
|
Origin: http://127.0.0.1:18180\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert!(
|
||||||
|
response.headers_raw.contains("Access-Control-Allow-Origin: http://127.0.0.1:18180"),
|
||||||
|
"CORS header for signer missing: {:?}",
|
||||||
|
response.headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_signer_port_cors_headers_for_home_parity() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /api/ping HTTP/1.1\r\n\
|
||||||
|
Host: localhost:8080\r\n\
|
||||||
|
Origin: http://home.parity\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert!(
|
||||||
|
response.headers_raw.contains("Access-Control-Allow-Origin: http://home.parity"),
|
||||||
|
"CORS header for home.parity missing: {:?}",
|
||||||
|
response.headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve_with_auth, request};
|
use tests::helpers::{serve_with_auth, request, assert_security_headers_for_embed};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_require_authorization() {
|
fn should_require_authorization() {
|
||||||
@ -66,7 +66,7 @@ fn should_allow_on_valid_auth() {
|
|||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
"\
|
"\
|
||||||
GET /home/ HTTP/1.1\r\n\
|
GET /ui/ HTTP/1.1\r\n\
|
||||||
Host: 127.0.0.1:8080\r\n\
|
Host: 127.0.0.1:8080\r\n\
|
||||||
Connection: close\r\n\
|
Connection: close\r\n\
|
||||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
||||||
@ -76,4 +76,5 @@ fn should_allow_on_valid_auth() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_security_headers_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve_with_registrar, request};
|
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,5 +34,34 @@ 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_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_503_when_syncing_but_should_make_the_calls() {
|
||||||
|
// given
|
||||||
|
let (server, registrar) = serve_with_registrar_and_sync();
|
||||||
|
{
|
||||||
|
let mut responses = registrar.responses.lock();
|
||||||
|
let res1 = responses.get(0).unwrap().clone();
|
||||||
|
let res2 = responses.get(1).unwrap().clone();
|
||||||
|
// Registrar will be called twice - fill up the responses.
|
||||||
|
responses.push(res1);
|
||||||
|
responses.push(res2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET / HTTP/1.1\r\n\
|
||||||
|
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.parity\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 503 Service Unavailable".to_owned());
|
||||||
|
assert_eq!(registrar.calls.lock().len(), 4);
|
||||||
|
assert_security_headers_for_embed(&response.headers);
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ use std::env;
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
|
use env_logger::LogBuilder;
|
||||||
|
|
||||||
use ServerBuilder;
|
use ServerBuilder;
|
||||||
use Server;
|
use Server;
|
||||||
@ -27,6 +28,7 @@ use devtools::http_client;
|
|||||||
|
|
||||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||||
|
const SIGNER_PORT: u16 = 18180;
|
||||||
|
|
||||||
pub struct FakeRegistrar {
|
pub struct FakeRegistrar {
|
||||||
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
||||||
@ -58,11 +60,23 @@ impl ContractClient for FakeRegistrar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_server(hosts: Option<Vec<String>>) -> (Server, Arc<FakeRegistrar>) {
|
fn init_logger() {
|
||||||
|
// Initialize logger
|
||||||
|
if let Ok(log) = env::var("RUST_LOG") {
|
||||||
|
let mut builder = LogBuilder::new();
|
||||||
|
builder.parse(&log);
|
||||||
|
builder.init().expect("Logger is initialized only once.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_server(hosts: Option<Vec<String>>, is_syncing: bool) -> (Server, Arc<FakeRegistrar>) {
|
||||||
|
init_logger();
|
||||||
let registrar = Arc::new(FakeRegistrar::new());
|
let registrar = Arc::new(FakeRegistrar::new());
|
||||||
let mut dapps_path = env::temp_dir();
|
let mut dapps_path = env::temp_dir();
|
||||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||||
let builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone());
|
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone());
|
||||||
|
builder.with_sync_status(Arc::new(move || is_syncing));
|
||||||
|
builder.with_signer_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,
|
||||||
@ -70,25 +84,38 @@ pub fn init_server(hosts: Option<Vec<String>>) -> (Server, Arc<FakeRegistrar>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_auth(user: &str, pass: &str) -> Server {
|
pub fn serve_with_auth(user: &str, pass: &str) -> Server {
|
||||||
|
init_logger();
|
||||||
let registrar = Arc::new(FakeRegistrar::new());
|
let registrar = Arc::new(FakeRegistrar::new());
|
||||||
let mut dapps_path = env::temp_dir();
|
let mut dapps_path = env::temp_dir();
|
||||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||||
let builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar);
|
let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar);
|
||||||
|
builder.with_signer_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()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
||||||
init_server(hosts).0
|
init_server(hosts, false).0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
||||||
init_server(None)
|
init_server(None, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
||||||
|
init_server(None, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve() -> Server {
|
pub fn serve() -> Server {
|
||||||
init_server(None).0
|
init_server(None, false).0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||||
http_client::request(server.addr(), request)
|
http_client::request(server.addr(), request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn assert_security_headers(headers: &[String]) {
|
||||||
|
http_client::assert_security_headers_present(headers, None)
|
||||||
|
}
|
||||||
|
pub fn assert_security_headers_for_embed(headers: &[String]) {
|
||||||
|
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// 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};
|
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() {
|
||||||
@ -33,7 +33,7 @@ fn should_redirect_to_home() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/");
|
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -53,7 +53,27 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Location: /home/");
|
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_redirect_to_home_for_users_with_cached_redirection() {
|
||||||
|
// given
|
||||||
|
let server = serve();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
GET /home/ HTTP/1.1\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
||||||
|
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -73,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!(response.body.contains("href=\"/home/"));
|
assert_security_headers_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -93,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!(response.body.contains("href=\"http://home.parity/"));
|
assert_security_headers_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -159,7 +179,8 @@ fn should_serve_proxy_pac() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.body, "86\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
assert_eq!(response.body, "D5\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -181,5 +202,6 @@ fn should_serve_utils() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.body.contains("function(){"), true);
|
assert_eq!(response.body.contains("function(){"), true);
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ fn should_allow_valid_host() {
|
|||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
"\
|
"\
|
||||||
GET /home/ HTTP/1.1\r\n\
|
GET /ui/ HTTP/1.1\r\n\
|
||||||
Host: localhost:8080\r\n\
|
Host: localhost:8080\r\n\
|
||||||
Connection: close\r\n\
|
Connection: close\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
@ -66,7 +66,7 @@ fn should_serve_dapps_domains() {
|
|||||||
let response = request(server,
|
let response = request(server,
|
||||||
"\
|
"\
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
Host: home.parity\r\n\
|
Host: ui.parity\r\n\
|
||||||
Connection: close\r\n\
|
Connection: close\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
{}
|
{}
|
||||||
@ -98,3 +98,30 @@ fn should_allow_parity_utils_even_on_invalid_domain() {
|
|||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_return_cors_headers_for_rpc() {
|
||||||
|
// given
|
||||||
|
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /rpc HTTP/1.1\r\n\
|
||||||
|
Host: localhost:8080\r\n\
|
||||||
|
Origin: null\r\n\
|
||||||
|
Content-Type: application/json\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert!(
|
||||||
|
!response.headers_raw.contains("Access-Control-Allow-Origin"),
|
||||||
|
"CORS headers were not expected: {:?}",
|
||||||
|
response.headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
18
dapps/ui/Cargo.toml
Normal file
18
dapps/ui/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
description = "Ethcore Parity UI"
|
||||||
|
homepage = "http://ethcore.io"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
name = "parity-ui"
|
||||||
|
version = "1.4.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.1"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
parity-ui-dev = { path = "../../js", optional = true }
|
||||||
|
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-precompiled-js = ["parity-ui-dev"]
|
||||||
|
use-precompiled-js = ["parity-ui-precompiled"]
|
33
dapps/ui/src/lib.rs
Normal file
33
dapps/ui/src/lib.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "parity-ui-dev")]
|
||||||
|
mod inner {
|
||||||
|
extern crate parity_ui_dev;
|
||||||
|
|
||||||
|
pub use self::parity_ui_dev::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parity-ui-precompiled")]
|
||||||
|
mod inner {
|
||||||
|
extern crate parity_ui_precompiled;
|
||||||
|
|
||||||
|
pub use self::parity_ui_precompiled::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub use self::inner::*;
|
@ -11,7 +11,7 @@ build = "build.rs"
|
|||||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clippy = { version = "0.0.90", optional = true}
|
clippy = { version = "0.0.96", optional = true}
|
||||||
ethcore-devtools = { path = "../devtools" }
|
ethcore-devtools = { path = "../devtools" }
|
||||||
ethcore-ipc = { path = "../ipc/rpc" }
|
ethcore-ipc = { path = "../ipc/rpc" }
|
||||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
||||||
|
@ -157,7 +157,7 @@ impl Drop for Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Ipc)]
|
#[ipc]
|
||||||
impl DatabaseService for Database {
|
impl DatabaseService for Database {
|
||||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
|
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
|
||||||
let mut db = self.db.write();
|
let mut db = self.db.write();
|
||||||
|
@ -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();
|
||||||
|
|
||||||
@ -64,3 +85,25 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if all required security headers are present
|
||||||
|
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||||
|
if let Some(port) = port {
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == &format!("X-Frame-Options: ALLOW-FROM http://127.0.0.1:{}", port)).is_some(),
|
||||||
|
"X-Frame-Options: ALLOW-FROM missing: {:?}", headers
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||||
|
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(),
|
||||||
|
"X-XSS-Protection missing: {:?}", headers
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
||||||
|
"X-Content-Type-Options missing: {:?}", headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
39
docker/ubuntu-stable/Dockerfile
Normal file
39
docker/ubuntu-stable/Dockerfile
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
FROM ubuntu:14.04
|
||||||
|
WORKDIR /build
|
||||||
|
# install tools and dependencies
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y \
|
||||||
|
g++ \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
file \
|
||||||
|
binutils
|
||||||
|
|
||||||
|
# install rustup
|
||||||
|
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
|
|
||||||
|
# rustup directory
|
||||||
|
ENV PATH /root/.cargo/bin:$PATH
|
||||||
|
|
||||||
|
# show backtraces
|
||||||
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
|
# show tools
|
||||||
|
RUN rustc -vV && \
|
||||||
|
cargo -V && \
|
||||||
|
gcc -v &&\
|
||||||
|
g++ -v
|
||||||
|
|
||||||
|
# build parity
|
||||||
|
RUN git clone https://github.com/ethcore/parity && \
|
||||||
|
cd parity && \
|
||||||
|
git checkout stable && \
|
||||||
|
git pull && \
|
||||||
|
cargo build --release --verbose && \
|
||||||
|
ls /build/parity/target/release/parity && \
|
||||||
|
strip /build/parity/target/release/parity
|
||||||
|
|
||||||
|
RUN file /build/parity/target/release/parity
|
||||||
|
|
||||||
|
EXPOSE 8080 8545 8180
|
||||||
|
ENTRYPOINT ["/build/parity/target/release/parity"]
|
@ -9,4 +9,4 @@ authors = ["arkpar <arkadiy@ethcore.io"]
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
sha3 = { path = "../util/sha3" }
|
sha3 = { path = "../util/sha3" }
|
||||||
primal = "0.2.3"
|
primal = "0.2.3"
|
||||||
parking_lot = "0.2.6"
|
parking_lot = "0.3"
|
||||||
|
@ -69,14 +69,19 @@ impl EthashManager {
|
|||||||
Some(ref e) if *e == epoch => lights.recent.clone(),
|
Some(ref e) if *e == epoch => lights.recent.clone(),
|
||||||
_ => match lights.prev_epoch.clone() {
|
_ => match lights.prev_epoch.clone() {
|
||||||
Some(e) if e == epoch => {
|
Some(e) if e == epoch => {
|
||||||
// swap
|
// don't swap if recent is newer.
|
||||||
let t = lights.prev_epoch;
|
if lights.recent_epoch > lights.prev_epoch {
|
||||||
lights.prev_epoch = lights.recent_epoch;
|
None
|
||||||
lights.recent_epoch = t;
|
} else {
|
||||||
let t = lights.prev.clone();
|
// swap
|
||||||
lights.prev = lights.recent.clone();
|
let t = lights.prev_epoch;
|
||||||
lights.recent = t;
|
lights.prev_epoch = lights.recent_epoch;
|
||||||
lights.recent.clone()
|
lights.recent_epoch = t;
|
||||||
|
let t = lights.prev.clone();
|
||||||
|
lights.prev = lights.recent.clone();
|
||||||
|
lights.recent = t;
|
||||||
|
lights.recent.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
@ -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"
|
||||||
|
|
||||||
@ -24,8 +24,11 @@ rayon = "0.4.2"
|
|||||||
semver = "0.2"
|
semver = "0.2"
|
||||||
bit-set = "0.4"
|
bit-set = "0.4"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
rand = "0.3"
|
||||||
|
byteorder = "0.5"
|
||||||
|
transient-hashmap = "0.1"
|
||||||
evmjit = { path = "../evmjit", optional = true }
|
evmjit = { path = "../evmjit", optional = true }
|
||||||
clippy = { version = "0.0.90", optional = true}
|
clippy = { version = "0.0.96", optional = true}
|
||||||
ethash = { path = "../ethash" }
|
ethash = { path = "../ethash" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore-io = { path = "../util/io" }
|
ethcore-io = { path = "../util/io" }
|
||||||
@ -36,10 +39,8 @@ ethstore = { path = "../ethstore" }
|
|||||||
ethkey = { path = "../ethkey" }
|
ethkey = { path = "../ethkey" }
|
||||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||||
rlp = { path = "../util/rlp" }
|
rlp = { path = "../util/rlp" }
|
||||||
rand = "0.3"
|
lru-cache = "0.1.0"
|
||||||
lru-cache = "0.0.7"
|
|
||||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
ethcore-bloom-journal = { path = "../util/bloom" }
|
||||||
byteorder = "0.5"
|
|
||||||
|
|
||||||
[dependencies.hyper]
|
[dependencies.hyper]
|
||||||
git = "https://github.com/ethcore/hyper"
|
git = "https://github.com/ethcore/hyper"
|
||||||
@ -47,7 +48,9 @@ default-features = false
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
jit = ["evmjit"]
|
jit = ["evmjit"]
|
||||||
evm-debug = []
|
evm-debug = ["slow-blocks"]
|
||||||
|
evm-debug-tests = ["evm-debug"]
|
||||||
|
slow-blocks = [] # Use SLOW_TX_DURATION="50" (compile time!) to track transactions over 50ms
|
||||||
json-tests = []
|
json-tests = []
|
||||||
test-heavy = []
|
test-heavy = []
|
||||||
dev = ["clippy"]
|
dev = ["clippy"]
|
||||||
|
@ -18,7 +18,7 @@ extern crate ethcore_ipc_codegen;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
||||||
ethcore_ipc_codegen::derive_ipc("src/client/traits.rs").unwrap();
|
ethcore_ipc_codegen::derive_ipc_cond("src/client/traits.rs", cfg!(feature="ipc")).unwrap();
|
||||||
ethcore_ipc_codegen::derive_ipc("src/snapshot/snapshot_service_trait.rs").unwrap();
|
ethcore_ipc_codegen::derive_ipc_cond("src/snapshot/snapshot_service_trait.rs", cfg!(feature="ipc")).unwrap();
|
||||||
ethcore_ipc_codegen::derive_ipc("src/client/chain_notify.rs").unwrap();
|
ethcore_ipc_codegen::derive_ipc_cond("src/client/chain_notify.rs", cfg!(feature="ipc")).unwrap();
|
||||||
}
|
}
|
||||||
|
16
ethcore/light/Cargo.toml
Normal file
16
ethcore/light/Cargo.toml
Normal 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
115
ethcore/light/src/client.rs
Normal 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
47
ethcore/light/src/lib.rs
Normal 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;
|
264
ethcore/light/src/net/buffer_flow.rs
Normal file
264
ethcore/light/src/net/buffer_flow.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
94
ethcore/light/src/net/error.rs
Normal file
94
ethcore/light/src/net/error.rs
Normal 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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
506
ethcore/light/src/net/mod.rs
Normal file
506
ethcore/light/src/net/mod.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
539
ethcore/light/src/net/status.rs
Normal file
539
ethcore/light/src/net/status.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
71
ethcore/light/src/provider.rs
Normal file
71
ethcore/light/src/provider.rs
Normal 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>;
|
||||||
|
}
|
145
ethcore/light/src/request.rs
Normal file
145
ethcore/light/src/request.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
ethcore/res/authority_round.json
Normal file
42
ethcore/res/authority_round.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "TestAuthorityRound",
|
||||||
|
"engine": {
|
||||||
|
"AuthorityRound": {
|
||||||
|
"params": {
|
||||||
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
|
"stepDuration": "1",
|
||||||
|
"authorities" : [
|
||||||
|
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
|
||||||
|
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"accountStartNonce": "0x0",
|
||||||
|
"maximumExtraDataSize": "0x20",
|
||||||
|
"minGasLimit": "0x1388",
|
||||||
|
"networkID" : "0x69"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"seal": {
|
||||||
|
"generic": {
|
||||||
|
"fields": 1,
|
||||||
|
"rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"difficulty": "0x20000",
|
||||||
|
"author": "0x0000000000000000000000000000000000000000",
|
||||||
|
"timestamp": "0x00",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"extraData": "0x",
|
||||||
|
"gasLimit": "0x2fefd8"
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
"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 } } } },
|
||||||
|
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||||
|
"9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "TestAuthority",
|
"name": "TestBasicAuthority",
|
||||||
"engine": {
|
"engine": {
|
||||||
"BasicAuthority": {
|
"BasicAuthority": {
|
||||||
"params": {
|
"params": {
|
@ -10,7 +10,14 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30"
|
"homesteadTransition": "0x118c30",
|
||||||
|
"eip150Transition": "0x2625a0",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff",
|
||||||
|
"ecip1010PauseTransition": "0x2dc6c0",
|
||||||
|
"ecip1010ContinueTransition": "0x4c4b40"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -38,10 +45,18 @@
|
|||||||
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
|
"enode://08c7ee6a4f861ff0664a49532bcc86de1363acd608999d1b76609bb9bc278649906f069057630fd9493924a368b5d1dc9b8f8bf13ac26df72512f6d1fabd8c95@45.32.7.81:30303",
|
||||||
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
|
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
|
||||||
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
|
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
|
||||||
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
|
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
|
||||||
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303"
|
"enode://ca5ae4eca09ba6787e29cf6d86f7634d07aae6b9e6317a59aff675851c0bf445068173208cf8ef7f5cd783d8e29b85b2fa3fa358124cf0546823149724f9bde1@138.68.1.16:30303",
|
||||||
|
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303",
|
||||||
|
"enode://fa20444ef991596ce99b81652ac4e61de1eddc4ff21d3cd42762abd7ed47e7cf044d3c9ccddaf6035d39725e4eb372806787829ccb9a08ec7cb71883cb8c3abd@50.149.116.182:30303",
|
||||||
|
"enode://4bd6a4df3612c718333eb5ea7f817923a8cdf1bed89cee70d1710b45a0b6b77b2819846440555e451a9b602ad2efa2d2facd4620650249d8468008946887820a@71.178.232.20:30304",
|
||||||
|
"enode://921cf8e4c345fe8db913c53964f9cadc667644e7f20195a0b7d877bd689a5934e146ff2c2259f1bae6817b6585153a007ceb67d260b720fa3e6fc4350df25c7f@51.255.49.170:30303",
|
||||||
|
"enode://ffea3b01c000cdd89e1e9229fea3e80e95b646f9b2aa55071fc865e2f19543c9b06045cc2e69453e6b78100a119e66be1b5ad50b36f2ffd27293caa28efdd1b2@128.199.93.177:3030",
|
||||||
|
"enode://ee3da491ce6a155eb132708eb0e8d04b0637926ec0ae1b79e63fc97cb9fc3818f49250a0ae0d7f79ed62b66ec677f408c4e01741504dc7a051e274f1e803d454@91.121.65.179:40404",
|
||||||
|
"enode://48e063a6cf5f335b1ef2ed98126bf522cf254396f850c7d442fe2edbbc23398787e14cd4de7968a00175a82762de9cbe9e1407d8ccbcaeca5004d65f8398d759@159.203.255.59:30303"
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
47
ethcore/res/ethereum/eip150_test.json
Normal file
47
ethcore/res/ethereum/eip150_test.json
Normal 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": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 } } } }
|
||||||
|
}
|
||||||
|
}
|
47
ethcore/res/ethereum/eip161_test.json
Normal file
47
ethcore/res/ethereum/eip161_test.json
Normal 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 } } } }
|
||||||
|
}
|
||||||
|
}
|
@ -11,10 +11,15 @@
|
|||||||
"durationLimit": "0x3C",
|
"durationLimit": "0x3C",
|
||||||
"blockReward": "0x6f05b59d3b200000",
|
"blockReward": "0x6f05b59d3b200000",
|
||||||
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
||||||
"frontierCompatibilityModeLimit": "0x30d40",
|
"homesteadTransition": "0x30d40",
|
||||||
"difficultyHardforkTransition": "0x59d9",
|
"difficultyHardforkTransition": "0x59d9",
|
||||||
"difficultyHardforkBoundDivisor": "0x0200",
|
"difficultyHardforkBoundDivisor": "0x0200",
|
||||||
"bombDefuseTransition": "0x30d40"
|
"bombDefuseTransition": "0x30d40",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0x3bb2bb5c6c9c9b7f4ef430b47dc7e026310042ea",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30",
|
"homesteadTransition": "0x118c30",
|
||||||
"daoHardforkTransition": "0x1d4c00",
|
"daoHardforkTransition": "0x1d4c00",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||||
"daoHardforkAccounts": [
|
"daoHardforkAccounts": [
|
||||||
@ -129,7 +129,13 @@
|
|||||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||||
]
|
],
|
||||||
|
"eip150Transition": "0x259518",
|
||||||
|
"eip155Transition": 2675000,
|
||||||
|
"eip160Transition": 2675000,
|
||||||
|
"eip161abcTransition": 2675000,
|
||||||
|
"eip161dTransition": 2675000,
|
||||||
|
"maxCodeSize": 24576
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -157,13 +163,27 @@
|
|||||||
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
"enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@136.243.154.245:30303",
|
"enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303",
|
||||||
|
"enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303",
|
||||||
"enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303",
|
"enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303",
|
||||||
"enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303",
|
"enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303",
|
||||||
|
"enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303",
|
||||||
|
"enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303",
|
||||||
|
"enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303",
|
||||||
|
"enode://84f5d5957b4880a8b0545e32e05472318898ad9fc8ebe1d56c90c12334a98e12351eccfdf3a2bf72432ac38b57e9d348400d17caa083879ade3822390f89773f@10.1.52.78:30303",
|
||||||
|
"enode://f90dc9b9bf7b8db97726b7849e175f1eb2707f3d8f281c929336e398dd89b0409fc6aeceb89e846278e9d3ecc3857cebfbe6758ff352ece6fe5d42921ee761db@10.1.173.87:30303",
|
||||||
|
"enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@10.3.149.199:30303",
|
||||||
|
"enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303",
|
||||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
"enode://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://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io: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 } } } },
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30",
|
"homesteadTransition": "0x118c30",
|
||||||
"daoHardforkTransition": "0x1d4c00",
|
"daoHardforkTransition": "0x1d4c00",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||||
"daoHardforkAccounts": [
|
"daoHardforkAccounts": [
|
||||||
@ -129,7 +129,12 @@
|
|||||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||||
]
|
],
|
||||||
|
"eip150Transition": "0x7fffffffffffffff",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,12 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
"homesteadTransition": "0x7fffffffffffffff",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,12 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x0"
|
"homesteadTransition": "0x0",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,12 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
||||||
"frontierCompatibilityModeLimit": "0x789b0"
|
"homesteadTransition": "0x789b0",
|
||||||
|
"eip150Transition": "0x1b34d8",
|
||||||
|
"eip155Transition": 1885000,
|
||||||
|
"eip160Transition": 1885000,
|
||||||
|
"eip161abcTransition": 1885000,
|
||||||
|
"eip161dTransition": 1885000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -17,7 +22,9 @@
|
|||||||
"accountStartNonce": "0x0100000",
|
"accountStartNonce": "0x0100000",
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x2"
|
"networkID" : "0x2",
|
||||||
|
"forkBlock": "0x1b34d8",
|
||||||
|
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
|
@ -8,7 +8,13 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x08",
|
"durationLimit": "0x08",
|
||||||
"blockReward": "0x14D1120D7B160000",
|
"blockReward": "0x14D1120D7B160000",
|
||||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||||
|
"homesteadTransition": "0x7fffffffffffffff",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit ac5475d676536cb945f98e9ff98384c01abd0599
|
Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "DAO hard-fork consensus test",
|
"name": "EIP150.1b hard-fork consensus test",
|
||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x5",
|
"homesteadTransition": "0x5",
|
||||||
"daoHardforkTransition": "0x8",
|
"daoHardforkTransition": "0x8",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||||
"daoHardforkAccounts": [
|
"daoHardforkAccounts": [
|
||||||
@ -129,7 +129,12 @@
|
|||||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||||
]
|
],
|
||||||
|
"eip150Transition": "0xa",
|
||||||
|
"eip155Transition": "0x7fffffffffffffff",
|
||||||
|
"eip160Transition": "0x7fffffffffffffff",
|
||||||
|
"eip161abcTransition": "0x7fffffffffffffff",
|
||||||
|
"eip161dTransition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
@ -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" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,9 @@ impl<'db> HashDB for AccountDB<'db>{
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||||
if key == &SHA3_NULL_RLP {
|
if key == &SHA3_NULL_RLP {
|
||||||
return Some(&NULL_RLP_STATIC);
|
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
|
||||||
}
|
}
|
||||||
self.db.get(&combine_key(&self.address_hash, key))
|
self.db.get(&combine_key(&self.address_hash, key))
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ impl<'db> HashDB for AccountDB<'db>{
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emplace(&mut self, _key: H256, _value: Bytes) {
|
fn emplace(&mut self, _key: H256, _value: DBValue) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ impl<'db> HashDB for AccountDB<'db>{
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
|
fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
|
||||||
self.db.get_aux(hash)
|
self.db.get_aux(hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,9 +158,9 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||||
if key == &SHA3_NULL_RLP {
|
if key == &SHA3_NULL_RLP {
|
||||||
return Some(&NULL_RLP_STATIC);
|
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
|
||||||
}
|
}
|
||||||
self.db.get(&combine_key(&self.address_hash, key))
|
self.db.get(&combine_key(&self.address_hash, key))
|
||||||
}
|
}
|
||||||
@ -178,16 +178,16 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
|||||||
}
|
}
|
||||||
let k = value.sha3();
|
let k = value.sha3();
|
||||||
let ak = combine_key(&self.address_hash, &k);
|
let ak = combine_key(&self.address_hash, &k);
|
||||||
self.db.emplace(ak, value.to_vec());
|
self.db.emplace(ak, DBValue::from_slice(value));
|
||||||
k
|
k
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emplace(&mut self, key: H256, value: Bytes) {
|
fn emplace(&mut self, key: H256, value: DBValue) {
|
||||||
if key == SHA3_NULL_RLP {
|
if key == SHA3_NULL_RLP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let key = combine_key(&self.address_hash, &key);
|
let key = combine_key(&self.address_hash, &key);
|
||||||
self.db.emplace(key, value.to_vec())
|
self.db.emplace(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&mut self, key: &H256) {
|
fn remove(&mut self, key: &H256) {
|
||||||
@ -202,7 +202,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
|||||||
self.db.insert_aux(hash, value);
|
self.db.insert_aux(hash, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
|
fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
|
||||||
self.db.get_aux(hash)
|
self.db.get_aux(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,9 +218,9 @@ impl<'db> HashDB for Wrapping<'db> {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||||
if key == &SHA3_NULL_RLP {
|
if key == &SHA3_NULL_RLP {
|
||||||
return Some(&NULL_RLP_STATIC);
|
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
|
||||||
}
|
}
|
||||||
self.0.get(key)
|
self.0.get(key)
|
||||||
}
|
}
|
||||||
@ -236,7 +236,7 @@ impl<'db> HashDB for Wrapping<'db> {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emplace(&mut self, _key: H256, _value: Bytes) {
|
fn emplace(&mut self, _key: H256, _value: DBValue) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,9 +252,9 @@ impl<'db> HashDB for WrappingMut<'db>{
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, key: &H256) -> Option<&[u8]> {
|
fn get(&self, key: &H256) -> Option<DBValue> {
|
||||||
if key == &SHA3_NULL_RLP {
|
if key == &SHA3_NULL_RLP {
|
||||||
return Some(&NULL_RLP_STATIC);
|
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
|
||||||
}
|
}
|
||||||
self.0.get(key)
|
self.0.get(key)
|
||||||
}
|
}
|
||||||
@ -273,7 +273,7 @@ impl<'db> HashDB for WrappingMut<'db>{
|
|||||||
self.0.insert(value)
|
self.0.insert(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emplace(&mut self, key: H256, value: Bytes) {
|
fn emplace(&mut self, key: H256, value: DBValue) {
|
||||||
if key == SHA3_NULL_RLP {
|
if key == SHA3_NULL_RLP {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ use std::time::{Instant, Duration};
|
|||||||
use util::{Mutex, RwLock};
|
use util::{Mutex, RwLock};
|
||||||
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
||||||
use ethstore::dir::{KeyDirectory};
|
use ethstore::dir::{KeyDirectory};
|
||||||
use ethstore::ethkey::{Address, Message, Secret, Random, Generator};
|
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
||||||
use ethjson::misc::AccountMeta;
|
use ethjson::misc::AccountMeta;
|
||||||
pub use ethstore::ethkey::Signature;
|
pub use ethstore::ethkey::Signature;
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ enum Unlock {
|
|||||||
/// Use with caution.
|
/// Use with caution.
|
||||||
Perm,
|
Perm,
|
||||||
/// Account unlocked with a timeout
|
/// Account unlocked with a timeout
|
||||||
Timed((Instant, u32)),
|
Timed(Instant),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data associated with account.
|
/// Data associated with account.
|
||||||
@ -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,16 +185,24 @@ 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())).unwrap())
|
sstore: Box::new(EthStore::open(Box::new(NullDir::default()))
|
||||||
|
.expect("NullDir load always succeeds; qed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new random account.
|
/// Creates new random account.
|
||||||
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
||||||
let secret = Random.generate().unwrap().secret().clone();
|
self.new_account_and_public(password).map(|d| d.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new random account and returns address and public key
|
||||||
|
pub fn new_account_and_public(&self, password: &str) -> Result<(Address, Public), Error> {
|
||||||
|
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||||
|
let public = acc.public().clone();
|
||||||
|
let secret = acc.secret().clone();
|
||||||
let address = try!(self.sstore.insert_account(secret, password));
|
let address = try!(self.sstore.insert_account(secret, password));
|
||||||
Ok(address)
|
Ok((address, public))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts new account into underlying store.
|
/// Inserts new account into underlying store.
|
||||||
@ -257,6 +275,20 @@ impl AccountProvider {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||||
|
pub fn test_password(&self, account: &Address, password: String) -> Result<bool, Error> {
|
||||||
|
match self.sstore.sign(account, &password, &Default::default()) {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(SSError::InvalidPassword) => Ok(false),
|
||||||
|
Err(e) => Err(Error::SStore(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
||||||
|
pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> {
|
||||||
|
self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore)
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper method used for unlocking accounts.
|
/// Helper method used for unlocking accounts.
|
||||||
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
||||||
// verify password by signing dump message
|
// verify password by signing dump message
|
||||||
@ -280,6 +312,21 @@ impl AccountProvider {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn password(&self, account: &Address) -> Result<String, Error> {
|
||||||
|
let mut unlocked = self.unlocked.lock();
|
||||||
|
let data = try!(unlocked.get(account).ok_or(Error::NotUnlocked)).clone();
|
||||||
|
if let Unlock::Temp = data.unlock {
|
||||||
|
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||||
|
}
|
||||||
|
if let Unlock::Timed(ref end) = data.unlock {
|
||||||
|
if Instant::now() > *end {
|
||||||
|
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||||
|
return Err(Error::NotUnlocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(data.password.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Unlocks account permanently.
|
/// Unlocks account permanently.
|
||||||
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::Perm)
|
self.unlock_account(account, password, Unlock::Perm)
|
||||||
@ -292,7 +339,7 @@ impl AccountProvider {
|
|||||||
|
|
||||||
/// Unlocks account temporarily with a timeout.
|
/// Unlocks account temporarily with a timeout.
|
||||||
pub fn unlock_account_timed(&self, account: Address, password: String, duration_ms: u32) -> Result<(), Error> {
|
pub fn unlock_account_timed(&self, account: Address, password: String, duration_ms: u32) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::Timed((Instant::now(), duration_ms)))
|
self.unlock_account(account, password, Unlock::Timed(Instant::now() + Duration::from_millis(duration_ms as u64)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if given account is unlocked
|
/// Checks if given account is unlocked
|
||||||
@ -301,51 +348,16 @@ impl AccountProvider {
|
|||||||
unlocked.get(&account).is_some()
|
unlocked.get(&account).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the message. Account must be unlocked.
|
/// Signs the message. If password is not provided the account must be unlocked.
|
||||||
pub fn sign(&self, account: Address, message: Message) -> Result<Signature, Error> {
|
pub fn sign(&self, account: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||||
let data = {
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
let mut unlocked = self.unlocked.lock();
|
Ok(try!(self.sstore.sign(&account, &password, &message)))
|
||||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
|
||||||
if let Unlock::Temp = data.unlock {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
}
|
|
||||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
|
||||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
return Err(Error::NotUnlocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
let signature = try!(self.sstore.sign(&account, &data.password, &message));
|
|
||||||
Ok(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts a message. Account must be unlocked.
|
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||||
pub fn decrypt(&self, account: Address, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let data = {
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
let mut unlocked = self.unlocked.lock();
|
Ok(try!(self.sstore.decrypt(&account, &password, shared_mac, message)))
|
||||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
|
||||||
if let Unlock::Temp = data.unlock {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
}
|
|
||||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
|
||||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
return Err(Error::NotUnlocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(try!(self.sstore.decrypt(&account, &data.password, shared_mac, message)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unlocks an account, signs the message, and locks it again.
|
|
||||||
pub fn sign_with_password(&self, account: Address, password: String, message: Message) -> Result<Signature, Error> {
|
|
||||||
let signature = try!(self.sstore.sign(&account, &password, &message));
|
|
||||||
Ok(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying `SecretStore` reference if one exists.
|
/// Returns the underlying `SecretStore` reference if one exists.
|
||||||
@ -361,11 +373,11 @@ impl AccountProvider {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{AccountProvider, AddressBook};
|
use super::{AccountProvider, AddressBook, Unlock};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::time::Instant;
|
||||||
use ethjson::misc::AccountMeta;
|
use ethjson::misc::AccountMeta;
|
||||||
use ethstore::ethkey::{Generator, Random};
|
use ethstore::ethkey::{Generator, Random};
|
||||||
use std::time::Duration;
|
|
||||||
use devtools::RandomTempPath;
|
use devtools::RandomTempPath;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -386,8 +398,8 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -397,11 +409,11 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -409,10 +421,10 @@ mod tests {
|
|||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
|
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 60000).is_err());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
|
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 60000).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
::std::thread::sleep(Duration::from_millis(2000));
|
ap.unlocked.lock().get_mut(&kp.address()).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,14 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Evm input params.
|
//! Evm input params.
|
||||||
use common::*;
|
use util::{Address, Bytes, Uint, U256};
|
||||||
|
use util::hash::{H256, FixedHash};
|
||||||
|
use util::sha3::{Hashable, SHA3_EMPTY};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Transaction value
|
/// Transaction value
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ActionValue {
|
pub enum ActionValue {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Ethcore basic typenames.
|
//! Ethcore basic typenames.
|
||||||
|
|
||||||
use util::*;
|
use util::hash::H2048;
|
||||||
|
|
||||||
/// Type for a 2048-bit log-bloom, as used by our blocks.
|
/// Type for a 2048-bit log-bloom, as used by our blocks.
|
||||||
pub type LogBloom = H2048;
|
pub type LogBloom = H2048;
|
||||||
|
@ -16,14 +16,26 @@
|
|||||||
|
|
||||||
//! Blockchain block.
|
//! Blockchain block.
|
||||||
|
|
||||||
use common::*;
|
use std::sync::Arc;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, Decoder, DecoderError, View, Stream};
|
||||||
|
use util::{Bytes, Address, Uint, FixedHash, Hashable, U256, H256, ordered_trie_root, SHA3_NULL_RLP};
|
||||||
|
use util::error::{Mismatch, OutOfBounds};
|
||||||
|
|
||||||
|
use basic_types::{LogBloom, Seal};
|
||||||
|
use env_info::{EnvInfo, LastHashes};
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use state::*;
|
use error::{Error, BlockError, TransactionError};
|
||||||
use state_db::StateDB;
|
|
||||||
use verification::PreverifiedBlock;
|
|
||||||
use trace::FlatTrace;
|
|
||||||
use factory::Factories;
|
use factory::Factories;
|
||||||
use rlp::*;
|
use header::Header;
|
||||||
|
use receipt::Receipt;
|
||||||
|
use state::State;
|
||||||
|
use state_db::StateDB;
|
||||||
|
use trace::FlatTrace;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
|
use verification::PreverifiedBlock;
|
||||||
|
use views::BlockView;
|
||||||
|
|
||||||
/// A block, encoded as it is on the block chain.
|
/// A block, encoded as it is on the block chain.
|
||||||
#[derive(Default, Debug, Clone, PartialEq)]
|
#[derive(Default, Debug, Clone, PartialEq)]
|
||||||
@ -338,7 +350,7 @@ impl<'x> OpenBlock<'x> {
|
|||||||
let t = outcome.trace;
|
let t = outcome.trace;
|
||||||
self.block.traces.as_mut().map(|traces| traces.push(t));
|
self.block.traces.as_mut().map(|traces| traces.push(t));
|
||||||
self.block.receipts.push(outcome.receipt);
|
self.block.receipts.push(outcome.receipt);
|
||||||
Ok(self.block.receipts.last().unwrap())
|
Ok(self.block.receipts.last().expect("receipt just pushed; qed"))
|
||||||
}
|
}
|
||||||
Err(x) => Err(From::from(x))
|
Err(x) => Err(From::from(x))
|
||||||
}
|
}
|
||||||
@ -392,6 +404,10 @@ impl<'x> OpenBlock<'x> {
|
|||||||
uncle_bytes: uncle_bytes,
|
uncle_bytes: uncle_bytes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Return mutable block reference. To be used in tests only.
|
||||||
|
pub fn block_mut (&mut self) -> &mut ExecutedBlock { &mut self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x> IsBlock for OpenBlock<'x> {
|
impl<'x> IsBlock for OpenBlock<'x> {
|
||||||
@ -518,25 +534,38 @@ pub fn enact(
|
|||||||
b.set_uncles_hash(header.uncles_hash().clone());
|
b.set_uncles_hash(header.uncles_hash().clone());
|
||||||
b.set_transactions_root(header.transactions_root().clone());
|
b.set_transactions_root(header.transactions_root().clone());
|
||||||
b.set_receipts_root(header.receipts_root().clone());
|
b.set_receipts_root(header.receipts_root().clone());
|
||||||
for t in transactions { try!(b.push_transaction(t.clone(), None)); }
|
|
||||||
for u in uncles { try!(b.push_uncle(u.clone())); }
|
try!(push_transactions(&mut b, transactions));
|
||||||
|
for u in uncles {
|
||||||
|
try!(b.push_uncle(u.clone()));
|
||||||
|
}
|
||||||
Ok(b.close_and_lock())
|
Ok(b.close_and_lock())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
#[inline]
|
||||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
#[cfg(not(feature = "slow-blocks"))]
|
||||||
pub fn enact_bytes(
|
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
|
||||||
block_bytes: &[u8],
|
for t in transactions {
|
||||||
engine: &Engine,
|
try!(block.push_transaction(t.clone(), None));
|
||||||
tracing: bool,
|
}
|
||||||
db: StateDB,
|
Ok(())
|
||||||
parent: &Header,
|
}
|
||||||
last_hashes: Arc<LastHashes>,
|
|
||||||
factories: Factories,
|
#[cfg(feature = "slow-blocks")]
|
||||||
) -> Result<LockedBlock, Error> {
|
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
|
||||||
let block = BlockView::new(block_bytes);
|
use std::time;
|
||||||
let header = block.header();
|
|
||||||
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100);
|
||||||
|
for t in transactions {
|
||||||
|
let hash = t.hash();
|
||||||
|
let start = time::Instant::now();
|
||||||
|
try!(block.push_transaction(t.clone(), None));
|
||||||
|
let took = start.elapsed();
|
||||||
|
if took > time::Duration::from_millis(slow_tx) {
|
||||||
|
warn!("Heavy transaction in block {:?}: {:?}", block.header().number(), hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||||
@ -554,26 +583,52 @@ pub fn enact_verified(
|
|||||||
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
|
||||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
|
||||||
pub fn enact_and_seal(
|
|
||||||
block_bytes: &[u8],
|
|
||||||
engine: &Engine,
|
|
||||||
tracing: bool,
|
|
||||||
db: StateDB,
|
|
||||||
parent: &Header,
|
|
||||||
last_hashes: Arc<LastHashes>,
|
|
||||||
factories: Factories,
|
|
||||||
) -> Result<SealedBlock, Error> {
|
|
||||||
let header = BlockView::new(block_bytes).header_view();
|
|
||||||
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use common::*;
|
use engines::Engine;
|
||||||
|
use env_info::LastHashes;
|
||||||
|
use error::Error;
|
||||||
|
use header::Header;
|
||||||
|
use factory::Factories;
|
||||||
|
use state_db::StateDB;
|
||||||
|
use views::BlockView;
|
||||||
|
use util::Address;
|
||||||
|
use util::hash::FixedHash;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||||
|
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||||
|
fn enact_bytes(
|
||||||
|
block_bytes: &[u8],
|
||||||
|
engine: &Engine,
|
||||||
|
tracing: bool,
|
||||||
|
db: StateDB,
|
||||||
|
parent: &Header,
|
||||||
|
last_hashes: Arc<LastHashes>,
|
||||||
|
factories: Factories,
|
||||||
|
) -> Result<LockedBlock, Error> {
|
||||||
|
let block = BlockView::new(block_bytes);
|
||||||
|
let header = block.header();
|
||||||
|
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, factories)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
||||||
|
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||||
|
fn enact_and_seal(
|
||||||
|
block_bytes: &[u8],
|
||||||
|
engine: &Engine,
|
||||||
|
tracing: bool,
|
||||||
|
db: StateDB,
|
||||||
|
parent: &Header,
|
||||||
|
last_hashes: Arc<LastHashes>,
|
||||||
|
factories: Factories,
|
||||||
|
) -> Result<SealedBlock, Error> {
|
||||||
|
let header = BlockView::new(block_bytes).header_view();
|
||||||
|
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal())))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn open_block() {
|
fn open_block() {
|
||||||
|
@ -29,3 +29,12 @@ pub struct BestBlock {
|
|||||||
/// Best block uncompressed bytes
|
/// Best block uncompressed bytes
|
||||||
pub block: Bytes,
|
pub block: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Best ancient block info. If the blockchain has a gap this keeps track of where it starts.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BestAncientBlock {
|
||||||
|
/// Best block hash.
|
||||||
|
pub hash: H256,
|
||||||
|
/// Best block number.
|
||||||
|
pub number: BlockNumber,
|
||||||
|
}
|
||||||
|
@ -27,7 +27,8 @@ use log_entry::{LogEntry, LocalizedLogEntry};
|
|||||||
use receipt::Receipt;
|
use receipt::Receipt;
|
||||||
use blooms::{Bloom, BloomGroup};
|
use blooms::{Bloom, BloomGroup};
|
||||||
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
||||||
use blockchain::best_block::BestBlock;
|
use blockchain::best_block::{BestBlock, BestAncientBlock};
|
||||||
|
use types::blockchain_info::BlockChainInfo;
|
||||||
use types::tree_route::TreeRoute;
|
use types::tree_route::TreeRoute;
|
||||||
use blockchain::update::ExtrasUpdate;
|
use blockchain::update::ExtrasUpdate;
|
||||||
use blockchain::{CacheSize, ImportRoute, Config};
|
use blockchain::{CacheSize, ImportRoute, Config};
|
||||||
@ -43,16 +44,24 @@ pub trait BlockProvider {
|
|||||||
/// (though not necessarily a part of the canon chain).
|
/// (though not necessarily a part of the canon chain).
|
||||||
fn is_known(&self, hash: &H256) -> bool;
|
fn is_known(&self, hash: &H256) -> bool;
|
||||||
|
|
||||||
/// Get the first block which this chain holds.
|
/// Get the first block of the best part of the chain.
|
||||||
|
/// Return `None` if there is no gap and the first block is the genesis.
|
||||||
/// Any queries of blocks which precede this one are not guaranteed to
|
/// Any queries of blocks which precede this one are not guaranteed to
|
||||||
/// succeed.
|
/// succeed.
|
||||||
fn first_block(&self) -> H256;
|
fn first_block(&self) -> Option<H256>;
|
||||||
|
|
||||||
/// Get the number of the first block.
|
/// Get the number of the first block.
|
||||||
fn first_block_number(&self) -> BlockNumber {
|
fn first_block_number(&self) -> Option<BlockNumber> {
|
||||||
self.block_number(&self.first_block()).expect("First block always stored; qed")
|
self.first_block().map(|b| self.block_number(&b).expect("First block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the best block of an first block sequence if there is a gap.
|
||||||
|
fn best_ancient_block(&self) -> Option<H256>;
|
||||||
|
|
||||||
|
/// Get the number of the first block.
|
||||||
|
fn best_ancient_number(&self) -> Option<BlockNumber> {
|
||||||
|
self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||||
|
}
|
||||||
/// Get raw block data
|
/// Get raw block data
|
||||||
fn block(&self, hash: &H256) -> Option<Bytes>;
|
fn block(&self, hash: &H256) -> Option<Bytes>;
|
||||||
|
|
||||||
@ -123,7 +132,8 @@ pub trait BlockProvider {
|
|||||||
|
|
||||||
/// Returns the header of the genesis block.
|
/// Returns the header of the genesis block.
|
||||||
fn genesis_header(&self) -> Header {
|
fn genesis_header(&self) -> Header {
|
||||||
self.block_header(&self.genesis_hash()).unwrap()
|
self.block_header(&self.genesis_hash())
|
||||||
|
.expect("Genesis header always stored; qed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns numbers of blocks containing given bloom.
|
/// Returns numbers of blocks containing given bloom.
|
||||||
@ -160,9 +170,14 @@ impl bc::group::BloomGroupDatabase for BlockChain {
|
|||||||
pub struct BlockChain {
|
pub struct BlockChain {
|
||||||
// All locks must be captured in the order declared here.
|
// All locks must be captured in the order declared here.
|
||||||
blooms_config: bc::Config,
|
blooms_config: bc::Config,
|
||||||
first_block: H256,
|
|
||||||
|
|
||||||
best_block: RwLock<BestBlock>,
|
best_block: RwLock<BestBlock>,
|
||||||
|
// Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps.
|
||||||
|
// Only updated with `insert_unordered_block`.
|
||||||
|
best_ancient_block: RwLock<Option<BestAncientBlock>>,
|
||||||
|
// Stores the last block of the last sequence of blocks. `None` if there are no gaps.
|
||||||
|
// This is calculated on start and does not get updated.
|
||||||
|
first_block: Option<H256>,
|
||||||
|
|
||||||
// block cache
|
// block cache
|
||||||
block_headers: RwLock<HashMap<H256, Bytes>>,
|
block_headers: RwLock<HashMap<H256, Bytes>>,
|
||||||
@ -181,6 +196,7 @@ pub struct BlockChain {
|
|||||||
|
|
||||||
pending_best_block: RwLock<Option<BestBlock>>,
|
pending_best_block: RwLock<Option<BestBlock>>,
|
||||||
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
||||||
|
pending_block_details: RwLock<HashMap<H256, BlockDetails>>,
|
||||||
pending_transaction_addresses: RwLock<HashMap<H256, Option<TransactionAddress>>>,
|
pending_transaction_addresses: RwLock<HashMap<H256, Option<TransactionAddress>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,8 +207,16 @@ impl BlockProvider for BlockChain {
|
|||||||
self.db.exists_with_cache(db::COL_EXTRA, &self.block_details, hash)
|
self.db.exists_with_cache(db::COL_EXTRA, &self.block_details, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_block(&self) -> H256 {
|
fn first_block(&self) -> Option<H256> {
|
||||||
self.first_block
|
self.first_block.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_ancient_block(&self) -> Option<H256> {
|
||||||
|
self.best_ancient_block.read().as_ref().map(|b| b.hash.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_ancient_number(&self) -> Option<BlockNumber> {
|
||||||
|
self.best_ancient_block.read().as_ref().map(|b| b.number)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get raw block data
|
/// Get raw block data
|
||||||
@ -332,7 +356,10 @@ impl BlockProvider for BlockChain {
|
|||||||
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
|
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
|
||||||
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
||||||
assert_eq!(receipts.len(), hashes.len());
|
if receipts.len() != hashes.len() {
|
||||||
|
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
|
||||||
|
assert!(false);
|
||||||
|
}
|
||||||
log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
|
log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
|
||||||
|
|
||||||
let receipts_len = receipts.len();
|
let receipts_len = receipts.len();
|
||||||
@ -368,6 +395,8 @@ impl BlockProvider for BlockChain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An iterator which walks the blockchain towards the genesis.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct AncestryIter<'a> {
|
pub struct AncestryIter<'a> {
|
||||||
current: H256,
|
current: H256,
|
||||||
chain: &'a BlockChain,
|
chain: &'a BlockChain,
|
||||||
@ -377,16 +406,16 @@ impl<'a> Iterator for AncestryIter<'a> {
|
|||||||
type Item = H256;
|
type Item = H256;
|
||||||
fn next(&mut self) -> Option<H256> {
|
fn next(&mut self) -> Option<H256> {
|
||||||
if self.current.is_zero() {
|
if self.current.is_zero() {
|
||||||
Option::None
|
None
|
||||||
} else {
|
} else {
|
||||||
let mut n = self.chain.block_details(&self.current).unwrap().parent;
|
self.chain.block_details(&self.current)
|
||||||
mem::swap(&mut self.current, &mut n);
|
.map(|details| mem::replace(&mut self.current, details.parent))
|
||||||
Some(n)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockChain {
|
impl BlockChain {
|
||||||
|
#[cfg_attr(feature="dev", allow(useless_let_if_seq))]
|
||||||
/// Create new instance of blockchain from given Genesis
|
/// Create new instance of blockchain from given Genesis
|
||||||
pub fn new(config: Config, genesis: &[u8], db: Arc<Database>) -> BlockChain {
|
pub fn new(config: Config, genesis: &[u8], db: Arc<Database>) -> BlockChain {
|
||||||
// 400 is the avarage size of the key
|
// 400 is the avarage size of the key
|
||||||
@ -397,8 +426,9 @@ impl BlockChain {
|
|||||||
levels: LOG_BLOOMS_LEVELS,
|
levels: LOG_BLOOMS_LEVELS,
|
||||||
elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX,
|
elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX,
|
||||||
},
|
},
|
||||||
first_block: H256::zero(),
|
first_block: None,
|
||||||
best_block: RwLock::new(BestBlock::default()),
|
best_block: RwLock::new(BestBlock::default()),
|
||||||
|
best_ancient_block: RwLock::new(None),
|
||||||
block_headers: RwLock::new(HashMap::new()),
|
block_headers: RwLock::new(HashMap::new()),
|
||||||
block_bodies: RwLock::new(HashMap::new()),
|
block_bodies: RwLock::new(HashMap::new()),
|
||||||
block_details: RwLock::new(HashMap::new()),
|
block_details: RwLock::new(HashMap::new()),
|
||||||
@ -410,6 +440,7 @@ impl BlockChain {
|
|||||||
cache_man: Mutex::new(cache_man),
|
cache_man: Mutex::new(cache_man),
|
||||||
pending_best_block: RwLock::new(None),
|
pending_best_block: RwLock::new(None),
|
||||||
pending_block_hashes: RwLock::new(HashMap::new()),
|
pending_block_hashes: RwLock::new(HashMap::new()),
|
||||||
|
pending_block_details: RwLock::new(HashMap::new()),
|
||||||
pending_transaction_addresses: RwLock::new(HashMap::new()),
|
pending_transaction_addresses: RwLock::new(HashMap::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -440,7 +471,6 @@ impl BlockChain {
|
|||||||
batch.write(db::COL_EXTRA, &header.number(), &hash);
|
batch.write(db::COL_EXTRA, &header.number(), &hash);
|
||||||
|
|
||||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
|
||||||
bc.db.write(batch).expect("Low level database error. Some issue with disk?");
|
bc.db.write(batch).expect("Low level database error. Some issue with disk?");
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -452,32 +482,45 @@ impl BlockChain {
|
|||||||
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
|
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
|
||||||
let best_block_rlp = bc.block(&best_block_hash).unwrap();
|
let best_block_rlp = bc.block(&best_block_hash).unwrap();
|
||||||
|
|
||||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map_or(Vec::new(), |v| v.to_vec());
|
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec());
|
||||||
|
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
|
||||||
|
let best_ancient_number;
|
||||||
|
if best_ancient.is_none() && best_block_number > 1 && bc.block_hash(1).is_none() {
|
||||||
|
best_ancient = Some(bc.genesis_hash());
|
||||||
|
best_ancient_number = Some(0);
|
||||||
|
} else {
|
||||||
|
best_ancient_number = best_ancient.as_ref().and_then(|h| bc.block_number(h));
|
||||||
|
}
|
||||||
|
|
||||||
// binary search for the first block.
|
// binary search for the first block.
|
||||||
if raw_first.is_empty() {
|
match raw_first {
|
||||||
let (mut f, mut hash) = (best_block_number, best_block_hash);
|
None => {
|
||||||
let mut l = 0;
|
let (mut f, mut hash) = (best_block_number, best_block_hash);
|
||||||
|
let mut l = best_ancient_number.unwrap_or(0);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if l >= f { break; }
|
if l >= f { break; }
|
||||||
|
|
||||||
let step = (f - l) >> 1;
|
let step = (f - l) >> 1;
|
||||||
let m = l + step;
|
let m = l + step;
|
||||||
|
|
||||||
match bc.block_hash(m) {
|
match bc.block_hash(m) {
|
||||||
Some(h) => { f = m; hash = h },
|
Some(h) => { f = m; hash = h },
|
||||||
None => { l = m + 1 },
|
None => { l = m + 1 },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut batch = db.transaction();
|
if hash != bc.genesis_hash() {
|
||||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
trace!("First block calculated: {:?}", hash);
|
||||||
db.write(batch).expect("Low level database error.");
|
let mut batch = db.transaction();
|
||||||
|
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||||
bc.first_block = hash;
|
db.write(batch).expect("Low level database error.");
|
||||||
} else {
|
bc.first_block = Some(hash);
|
||||||
bc.first_block = H256::from_slice(&raw_first);
|
}
|
||||||
|
},
|
||||||
|
Some(raw_first) => {
|
||||||
|
bc.first_block = Some(H256::from_slice(&raw_first));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// and write them
|
// and write them
|
||||||
@ -488,6 +531,14 @@ impl BlockChain {
|
|||||||
hash: best_block_hash,
|
hash: best_block_hash,
|
||||||
block: best_block_rlp,
|
block: best_block_rlp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) {
|
||||||
|
let mut best_ancient_block = bc.best_ancient_block.write();
|
||||||
|
*best_ancient_block = Some(BestAncientBlock {
|
||||||
|
hash: hash,
|
||||||
|
number: number,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bc
|
bc
|
||||||
@ -517,7 +568,7 @@ impl BlockChain {
|
|||||||
let range = extras.number as bc::Number .. extras.number as bc::Number;
|
let range = extras.number as bc::Number .. extras.number as bc::Number;
|
||||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||||
let changes = chain.replace(&range, vec![]);
|
let changes = chain.replace(&range, vec![]);
|
||||||
for (k, v) in changes.into_iter() {
|
for (k, v) in changes {
|
||||||
batch.write(db::COL_EXTRA, &LogGroupPosition::from(k), &BloomGroup::from(v));
|
batch.write(db::COL_EXTRA, &LogGroupPosition::from(k), &BloomGroup::from(v));
|
||||||
}
|
}
|
||||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||||
@ -641,11 +692,12 @@ impl BlockChain {
|
|||||||
/// Inserts a verified, known block from the canonical chain.
|
/// Inserts a verified, known block from the canonical chain.
|
||||||
///
|
///
|
||||||
/// Can be performed out-of-order, but care must be taken that the final chain is in a correct state.
|
/// Can be performed out-of-order, but care must be taken that the final chain is in a correct state.
|
||||||
/// This is used by snapshot restoration.
|
/// This is used by snapshot restoration and when downloading missing blocks for the chain gap.
|
||||||
///
|
/// `is_best` forces the best block to be updated to this block.
|
||||||
|
/// `is_ancient` forces the best block of the first block sequence to be updated to this block.
|
||||||
/// Supply a dummy parent total difficulty when the parent block may not be in the chain.
|
/// Supply a dummy parent total difficulty when the parent block may not be in the chain.
|
||||||
/// Returns true if the block is disconnected.
|
/// Returns true if the block is disconnected.
|
||||||
pub fn insert_snapshot_block(&self, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool) -> bool {
|
pub fn insert_unordered_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool, is_ancient: bool) -> bool {
|
||||||
let block = BlockView::new(bytes);
|
let block = BlockView::new(bytes);
|
||||||
let header = block.header_view();
|
let header = block.header_view();
|
||||||
let hash = header.sha3();
|
let hash = header.sha3();
|
||||||
@ -656,8 +708,6 @@ impl BlockChain {
|
|||||||
|
|
||||||
assert!(self.pending_best_block.read().is_none());
|
assert!(self.pending_best_block.read().is_none());
|
||||||
|
|
||||||
let mut batch = self.db.transaction();
|
|
||||||
|
|
||||||
let block_rlp = UntrustedRlp::new(bytes);
|
let block_rlp = UntrustedRlp::new(bytes);
|
||||||
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks);
|
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks);
|
||||||
let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks);
|
let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks);
|
||||||
@ -671,13 +721,13 @@ impl BlockChain {
|
|||||||
if let Some(parent_details) = maybe_parent {
|
if let Some(parent_details) = maybe_parent {
|
||||||
// parent known to be in chain.
|
// parent known to be in chain.
|
||||||
let info = BlockInfo {
|
let info = BlockInfo {
|
||||||
hash: hash,
|
hash: hash.clone(),
|
||||||
number: header.number(),
|
number: header.number(),
|
||||||
total_difficulty: parent_details.total_difficulty + header.difficulty(),
|
total_difficulty: parent_details.total_difficulty + header.difficulty(),
|
||||||
location: BlockLocation::CanonChain,
|
location: BlockLocation::CanonChain,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
self.prepare_update(batch, ExtrasUpdate {
|
||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: self.prepare_block_details_update(bytes, &info),
|
block_details: self.prepare_block_details_update(bytes, &info),
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
@ -686,7 +736,21 @@ impl BlockChain {
|
|||||||
info: info,
|
info: info,
|
||||||
block: bytes
|
block: bytes
|
||||||
}, is_best);
|
}, is_best);
|
||||||
self.db.write(batch).unwrap();
|
|
||||||
|
if is_ancient {
|
||||||
|
let mut best_ancient_block = self.best_ancient_block.write();
|
||||||
|
let ancient_number = best_ancient_block.as_ref().map_or(0, |b| b.number);
|
||||||
|
if self.block_hash(header.number() + 1).is_some() {
|
||||||
|
batch.delete(db::COL_EXTRA, b"ancient");
|
||||||
|
*best_ancient_block = None;
|
||||||
|
} else if header.number() > ancient_number {
|
||||||
|
batch.put(db::COL_EXTRA, b"ancient", &hash);
|
||||||
|
*best_ancient_block = Some(BestAncientBlock {
|
||||||
|
hash: hash,
|
||||||
|
number: header.number(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@ -711,7 +775,7 @@ impl BlockChain {
|
|||||||
let mut update = HashMap::new();
|
let mut update = HashMap::new();
|
||||||
update.insert(hash, block_details);
|
update.insert(hash, block_details);
|
||||||
|
|
||||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
self.prepare_update(batch, ExtrasUpdate {
|
||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: update,
|
block_details: update,
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
@ -720,8 +784,6 @@ impl BlockChain {
|
|||||||
info: info,
|
info: info,
|
||||||
block: bytes,
|
block: bytes,
|
||||||
}, is_best);
|
}, is_best);
|
||||||
self.db.write(batch).unwrap();
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -730,11 +792,10 @@ impl BlockChain {
|
|||||||
/// the chain and the child's parent is this block.
|
/// the chain and the child's parent is this block.
|
||||||
///
|
///
|
||||||
/// Used in snapshots to glue the chunks together at the end.
|
/// Used in snapshots to glue the chunks together at the end.
|
||||||
pub fn add_child(&self, block_hash: H256, child_hash: H256) {
|
pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) {
|
||||||
let mut parent_details = self.block_details(&block_hash)
|
let mut parent_details = self.block_details(&block_hash)
|
||||||
.unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash));
|
.unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash));
|
||||||
|
|
||||||
let mut batch = self.db.transaction();
|
|
||||||
parent_details.children.push(child_hash);
|
parent_details.children.push(child_hash);
|
||||||
|
|
||||||
let mut update = HashMap::new();
|
let mut update = HashMap::new();
|
||||||
@ -745,8 +806,6 @@ impl BlockChain {
|
|||||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite);
|
||||||
|
|
||||||
self.cache_man.lock().note_used(CacheID::BlockDetails(block_hash));
|
self.cache_man.lock().note_used(CacheID::BlockDetails(block_hash));
|
||||||
|
|
||||||
self.db.write(batch).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(similar_names))]
|
#[cfg_attr(feature="dev", allow(similar_names))]
|
||||||
@ -835,17 +894,6 @@ impl BlockChain {
|
|||||||
|
|
||||||
/// Prepares extras update.
|
/// Prepares extras update.
|
||||||
fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
||||||
{
|
|
||||||
let block_hashes: Vec<_> = update.block_details.keys().cloned().collect();
|
|
||||||
|
|
||||||
let mut write_details = self.block_details.write();
|
|
||||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
|
|
||||||
|
|
||||||
let mut cache_man = self.cache_man.lock();
|
|
||||||
for hash in block_hashes {
|
|
||||||
cache_man.note_used(CacheID::BlockDetails(hash));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut write_receipts = self.block_receipts.write();
|
let mut write_receipts = self.block_receipts.write();
|
||||||
@ -857,7 +905,7 @@ impl BlockChain {
|
|||||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_blocks_blooms, update.blocks_blooms, CacheUpdatePolicy::Remove);
|
batch.extend_with_cache(db::COL_EXTRA, &mut *write_blocks_blooms, update.blocks_blooms, CacheUpdatePolicy::Remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
// These cached values must be updated last with all three locks taken to avoid
|
// These cached values must be updated last with all four locks taken to avoid
|
||||||
// cache decoherence
|
// cache decoherence
|
||||||
{
|
{
|
||||||
let mut best_block = self.pending_best_block.write();
|
let mut best_block = self.pending_best_block.write();
|
||||||
@ -875,8 +923,10 @@ impl BlockChain {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
let mut write_hashes = self.pending_block_hashes.write();
|
let mut write_hashes = self.pending_block_hashes.write();
|
||||||
|
let mut write_details = self.pending_block_details.write();
|
||||||
let mut write_txs = self.pending_transaction_addresses.write();
|
let mut write_txs = self.pending_transaction_addresses.write();
|
||||||
|
|
||||||
|
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
|
||||||
batch.extend_with_cache(db::COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_cache(db::COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
|
||||||
batch.extend_with_option_cache(db::COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_option_cache(db::COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
|
||||||
}
|
}
|
||||||
@ -886,9 +936,11 @@ impl BlockChain {
|
|||||||
pub fn commit(&self) {
|
pub fn commit(&self) {
|
||||||
let mut pending_best_block = self.pending_best_block.write();
|
let mut pending_best_block = self.pending_best_block.write();
|
||||||
let mut pending_write_hashes = self.pending_block_hashes.write();
|
let mut pending_write_hashes = self.pending_block_hashes.write();
|
||||||
|
let mut pending_block_details = self.pending_block_details.write();
|
||||||
let mut pending_write_txs = self.pending_transaction_addresses.write();
|
let mut pending_write_txs = self.pending_transaction_addresses.write();
|
||||||
|
|
||||||
let mut best_block = self.best_block.write();
|
let mut best_block = self.best_block.write();
|
||||||
|
let mut write_block_details = self.block_details.write();
|
||||||
let mut write_hashes = self.block_hashes.write();
|
let mut write_hashes = self.block_hashes.write();
|
||||||
let mut write_txs = self.transaction_addresses.write();
|
let mut write_txs = self.transaction_addresses.write();
|
||||||
// update best block
|
// update best block
|
||||||
@ -901,9 +953,11 @@ impl BlockChain {
|
|||||||
|
|
||||||
let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect();
|
let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect();
|
||||||
let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect();
|
let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect();
|
||||||
|
let pending_block_hashes: Vec<_> = pending_block_details.keys().cloned().collect();
|
||||||
|
|
||||||
write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new()));
|
write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new()));
|
||||||
write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed"))));
|
write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed"))));
|
||||||
|
write_block_details.extend(mem::replace(&mut *pending_block_details, HashMap::new()));
|
||||||
|
|
||||||
for hash in retracted_txs.keys() {
|
for hash in retracted_txs.keys() {
|
||||||
write_txs.remove(hash);
|
write_txs.remove(hash);
|
||||||
@ -917,6 +971,10 @@ impl BlockChain {
|
|||||||
for hash in enacted_txs_keys {
|
for hash in enacted_txs_keys {
|
||||||
cache_man.note_used(CacheID::TransactionAddresses(hash));
|
cache_man.note_used(CacheID::TransactionAddresses(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for hash in pending_block_hashes {
|
||||||
|
cache_man.note_used(CacheID::BlockDetails(hash));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
|
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
|
||||||
@ -941,17 +999,29 @@ impl BlockChain {
|
|||||||
if !self.is_known(parent) { return None; }
|
if !self.is_known(parent) { return None; }
|
||||||
|
|
||||||
let mut excluded = HashSet::new();
|
let mut excluded = HashSet::new();
|
||||||
for a in self.ancestry_iter(parent.clone()).unwrap().take(uncle_generations) {
|
let ancestry = match self.ancestry_iter(parent.clone()) {
|
||||||
excluded.extend(self.uncle_hashes(&a).unwrap().into_iter());
|
Some(iter) => iter,
|
||||||
excluded.insert(a);
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
for a in ancestry.clone().take(uncle_generations) {
|
||||||
|
if let Some(uncles) = self.uncle_hashes(&a) {
|
||||||
|
excluded.extend(uncles);
|
||||||
|
excluded.insert(a);
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
for a in self.ancestry_iter(parent.clone()).unwrap().skip(1).take(uncle_generations) {
|
for a in ancestry.skip(1).take(uncle_generations) {
|
||||||
ret.extend(self.block_details(&a).unwrap().children.iter()
|
if let Some(details) = self.block_details(&a) {
|
||||||
.filter(|h| !excluded.contains(h))
|
ret.extend(details.children.iter().filter(|h| !excluded.contains(h)))
|
||||||
);
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ret)
|
Some(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1207,6 +1277,29 @@ impl BlockChain {
|
|||||||
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
||||||
body.out()
|
body.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns general blockchain information
|
||||||
|
pub fn chain_info(&self) -> BlockChainInfo {
|
||||||
|
// ensure data consistencly by locking everything first
|
||||||
|
let best_block = self.best_block.read();
|
||||||
|
let best_ancient_block = self.best_ancient_block.read();
|
||||||
|
BlockChainInfo {
|
||||||
|
total_difficulty: best_block.total_difficulty.clone(),
|
||||||
|
pending_total_difficulty: best_block.total_difficulty.clone(),
|
||||||
|
genesis_hash: self.genesis_hash(),
|
||||||
|
best_block_hash: best_block.hash.clone(),
|
||||||
|
best_block_number: best_block.number,
|
||||||
|
first_block_hash: self.first_block(),
|
||||||
|
first_block_number: From::from(self.first_block_number()),
|
||||||
|
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()),
|
||||||
|
ancient_block_number: best_ancient_block.as_ref().map(|b| b.number),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn db(&self) -> &Arc<Database> {
|
||||||
|
&self.db
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -1375,7 +1468,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
|
||||||
@ -1439,7 +1532,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(),
|
||||||
@ -1448,7 +1541,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(),
|
||||||
@ -1457,7 +1550,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())
|
||||||
@ -1763,7 +1856,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(),
|
||||||
@ -1771,7 +1864,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(),
|
||||||
@ -1779,7 +1872,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();
|
||||||
|
@ -55,18 +55,23 @@ impl<T> CacheManager<T> where T: Eq + Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..COLLECTION_QUEUE_SIZE {
|
for _ in 0..COLLECTION_QUEUE_SIZE {
|
||||||
let current_size = notify_unused(self.cache_usage.pop_back().unwrap());
|
if let Some(back) = self.cache_usage.pop_back() {
|
||||||
self.cache_usage.push_front(Default::default());
|
let current_size = notify_unused(back);
|
||||||
if current_size < self.max_cache_size {
|
self.cache_usage.push_front(Default::default());
|
||||||
break;
|
if current_size < self.max_cache_size {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_cache_if_needed(&mut self) {
|
fn rotate_cache_if_needed(&mut self) {
|
||||||
|
if self.cache_usage.is_empty() { return }
|
||||||
|
|
||||||
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
|
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
|
||||||
let cache = self.cache_usage.pop_back().unwrap();
|
if let Some(cache) = self.cache_usage.pop_back() {
|
||||||
self.cache_usage.push_front(cache);
|
self.cache_usage.push_front(cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ use ipc::IpcConfig;
|
|||||||
use util::H256;
|
use util::H256;
|
||||||
|
|
||||||
/// Represents what has to be handled by actor listening to chain events
|
/// Represents what has to be handled by actor listening to chain events
|
||||||
#[derive(Ipc)]
|
#[ipc]
|
||||||
pub trait ChainNotify : Send + Sync {
|
pub trait ChainNotify : Send + Sync {
|
||||||
/// fires when chain has new blocks.
|
/// fires when chain has new blocks.
|
||||||
fn new_blocks(&self,
|
fn new_blocks(&self,
|
||||||
|
@ -30,10 +30,10 @@ use util::kvdb::*;
|
|||||||
|
|
||||||
// other
|
// other
|
||||||
use io::*;
|
use io::*;
|
||||||
use views::{HeaderView, BodyView};
|
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;
|
||||||
@ -45,8 +45,9 @@ use block::*;
|
|||||||
use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
||||||
use blockchain::extras::TransactionAddress;
|
use blockchain::extras::TransactionAddress;
|
||||||
use types::filter::Filter;
|
use types::filter::Filter;
|
||||||
|
use types::mode::Mode as IpcMode;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use verification::queue::{BlockQueue, QueueInfo as BlockQueueInfo};
|
use verification::queue::BlockQueue;
|
||||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||||
use client::{
|
use client::{
|
||||||
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
|
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
|
||||||
@ -60,20 +61,23 @@ use receipt::LocalizedReceipt;
|
|||||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||||
use trace;
|
use trace;
|
||||||
use trace::FlatTransactionTraces;
|
use trace::FlatTransactionTraces;
|
||||||
use evm::Factory as EvmFactory;
|
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;
|
||||||
|
|
||||||
// re-export
|
// re-export
|
||||||
pub use types::blockchain_info::BlockChainInfo;
|
pub use types::blockchain_info::BlockChainInfo;
|
||||||
pub use types::block_status::BlockStatus;
|
pub use types::block_status::BlockStatus;
|
||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
|
pub use verification::queue::QueueInfo as BlockQueueInfo;
|
||||||
|
|
||||||
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
||||||
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
||||||
|
const MIN_HISTORY_SIZE: u64 = 8;
|
||||||
|
|
||||||
impl fmt::Display for BlockChainInfo {
|
impl fmt::Display for BlockChainInfo {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@ -120,7 +124,7 @@ impl SleepState {
|
|||||||
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
||||||
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
mode: Mode,
|
mode: Mutex<Mode>,
|
||||||
chain: RwLock<Arc<BlockChain>>,
|
chain: RwLock<Arc<BlockChain>>,
|
||||||
tracedb: RwLock<TraceDB<BlockChain>>,
|
tracedb: RwLock<TraceDB<BlockChain>>,
|
||||||
engine: Arc<Engine>,
|
engine: Arc<Engine>,
|
||||||
@ -136,22 +140,14 @@ pub struct Client {
|
|||||||
miner: Arc<Miner>,
|
miner: Arc<Miner>,
|
||||||
sleep_state: Mutex<SleepState>,
|
sleep_state: Mutex<SleepState>,
|
||||||
liveness: AtomicBool,
|
liveness: AtomicBool,
|
||||||
io_channel: IoChannel<ClientIoMessage>,
|
io_channel: Mutex<IoChannel<ClientIoMessage>>,
|
||||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||||
queue_transactions: AtomicUsize,
|
queue_transactions: AtomicUsize,
|
||||||
last_hashes: RwLock<VecDeque<H256>>,
|
last_hashes: RwLock<VecDeque<H256>>,
|
||||||
factories: Factories,
|
factories: Factories,
|
||||||
}
|
history: u64,
|
||||||
|
rng: Mutex<OsRng>,
|
||||||
/// The pruning constant -- how old blocks must be before we
|
on_mode_change: Mutex<Option<Box<FnMut(&Mode) + 'static + Send>>>,
|
||||||
/// assume finality of a given candidate.
|
|
||||||
pub const HISTORY: u64 = 1200;
|
|
||||||
|
|
||||||
/// Append a path element to the given path and return the string.
|
|
||||||
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {
|
|
||||||
let mut p = path.as_ref().to_path_buf();
|
|
||||||
p.push(item);
|
|
||||||
p.to_str().unwrap().to_owned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@ -167,7 +163,7 @@ impl Client {
|
|||||||
let path = path.to_path_buf();
|
let path = path.to_path_buf();
|
||||||
let gb = spec.genesis_block();
|
let gb = spec.genesis_block();
|
||||||
|
|
||||||
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
|
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().expect("DB path could not be converted to string.")).map_err(ClientError::Database)));
|
||||||
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone()));
|
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone()));
|
||||||
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
|
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
|
||||||
|
|
||||||
@ -177,27 +173,49 @@ impl Client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
||||||
let mut state_db = StateDB::new(journal_db);
|
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
|
||||||
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
||||||
let mut batch = DBTransaction::new(&db);
|
let mut batch = DBTransaction::new(&db);
|
||||||
try!(state_db.commit(&mut batch, 0, &spec.genesis_header().hash(), None));
|
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
||||||
try!(db.write(batch).map_err(ClientError::Database));
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());
|
||||||
|
|
||||||
|
let history = if config.history < MIN_HISTORY_SIZE {
|
||||||
|
info!(target: "client", "Ignoring pruning history parameter of {}\
|
||||||
|
, falling back to minimum of {}",
|
||||||
|
config.history, MIN_HISTORY_SIZE);
|
||||||
|
MIN_HISTORY_SIZE
|
||||||
|
} else {
|
||||||
|
config.history
|
||||||
|
};
|
||||||
|
|
||||||
|
if let (Some(earliest), Some(latest)) = (state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()) {
|
||||||
|
if latest > earliest && latest - earliest > history {
|
||||||
|
for era in earliest..(latest - history + 1) {
|
||||||
|
trace!("Removing era {}", era);
|
||||||
|
let mut batch = DBTransaction::new(&db);
|
||||||
|
try!(state_db.mark_canonical(&mut batch, era, &chain.block_hash(era).expect("Old block not found in the database")));
|
||||||
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
||||||
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
||||||
}
|
}
|
||||||
|
|
||||||
let engine = spec.engine.clone();
|
let engine = spec.engine.clone();
|
||||||
|
|
||||||
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone());
|
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal());
|
||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
panic_handler.forward_from(&block_queue);
|
panic_handler.forward_from(&block_queue);
|
||||||
|
|
||||||
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()),
|
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||||
trie: TrieFactory::new(trie_spec),
|
trie: TrieFactory::new(trie_spec),
|
||||||
accountdb: Default::default(),
|
accountdb: Default::default(),
|
||||||
};
|
};
|
||||||
@ -205,7 +223,7 @@ impl Client {
|
|||||||
let client = Client {
|
let client = Client {
|
||||||
sleep_state: Mutex::new(SleepState::new(awake)),
|
sleep_state: Mutex::new(SleepState::new(awake)),
|
||||||
liveness: AtomicBool::new(awake),
|
liveness: AtomicBool::new(awake),
|
||||||
mode: config.mode.clone(),
|
mode: Mutex::new(config.mode.clone()),
|
||||||
chain: RwLock::new(chain),
|
chain: RwLock::new(chain),
|
||||||
tracedb: tracedb,
|
tracedb: tracedb,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
@ -219,11 +237,14 @@ impl Client {
|
|||||||
import_lock: Mutex::new(()),
|
import_lock: Mutex::new(()),
|
||||||
panic_handler: panic_handler,
|
panic_handler: panic_handler,
|
||||||
miner: miner,
|
miner: miner,
|
||||||
io_channel: message_channel,
|
io_channel: Mutex::new(message_channel),
|
||||||
notify: RwLock::new(Vec::new()),
|
notify: RwLock::new(Vec::new()),
|
||||||
queue_transactions: AtomicUsize::new(0),
|
queue_transactions: AtomicUsize::new(0),
|
||||||
last_hashes: RwLock::new(VecDeque::new()),
|
last_hashes: RwLock::new(VecDeque::new()),
|
||||||
factories: factories,
|
factories: factories,
|
||||||
|
history: history,
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
@ -241,6 +262,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();
|
||||||
@ -249,6 +275,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();
|
||||||
@ -282,7 +324,7 @@ impl Client {
|
|||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
// Check the block isn't so old we won't be able to enact it.
|
// Check the block isn't so old we won't be able to enact it.
|
||||||
let best_block_number = chain.best_block_number();
|
let best_block_number = chain.best_block_number();
|
||||||
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
if best_block_number >= self.history && header.number() <= best_block_number - self.history {
|
||||||
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
@ -296,31 +338,27 @@ impl Client {
|
|||||||
|
|
||||||
// Check if Parent is in chain
|
// Check if Parent is in chain
|
||||||
let chain_has_parent = chain.block_header(header.parent_hash());
|
let chain_has_parent = chain.block_header(header.parent_hash());
|
||||||
if let None = chain_has_parent {
|
if let Some(parent) = chain_has_parent {
|
||||||
|
// Enact Verified Block
|
||||||
|
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
||||||
|
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
|
||||||
|
|
||||||
|
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||||
|
let locked_block = try!(enact_result.map_err(|e| {
|
||||||
|
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Final Verification
|
||||||
|
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
||||||
|
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(locked_block)
|
||||||
|
} else {
|
||||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
|
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
|
||||||
return Err(());
|
Err(())
|
||||||
};
|
|
||||||
|
|
||||||
// Enact Verified Block
|
|
||||||
let parent = chain_has_parent.unwrap();
|
|
||||||
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
|
||||||
let is_canon = header.parent_hash() == &chain.best_block_hash();
|
|
||||||
let db = if is_canon { self.state_db.lock().boxed_clone_canon() } else { self.state_db.lock().boxed_clone() };
|
|
||||||
|
|
||||||
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
|
||||||
if let Err(e) = enact_result {
|
|
||||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
|
||||||
return Err(());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Final Verification
|
|
||||||
let locked_block = enact_result.unwrap();
|
|
||||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
|
||||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(locked_block)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
|
fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
|
||||||
@ -351,58 +389,55 @@ impl Client {
|
|||||||
|
|
||||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||||
pub fn import_verified_blocks(&self) -> usize {
|
pub fn import_verified_blocks(&self) -> usize {
|
||||||
let max_blocks_to_import = 64;
|
let max_blocks_to_import = 4;
|
||||||
let (imported_blocks, import_results, invalid_blocks, imported, duration) = {
|
let (imported_blocks, import_results, invalid_blocks, imported, duration, is_empty) = {
|
||||||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||||
let mut invalid_blocks = HashSet::new();
|
let mut invalid_blocks = HashSet::new();
|
||||||
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
||||||
|
|
||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
|
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||||
|
if blocks.is_empty() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let _timer = PerfTimer::new("import_verified_blocks");
|
let _timer = PerfTimer::new("import_verified_blocks");
|
||||||
let start = precise_time_ns();
|
let start = precise_time_ns();
|
||||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
if invalid_blocks.contains(header.parent_hash()) {
|
let is_invalid = invalid_blocks.contains(header.parent_hash());
|
||||||
|
if is_invalid {
|
||||||
invalid_blocks.insert(header.hash());
|
invalid_blocks.insert(header.hash());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let closed_block = self.check_and_close_block(&block);
|
if let Ok(closed_block) = self.check_and_close_block(&block) {
|
||||||
if let Err(_) = closed_block {
|
imported_blocks.push(header.hash());
|
||||||
|
|
||||||
|
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
|
||||||
|
import_results.push(route);
|
||||||
|
|
||||||
|
self.report.write().accrue_block(&block);
|
||||||
|
} else {
|
||||||
invalid_blocks.insert(header.hash());
|
invalid_blocks.insert(header.hash());
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let closed_block = closed_block.unwrap();
|
|
||||||
imported_blocks.push(header.hash());
|
|
||||||
|
|
||||||
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
|
|
||||||
import_results.push(route);
|
|
||||||
|
|
||||||
self.report.write().accrue_block(&block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let imported = imported_blocks.len();
|
let imported = imported_blocks.len();
|
||||||
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
||||||
|
|
||||||
{
|
if !invalid_blocks.is_empty() {
|
||||||
if !invalid_blocks.is_empty() {
|
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
|
||||||
}
|
|
||||||
if !imported_blocks.is_empty() {
|
|
||||||
self.block_queue.mark_as_good(&imported_blocks);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
|
||||||
let duration_ns = precise_time_ns() - start;
|
let duration_ns = precise_time_ns() - start;
|
||||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns)
|
(imported_blocks, import_results, invalid_blocks, imported, duration_ns, is_empty)
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
if !imported_blocks.is_empty() && is_empty {
|
||||||
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
|
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
|
||||||
|
|
||||||
if self.queue_info().is_empty() {
|
if is_empty {
|
||||||
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,17 +458,45 @@ impl Client {
|
|||||||
imported
|
imported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import a block with transaction receipts.
|
||||||
|
/// The block is guaranteed to be the next best blocks in the first block sequence.
|
||||||
|
/// Does no sealing or transaction validation.
|
||||||
|
fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, ::error::Error> {
|
||||||
|
let block = BlockView::new(&block_bytes);
|
||||||
|
let header = block.header();
|
||||||
|
let hash = header.hash();
|
||||||
|
let _import_lock = self.import_lock.lock();
|
||||||
|
{
|
||||||
|
let _timer = PerfTimer::new("import_old_block");
|
||||||
|
let mut rng = self.rng.lock();
|
||||||
|
let chain = self.chain.read();
|
||||||
|
|
||||||
|
// verify block.
|
||||||
|
try!(::snapshot::verify_old_block(
|
||||||
|
&mut *rng,
|
||||||
|
&header,
|
||||||
|
&*self.engine,
|
||||||
|
&*chain,
|
||||||
|
Some(&block_bytes),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Commit results
|
||||||
|
let receipts = ::rlp::decode(&receipts_bytes);
|
||||||
|
let mut batch = DBTransaction::new(&self.db.read());
|
||||||
|
chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true);
|
||||||
|
// Final commit to the DB
|
||||||
|
self.db.read().write_buffered(batch);
|
||||||
|
chain.commit();
|
||||||
|
}
|
||||||
|
self.db.read().flush().expect("DB flush failed.");
|
||||||
|
Ok(hash)
|
||||||
|
}
|
||||||
|
|
||||||
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
||||||
let number = block.header().number();
|
let number = block.header().number();
|
||||||
let parent = block.header().parent_hash().clone();
|
let parent = block.header().parent_hash().clone();
|
||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
// Are we committing an era?
|
|
||||||
let ancient = if number >= HISTORY {
|
|
||||||
let n = number - HISTORY;
|
|
||||||
Some((n, chain.block_hash(n).unwrap()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Commit results
|
// Commit results
|
||||||
let receipts = block.receipts().to_owned();
|
let receipts = block.receipts().to_owned();
|
||||||
@ -449,7 +512,17 @@ impl Client {
|
|||||||
// already-imported block of the same number.
|
// already-imported block of the same number.
|
||||||
// TODO: Prove it with a test.
|
// TODO: Prove it with a test.
|
||||||
let mut state = block.drain();
|
let mut state = block.drain();
|
||||||
state.commit(&mut batch, number, hash, ancient).expect("DB commit failed.");
|
|
||||||
|
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
|
||||||
|
|
||||||
|
if number >= self.history {
|
||||||
|
let n = number - self.history;
|
||||||
|
if let Some(ancient_hash) = chain.block_hash(n) {
|
||||||
|
state.mark_canonical(&mut batch, n, &ancient_hash).expect("DB commit failed");
|
||||||
|
} else {
|
||||||
|
debug!(target: "client", "Missing expected hash for block {}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let route = chain.insert_block(&mut batch, block_data, receipts);
|
let route = chain.insert_block(&mut batch, block_data, receipts);
|
||||||
self.tracedb.read().import(&mut batch, TraceImportRequest {
|
self.tracedb.read().import(&mut batch, TraceImportRequest {
|
||||||
@ -459,6 +532,9 @@ impl Client {
|
|||||||
enacted: route.enacted.clone(),
|
enacted: route.enacted.clone(),
|
||||||
retracted: route.retracted.len()
|
retracted: route.retracted.len()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
|
||||||
|
state.sync_cache(&route.enacted, &route.retracted, is_canon);
|
||||||
// Final commit to the DB
|
// Final commit to the DB
|
||||||
self.db.read().write_buffered(batch);
|
self.db.read().write_buffered(batch);
|
||||||
chain.commit();
|
chain.commit();
|
||||||
@ -478,6 +554,7 @@ impl Client {
|
|||||||
|
|
||||||
/// Import transactions from the IO queue
|
/// Import transactions from the IO queue
|
||||||
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
|
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
|
||||||
|
trace!(target: "external_tx", "Importing queued");
|
||||||
let _timer = PerfTimer::new("import_queued_transactions");
|
let _timer = PerfTimer::new("import_queued_transactions");
|
||||||
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
|
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
|
||||||
let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok()).collect();
|
let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok()).collect();
|
||||||
@ -485,6 +562,11 @@ impl Client {
|
|||||||
results.len()
|
results.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used by PoA to try sealing on period change.
|
||||||
|
pub fn update_sealing(&self) {
|
||||||
|
self.miner.update_sealing(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to get a copy of a specific block's final state.
|
/// Attempt to get a copy of a specific block's final state.
|
||||||
///
|
///
|
||||||
/// This will not fail if given BlockID::Latest.
|
/// This will not fail if given BlockID::Latest.
|
||||||
@ -506,7 +588,7 @@ impl Client {
|
|||||||
let db = self.state_db.lock().boxed_clone();
|
let db = self.state_db.lock().boxed_clone();
|
||||||
|
|
||||||
// early exit for pruned blocks
|
// early exit for pruned blocks
|
||||||
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + HISTORY {
|
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + self.history {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,9 +615,11 @@ impl Client {
|
|||||||
|
|
||||||
/// Get a copy of the best block's state.
|
/// Get a copy of the best block's state.
|
||||||
pub fn state(&self) -> State {
|
pub fn state(&self) -> State {
|
||||||
|
let header = self.best_block_header();
|
||||||
|
let header = HeaderView::new(&header);
|
||||||
State::from_existing(
|
State::from_existing(
|
||||||
self.state_db.lock().boxed_clone(),
|
self.state_db.lock().boxed_clone_canon(&header.hash()),
|
||||||
HeaderView::new(&self.best_block_header()).state_root(),
|
header.state_root(),
|
||||||
self.engine.account_start_nonce(),
|
self.engine.account_start_nonce(),
|
||||||
self.factories.clone())
|
self.factories.clone())
|
||||||
.expect("State root of best block header always valid.")
|
.expect("State root of best block header always valid.")
|
||||||
@ -560,7 +644,8 @@ impl Client {
|
|||||||
self.block_queue.collect_garbage();
|
self.block_queue.collect_garbage();
|
||||||
self.tracedb.read().collect_garbage();
|
self.tracedb.read().collect_garbage();
|
||||||
|
|
||||||
match self.mode {
|
let mode = self.mode.lock().clone();
|
||||||
|
match mode {
|
||||||
Mode::Dark(timeout) => {
|
Mode::Dark(timeout) => {
|
||||||
let mut ss = self.sleep_state.lock();
|
let mut ss = self.sleep_state.lock();
|
||||||
if let Some(t) = ss.last_activity {
|
if let Some(t) = ss.last_activity {
|
||||||
@ -609,20 +694,23 @@ impl Client {
|
|||||||
let best_block_number = self.chain_info().best_block_number;
|
let best_block_number = self.chain_info().best_block_number;
|
||||||
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
||||||
|
|
||||||
if best_block_number > HISTORY + block_number && db.is_pruned() {
|
if best_block_number > self.history + block_number && db.is_pruned() {
|
||||||
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let history = ::std::cmp::min(self.history, 1000);
|
||||||
|
|
||||||
let start_hash = match at {
|
let start_hash = match at {
|
||||||
BlockID::Latest => {
|
BlockID::Latest => {
|
||||||
let start_num = if best_block_number > 1000 {
|
let start_num = match db.earliest_era() {
|
||||||
best_block_number - 1000
|
Some(era) => ::std::cmp::max(era, best_block_number - history),
|
||||||
} else {
|
None => best_block_number - history,
|
||||||
0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.block_hash(BlockID::Number(start_num))
|
match self.block_hash(BlockID::Number(start_num)) {
|
||||||
.expect("blocks within HISTORY are always stored.")
|
Some(h) => h,
|
||||||
|
None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => match self.block_hash(at) {
|
_ => match self.block_hash(at) {
|
||||||
Some(hash) => hash,
|
Some(hash) => hash,
|
||||||
@ -635,6 +723,11 @@ impl Client {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask the client what the history parameter is.
|
||||||
|
pub fn pruning_history(&self) -> u64 {
|
||||||
|
self.history
|
||||||
|
}
|
||||||
|
|
||||||
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
|
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
|
||||||
match id {
|
match id {
|
||||||
BlockID::Hash(hash) => Some(hash),
|
BlockID::Hash(hash) => Some(hash),
|
||||||
@ -691,7 +784,8 @@ impl snapshot::DatabaseRestore for Client {
|
|||||||
let db = self.db.write();
|
let db = self.db.write();
|
||||||
try!(db.restore(new_db));
|
try!(db.restore(new_db));
|
||||||
|
|
||||||
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE));
|
let cache_size = state_db.cache_size();
|
||||||
|
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
|
||||||
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
|
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
|
||||||
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
|
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -703,7 +797,7 @@ impl BlockChainClient for Client {
|
|||||||
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||||
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
|
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
|
||||||
let view = HeaderView::new(&header);
|
let view = HeaderView::new(&header);
|
||||||
let last_hashes = self.build_last_hashes(view.hash());
|
let last_hashes = self.build_last_hashes(view.parent_hash());
|
||||||
let env_info = EnvInfo {
|
let env_info = EnvInfo {
|
||||||
number: view.number(),
|
number: view.number(),
|
||||||
author: view.author(),
|
author: view.author(),
|
||||||
@ -725,7 +819,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));
|
||||||
@ -775,12 +869,43 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn keep_alive(&self) {
|
fn keep_alive(&self) {
|
||||||
if self.mode != Mode::Active {
|
let should_wake = match &*self.mode.lock() {
|
||||||
|
&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 {
|
||||||
|
let r = self.mode.lock().clone().into();
|
||||||
|
trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mode(&self, new_mode: IpcMode) {
|
||||||
|
trace!(target: "mode", "Client::set_mode({:?})", new_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::Off => self.sleep(),
|
||||||
|
_ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn best_block_header(&self) -> Bytes {
|
fn best_block_header(&self) -> Bytes {
|
||||||
self.chain.read().best_block_header()
|
self.chain.read().best_block_header()
|
||||||
}
|
}
|
||||||
@ -897,8 +1022,10 @@ impl BlockChainClient for Client {
|
|||||||
BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
|
BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
|
||||||
});
|
});
|
||||||
|
|
||||||
match (t, chain.transaction_receipt(&address)) {
|
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
|
||||||
(Some(tx), Some(receipt)) => {
|
|
||||||
|
match (tx_and_sender, chain.transaction_receipt(&address)) {
|
||||||
|
(Some((tx, sender)), Some(receipt)) => {
|
||||||
let block_hash = tx.block_hash.clone();
|
let block_hash = tx.block_hash.clone();
|
||||||
let block_number = tx.block_number.clone();
|
let block_number = tx.block_number.clone();
|
||||||
let transaction_hash = tx.hash();
|
let transaction_hash = tx.hash();
|
||||||
@ -920,7 +1047,7 @@ impl BlockChainClient for Client {
|
|||||||
gas_used: receipt.gas_used - prior_gas_used,
|
gas_used: receipt.gas_used - prior_gas_used,
|
||||||
contract_address: match tx.action {
|
contract_address: match tx.action {
|
||||||
Action::Call(_) => None,
|
Action::Call(_) => None,
|
||||||
Action::Create => Some(contract_address(&tx.sender().unwrap(), &tx.nonce))
|
Action::Create => Some(contract_address(&sender, &tx.nonce))
|
||||||
},
|
},
|
||||||
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
||||||
entry: log,
|
entry: log,
|
||||||
@ -929,7 +1056,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
|
||||||
@ -975,6 +1104,20 @@ impl BlockChainClient for Client {
|
|||||||
Ok(try!(self.block_queue.import(unverified)))
|
Ok(try!(self.block_queue.import(unverified)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||||
|
{
|
||||||
|
// check block order
|
||||||
|
let header = BlockView::new(&block_bytes).header_view();
|
||||||
|
if self.chain.read().is_known(&header.hash()) {
|
||||||
|
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
|
||||||
|
}
|
||||||
|
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
||||||
|
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.import_old_block(block_bytes, receipts_bytes).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
fn queue_info(&self) -> BlockQueueInfo {
|
fn queue_info(&self) -> BlockQueueInfo {
|
||||||
self.block_queue.queue_info()
|
self.block_queue.queue_info()
|
||||||
}
|
}
|
||||||
@ -984,14 +1127,7 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
let chain = self.chain.read();
|
self.chain.read().chain_info()
|
||||||
BlockChainInfo {
|
|
||||||
total_difficulty: chain.best_block_total_difficulty(),
|
|
||||||
pending_total_difficulty: chain.best_block_total_difficulty(),
|
|
||||||
genesis_hash: chain.genesis_hash(),
|
|
||||||
best_block_hash: chain.best_block_hash(),
|
|
||||||
best_block_number: From::from(chain.best_block_number())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn additional_params(&self) -> BTreeMap<String, String> {
|
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||||
@ -1021,17 +1157,18 @@ impl BlockChainClient for Client {
|
|||||||
let start = self.block_number(filter.range.start);
|
let start = self.block_number(filter.range.start);
|
||||||
let end = self.block_number(filter.range.end);
|
let end = self.block_number(filter.range.end);
|
||||||
|
|
||||||
if start.is_some() && end.is_some() {
|
match (start, end) {
|
||||||
let filter = trace::Filter {
|
(Some(s), Some(e)) => {
|
||||||
range: start.unwrap() as usize..end.unwrap() as usize,
|
let filter = trace::Filter {
|
||||||
from_address: From::from(filter.from_address),
|
range: s as usize..e as usize,
|
||||||
to_address: From::from(filter.to_address),
|
from_address: From::from(filter.from_address),
|
||||||
};
|
to_address: From::from(filter.to_address),
|
||||||
|
};
|
||||||
|
|
||||||
let traces = self.tracedb.read().filter(&filter);
|
let traces = self.tracedb.read().filter(&filter);
|
||||||
Some(traces)
|
Some(traces)
|
||||||
} else {
|
},
|
||||||
None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,11 +1199,13 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn queue_transactions(&self, transactions: Vec<Bytes>) {
|
fn queue_transactions(&self, transactions: Vec<Bytes>) {
|
||||||
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
|
let queue_size = self.queue_transactions.load(AtomicOrdering::Relaxed);
|
||||||
|
trace!(target: "external_tx", "Queue size: {}", queue_size);
|
||||||
|
if queue_size > MAX_TX_QUEUE_SIZE {
|
||||||
debug!("Ignoring {} transactions: queue is full", transactions.len());
|
debug!("Ignoring {} transactions: queue is full", transactions.len());
|
||||||
} else {
|
} else {
|
||||||
let len = transactions.len();
|
let len = transactions.len();
|
||||||
match self.io_channel.send(ClientIoMessage::NewTransactions(transactions)) {
|
match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions)) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
|
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
|
||||||
}
|
}
|
||||||
@ -1078,11 +1217,31 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||||
self.miner.pending_transactions()
|
self.miner.pending_transactions(self.chain.read().best_block_number())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
self.engine.schedule(&self.latest_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 {
|
||||||
let engine = &*self.engine;
|
let engine = &*self.engine;
|
||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
@ -1092,7 +1251,7 @@ impl MiningBlockChainClient for Client {
|
|||||||
engine,
|
engine,
|
||||||
self.factories.clone(),
|
self.factories.clone(),
|
||||||
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
||||||
self.state_db.lock().boxed_clone(),
|
self.state_db.lock().boxed_clone_canon(&h),
|
||||||
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
||||||
self.build_last_hashes(h.clone()),
|
self.build_last_hashes(h.clone()),
|
||||||
author,
|
author,
|
||||||
@ -1103,11 +1262,15 @@ impl MiningBlockChainClient for Client {
|
|||||||
// Add uncles
|
// Add uncles
|
||||||
chain
|
chain
|
||||||
.find_uncle_headers(&h, engine.maximum_uncle_age())
|
.find_uncle_headers(&h, engine.maximum_uncle_age())
|
||||||
.unwrap()
|
.unwrap_or_else(Vec::new)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.take(engine.maximum_uncle_count())
|
.take(engine.maximum_uncle_count())
|
||||||
.foreach(|h| {
|
.foreach(|h| {
|
||||||
open_block.push_uncle(h).unwrap();
|
open_block.push_uncle(h).expect("pushing maximum_uncle_count;
|
||||||
|
open_block was just created;
|
||||||
|
push_uncle is not ok only if more than maximum_uncle_count is pushed;
|
||||||
|
so all push_uncle are Ok;
|
||||||
|
qed");
|
||||||
});
|
});
|
||||||
|
|
||||||
open_block
|
open_block
|
||||||
@ -1118,20 +1281,22 @@ impl MiningBlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
||||||
let _import_lock = self.import_lock.lock();
|
|
||||||
let _timer = PerfTimer::new("import_sealed_block");
|
|
||||||
let start = precise_time_ns();
|
|
||||||
|
|
||||||
let h = block.header().hash();
|
let h = block.header().hash();
|
||||||
let number = block.header().number();
|
let start = precise_time_ns();
|
||||||
|
let route = {
|
||||||
let block_data = block.rlp_bytes();
|
// scope for self.import_lock
|
||||||
let route = self.commit_block(block, &h, &block_data);
|
let _import_lock = self.import_lock.lock();
|
||||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
let _timer = PerfTimer::new("import_sealed_block");
|
||||||
|
|
||||||
|
let number = block.header().number();
|
||||||
|
let block_data = block.rlp_bytes();
|
||||||
|
let route = self.commit_block(block, &h, &block_data);
|
||||||
|
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||||
|
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
||||||
|
route
|
||||||
|
};
|
||||||
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
||||||
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
||||||
|
|
||||||
self.notify(|notify| {
|
self.notify(|notify| {
|
||||||
notify.new_blocks(
|
notify.new_blocks(
|
||||||
vec![h.clone()],
|
vec![h.clone()],
|
||||||
@ -1152,3 +1317,33 @@ impl MayPanic for Client {
|
|||||||
self.panic_handler.on_panic(closure);
|
self.panic_handler.on_panic(closure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_cache_details_before_commit() {
|
||||||
|
use tests::helpers::*;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
let client = generate_dummy_client(0);
|
||||||
|
let genesis = client.chain_info().best_block_hash;
|
||||||
|
let (new_hash, new_block) = get_good_dummy_block_hash();
|
||||||
|
|
||||||
|
let go = {
|
||||||
|
// Separate thread uncommited transaction
|
||||||
|
let go = Arc::new(AtomicBool::new(false));
|
||||||
|
let go_thread = go.clone();
|
||||||
|
let another_client = client.reference().clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut batch = DBTransaction::new(&*another_client.chain.read().db().clone());
|
||||||
|
another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new());
|
||||||
|
go_thread.store(true, Ordering::SeqCst);
|
||||||
|
});
|
||||||
|
go
|
||||||
|
};
|
||||||
|
|
||||||
|
while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); }
|
||||||
|
|
||||||
|
assert!(client.tree_route(&genesis, &new_hash).is_none());
|
||||||
|
}
|
||||||
|
@ -15,6 +15,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::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
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;
|
||||||
@ -26,23 +28,26 @@ use util::{journaldb, CompactionProfile};
|
|||||||
/// Client state db compaction profile
|
/// Client state db compaction profile
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DatabaseCompactionProfile {
|
pub enum DatabaseCompactionProfile {
|
||||||
/// Default compaction profile
|
/// Try to determine compaction profile automatically
|
||||||
Default,
|
Auto,
|
||||||
|
/// SSD compaction profile
|
||||||
|
SSD,
|
||||||
/// HDD or other slow storage io compaction profile
|
/// HDD or other slow storage io compaction profile
|
||||||
HDD,
|
HDD,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DatabaseCompactionProfile {
|
impl Default for DatabaseCompactionProfile {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
DatabaseCompactionProfile::Default
|
DatabaseCompactionProfile::Auto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseCompactionProfile {
|
impl DatabaseCompactionProfile {
|
||||||
/// Returns corresponding compaction profile.
|
/// Returns corresponding compaction profile.
|
||||||
pub fn compaction_profile(&self) -> CompactionProfile {
|
pub fn compaction_profile(&self, db_path: &Path) -> CompactionProfile {
|
||||||
match *self {
|
match *self {
|
||||||
DatabaseCompactionProfile::Default => Default::default(),
|
DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path),
|
||||||
|
DatabaseCompactionProfile::SSD => CompactionProfile::ssd(),
|
||||||
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,9 +58,10 @@ impl FromStr for DatabaseCompactionProfile {
|
|||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"ssd" | "default" => Ok(DatabaseCompactionProfile::Default),
|
"auto" => Ok(DatabaseCompactionProfile::Auto),
|
||||||
|
"ssd" => Ok(DatabaseCompactionProfile::SSD),
|
||||||
"hdd" => Ok(DatabaseCompactionProfile::HDD),
|
"hdd" => Ok(DatabaseCompactionProfile::HDD),
|
||||||
_ => Err("Invalid compaction profile given. Expected hdd/ssd (default).".into()),
|
_ => Err("Invalid compaction profile given. Expected default/hdd/ssd.".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +77,8 @@ pub enum Mode {
|
|||||||
/// Goes offline after RLP is inactive for some (given) time and
|
/// Goes offline after RLP is inactive for some (given) time and
|
||||||
/// stays inactive.
|
/// stays inactive.
|
||||||
Dark(Duration),
|
Dark(Duration),
|
||||||
|
/// Always off.
|
||||||
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Mode {
|
impl Default for Mode {
|
||||||
@ -79,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 {
|
||||||
@ -96,7 +115,7 @@ pub struct ClientConfig {
|
|||||||
pub pruning: journaldb::Algorithm,
|
pub pruning: journaldb::Algorithm,
|
||||||
/// The name of the client instance.
|
/// The name of the client instance.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// State db cache-size if not default
|
/// RocksDB state column cache-size if not default
|
||||||
pub db_cache_size: Option<usize>,
|
pub db_cache_size: Option<usize>,
|
||||||
/// State db compaction profile
|
/// State db compaction profile
|
||||||
pub db_compaction: DatabaseCompactionProfile,
|
pub db_compaction: DatabaseCompactionProfile,
|
||||||
@ -106,6 +125,14 @@ pub struct ClientConfig {
|
|||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
/// Type of block verifier used by client.
|
/// Type of block verifier used by client.
|
||||||
pub verifier_type: VerifierType,
|
pub verifier_type: VerifierType,
|
||||||
|
/// State db cache-size.
|
||||||
|
pub state_cache_size: usize,
|
||||||
|
/// EVM jump-tables cache size.
|
||||||
|
pub jump_table_size: usize,
|
||||||
|
/// State pruning history size.
|
||||||
|
pub history: u64,
|
||||||
|
/// Check seal valididity on block import
|
||||||
|
pub check_seal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -114,13 +141,13 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_compaction_profile() {
|
fn test_default_compaction_profile() {
|
||||||
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Default);
|
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Auto);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parsing_compaction_profile() {
|
fn test_parsing_compaction_profile() {
|
||||||
assert_eq!(DatabaseCompactionProfile::Default, "ssd".parse().unwrap());
|
assert_eq!(DatabaseCompactionProfile::Auto, "auto".parse().unwrap());
|
||||||
assert_eq!(DatabaseCompactionProfile::Default, "default".parse().unwrap());
|
assert_eq!(DatabaseCompactionProfile::SSD, "ssd".parse().unwrap());
|
||||||
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
|
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,13 +30,21 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
|||||||
pub use types::trace_filter::Filter as TraceFilter;
|
pub use types::trace_filter::Filter as TraceFilter;
|
||||||
pub use executive::{Executed, Executive, TransactOptions};
|
pub use executive::{Executed, Executive, TransactOptions};
|
||||||
pub use env_info::{LastHashes, EnvInfo};
|
pub use env_info::{LastHashes, EnvInfo};
|
||||||
pub use self::chain_notify::{ChainNotify, ChainNotifyClient};
|
pub use self::chain_notify::ChainNotify;
|
||||||
|
|
||||||
pub use types::call_analytics::CallAnalytics;
|
pub use types::call_analytics::CallAnalytics;
|
||||||
pub use block_import_error::BlockImportError;
|
pub use block_import_error::BlockImportError;
|
||||||
pub use transaction_import::TransactionImportResult;
|
pub use transaction_import::TransactionImportResult;
|
||||||
pub use transaction_import::TransactionImportError;
|
pub use transaction_import::TransactionImportError;
|
||||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
|
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
|
||||||
|
pub use verification::VerifierType;
|
||||||
|
|
||||||
|
/// IPC interfaces
|
||||||
|
#[cfg(feature="ipc")]
|
||||||
|
pub mod remote {
|
||||||
|
pub use super::traits::RemoteClient;
|
||||||
|
pub use super::chain_notify::ChainNotifyClient;
|
||||||
|
}
|
||||||
|
|
||||||
mod traits {
|
mod traits {
|
||||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||||
|
@ -34,9 +34,11 @@ use log_entry::LocalizedLogEntry;
|
|||||||
use receipt::{Receipt, LocalizedReceipt};
|
use receipt::{Receipt, LocalizedReceipt};
|
||||||
use blockchain::extras::BlockReceipts;
|
use blockchain::extras::BlockReceipts;
|
||||||
use error::{ImportResult};
|
use error::{ImportResult};
|
||||||
use evm::{Factory as EvmFactory, VMType};
|
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 views::BlockView;
|
||||||
|
|
||||||
use verification::queue::QueueInfo;
|
use verification::queue::QueueInfo;
|
||||||
use block::{OpenBlock, SealedBlock};
|
use block::{OpenBlock, SealedBlock};
|
||||||
@ -55,6 +57,8 @@ pub struct TestBlockChainClient {
|
|||||||
pub genesis_hash: H256,
|
pub genesis_hash: H256,
|
||||||
/// Last block hash.
|
/// Last block hash.
|
||||||
pub last_hash: RwLock<H256>,
|
pub last_hash: RwLock<H256>,
|
||||||
|
/// Extra data do set for each block
|
||||||
|
pub extra_data: Bytes,
|
||||||
/// Difficulty.
|
/// Difficulty.
|
||||||
pub difficulty: RwLock<U256>,
|
pub difficulty: RwLock<U256>,
|
||||||
/// Balances.
|
/// Balances.
|
||||||
@ -81,6 +85,10 @@ pub struct TestBlockChainClient {
|
|||||||
pub vm_factory: EvmFactory,
|
pub vm_factory: EvmFactory,
|
||||||
/// Timestamp assigned to latest sealed block
|
/// Timestamp assigned to latest sealed block
|
||||||
pub latest_block_timestamp: RwLock<u64>,
|
pub latest_block_timestamp: RwLock<u64>,
|
||||||
|
/// Ancient block info.
|
||||||
|
pub ancient_block: RwLock<Option<(H256, u64)>>,
|
||||||
|
/// First block info.
|
||||||
|
pub first_block: RwLock<Option<(H256, u64)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -105,11 +113,27 @@ impl Default for TestBlockChainClient {
|
|||||||
impl TestBlockChainClient {
|
impl TestBlockChainClient {
|
||||||
/// Creates new test client.
|
/// Creates new test client.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
Self::new_with_extra_data(Bytes::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new test client with specified extra data for each block
|
||||||
|
pub fn new_with_extra_data(extra_data: Bytes) -> Self {
|
||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
|
TestBlockChainClient::new_with_spec_and_extra(spec, extra_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create test client with custom spec.
|
||||||
|
pub fn new_with_spec(spec: Spec) -> Self {
|
||||||
|
TestBlockChainClient::new_with_spec_and_extra(spec, Bytes::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create test client with custom spec and extra data.
|
||||||
|
pub fn new_with_spec_and_extra(spec: Spec, extra_data: Bytes) -> Self {
|
||||||
let mut client = TestBlockChainClient {
|
let mut client = TestBlockChainClient {
|
||||||
blocks: RwLock::new(HashMap::new()),
|
blocks: RwLock::new(HashMap::new()),
|
||||||
numbers: RwLock::new(HashMap::new()),
|
numbers: RwLock::new(HashMap::new()),
|
||||||
genesis_hash: H256::new(),
|
genesis_hash: H256::new(),
|
||||||
|
extra_data: extra_data,
|
||||||
last_hash: RwLock::new(H256::new()),
|
last_hash: RwLock::new(H256::new()),
|
||||||
difficulty: RwLock::new(From::from(0)),
|
difficulty: RwLock::new(From::from(0)),
|
||||||
balances: RwLock::new(HashMap::new()),
|
balances: RwLock::new(HashMap::new()),
|
||||||
@ -122,8 +146,10 @@ impl TestBlockChainClient {
|
|||||||
queue_size: AtomicUsize::new(0),
|
queue_size: AtomicUsize::new(0),
|
||||||
miner: Arc::new(Miner::with_spec(&spec)),
|
miner: Arc::new(Miner::with_spec(&spec)),
|
||||||
spec: spec,
|
spec: spec,
|
||||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
vm_factory: EvmFactory::new(VMType::Interpreter, 1024 * 1024),
|
||||||
latest_block_timestamp: RwLock::new(10_000_000),
|
latest_block_timestamp: RwLock::new(10_000_000),
|
||||||
|
ancient_block: RwLock::new(None),
|
||||||
|
first_block: RwLock::new(None),
|
||||||
};
|
};
|
||||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||||
client.genesis_hash = client.last_hash.read().clone();
|
client.genesis_hash = client.last_hash.read().clone();
|
||||||
@ -184,6 +210,7 @@ impl TestBlockChainClient {
|
|||||||
header.set_parent_hash(self.last_hash.read().clone());
|
header.set_parent_hash(self.last_hash.read().clone());
|
||||||
header.set_number(n as BlockNumber);
|
header.set_number(n as BlockNumber);
|
||||||
header.set_gas_limit(U256::from(1_000_000));
|
header.set_gas_limit(U256::from(1_000_000));
|
||||||
|
header.set_extra_data(self.extra_data.clone());
|
||||||
let uncles = match with {
|
let uncles = match with {
|
||||||
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
||||||
let mut uncles = RlpStream::new_list(1);
|
let mut uncles = RlpStream::new_list(1);
|
||||||
@ -211,7 +238,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()
|
||||||
},
|
},
|
||||||
@ -277,7 +304,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");
|
||||||
@ -289,7 +316,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
|||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
|
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
|
||||||
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
||||||
let state_db = StateDB::new(journal_db);
|
let state_db = StateDB::new(journal_db, 1024 * 1024);
|
||||||
GuardedTempResult {
|
GuardedTempResult {
|
||||||
_temp: temp,
|
_temp: temp,
|
||||||
result: Some(state_db)
|
result: Some(state_db)
|
||||||
@ -297,6 +324,10 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MiningBlockChainClient for TestBlockChainClient {
|
impl MiningBlockChainClient for TestBlockChainClient {
|
||||||
|
fn latest_schedule(&self) -> Schedule {
|
||||||
|
Schedule::new_post_eip150(24576, 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 {
|
||||||
let engine = &*self.spec.engine;
|
let engine = &*self.spec.engine;
|
||||||
let genesis_header = self.spec.genesis_header();
|
let genesis_header = self.spec.genesis_header();
|
||||||
@ -397,6 +428,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()
|
||||||
}
|
}
|
||||||
@ -439,6 +474,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,
|
||||||
@ -551,6 +593,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
Ok(h)
|
Ok(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn import_block_with_receipts(&self, b: Bytes, _r: Bytes) -> Result<H256, BlockImportError> {
|
||||||
|
self.import_block(b)
|
||||||
|
}
|
||||||
|
|
||||||
fn queue_info(&self) -> QueueInfo {
|
fn queue_info(&self) -> QueueInfo {
|
||||||
QueueInfo {
|
QueueInfo {
|
||||||
verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed),
|
verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed),
|
||||||
@ -576,6 +622,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
genesis_hash: self.genesis_hash.clone(),
|
genesis_hash: self.genesis_hash.clone(),
|
||||||
best_block_hash: self.last_hash.read().clone(),
|
best_block_hash: self.last_hash.read().clone(),
|
||||||
best_block_number: self.blocks.read().len() as BlockNumber - 1,
|
best_block_number: self.blocks.read().len() as BlockNumber - 1,
|
||||||
|
first_block_hash: self.first_block.read().as_ref().map(|x| x.0),
|
||||||
|
first_block_number: self.first_block.read().as_ref().map(|x| x.1),
|
||||||
|
ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0),
|
||||||
|
ancient_block_number: self.ancient_block.read().as_ref().map(|x| x.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,6 +652,12 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||||
self.miner.pending_transactions()
|
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 set_mode(&self, _: Mode) { unimplemented!(); }
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use util::{U256, Address, H256, H2048, Bytes, Itertools};
|
use util::{U256, Address, H256, H2048, Bytes, Itertools};
|
||||||
|
use util::stats::Histogram;
|
||||||
use blockchain::TreeRoute;
|
use blockchain::TreeRoute;
|
||||||
use verification::queue::QueueInfo as BlockQueueInfo;
|
use verification::queue::QueueInfo as BlockQueueInfo;
|
||||||
use block::{OpenBlock, SealedBlock};
|
use block::{OpenBlock, SealedBlock};
|
||||||
@ -27,18 +28,18 @@ use views::{BlockView};
|
|||||||
use error::{ImportResult, CallError};
|
use error::{ImportResult, CallError};
|
||||||
use receipt::LocalizedReceipt;
|
use receipt::LocalizedReceipt;
|
||||||
use trace::LocalizedTrace;
|
use trace::LocalizedTrace;
|
||||||
use evm::Factory as EvmFactory;
|
use evm::{Factory as EvmFactory, Schedule};
|
||||||
use types::ids::*;
|
|
||||||
use types::trace_filter::Filter as TraceFilter;
|
|
||||||
use executive::Executed;
|
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;
|
||||||
|
|
||||||
#[derive(Ipc)]
|
|
||||||
#[ipc(client_ident="RemoteClient")]
|
#[ipc(client_ident="RemoteClient")]
|
||||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||||
pub trait BlockChainClient : Sync + Send {
|
pub trait BlockChainClient : Sync + Send {
|
||||||
@ -140,6 +141,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Import a block into the blockchain.
|
/// Import a block into the blockchain.
|
||||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
|
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||||
|
|
||||||
|
/// Import a block with transaction receipts. Does no sealing and transaction validation.
|
||||||
|
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||||
|
|
||||||
/// Get block queue information.
|
/// Get block queue information.
|
||||||
fn queue_info(&self) -> BlockQueueInfo;
|
fn queue_info(&self) -> BlockQueueInfo;
|
||||||
|
|
||||||
@ -188,31 +192,55 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// list all transactions
|
/// list all transactions
|
||||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||||
|
|
||||||
/// Get the gas price distribution.
|
/// Sorted list of transaction gas prices from at least last sample_size blocks.
|
||||||
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
|
fn gas_price_corpus(&self, sample_size: usize) -> Vec<U256> {
|
||||||
let mut h = self.chain_info().best_block_hash;
|
let mut h = self.chain_info().best_block_hash;
|
||||||
let mut corpus = Vec::new();
|
let mut corpus = Vec::new();
|
||||||
for _ in 0..sample_size {
|
while corpus.is_empty() {
|
||||||
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
for _ in 0..sample_size {
|
||||||
let block = BlockView::new(&block_bytes);
|
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||||
let header = block.header_view();
|
let block = BlockView::new(&block_bytes);
|
||||||
if header.number() == 0 {
|
let header = block.header_view();
|
||||||
break;
|
if header.number() == 0 {
|
||||||
|
return corpus;
|
||||||
|
}
|
||||||
|
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
|
||||||
|
h = header.parent_hash().clone();
|
||||||
}
|
}
|
||||||
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
|
|
||||||
h = header.parent_hash().clone();
|
|
||||||
}
|
}
|
||||||
corpus.sort();
|
corpus.sort();
|
||||||
let n = corpus.len();
|
corpus
|
||||||
if n > 0 {
|
|
||||||
Ok((0..(distribution_size + 1))
|
|
||||||
.map(|i| corpus[i * (n - 1) / distribution_size])
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate median gas price from recent blocks if they have any transactions.
|
||||||
|
fn gas_price_median(&self, sample_size: usize) -> Option<U256> {
|
||||||
|
let corpus = self.gas_price_corpus(sample_size);
|
||||||
|
corpus.get(corpus.len() / 2).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the gas price distribution based on recent blocks if they have any transactions.
|
||||||
|
fn gas_price_histogram(&self, sample_size: usize, bucket_number: usize) -> Option<Histogram> {
|
||||||
|
let raw_corpus = self.gas_price_corpus(sample_size);
|
||||||
|
let raw_len = raw_corpus.len();
|
||||||
|
// Throw out outliers.
|
||||||
|
let (corpus, _) = raw_corpus.split_at(raw_len - raw_len / 40);
|
||||||
|
Histogram::new(corpus, bucket_number)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the preferred network ID to sign on
|
||||||
|
fn signing_network_id(&self) -> Option<u8>;
|
||||||
|
|
||||||
|
/// Get the mode.
|
||||||
|
fn mode(&self) -> Mode;
|
||||||
|
|
||||||
|
/// Set the 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
|
||||||
@ -229,6 +257,9 @@ pub trait MiningBlockChainClient : BlockChainClient {
|
|||||||
|
|
||||||
/// Import sealed block. Skips all verifications.
|
/// Import sealed block. Skips all verifications.
|
||||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
|
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
|
||||||
|
|
||||||
|
/// Returns latest schedule.
|
||||||
|
fn latest_schedule(&self) -> Schedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IpcConfig for BlockChainClient { }
|
impl IpcConfig for BlockChainClient { }
|
||||||
|
@ -114,7 +114,7 @@ pub trait Writable {
|
|||||||
R: Deref<Target = [u8]> {
|
R: Deref<Target = [u8]> {
|
||||||
match policy {
|
match policy {
|
||||||
CacheUpdatePolicy::Overwrite => {
|
CacheUpdatePolicy::Overwrite => {
|
||||||
for (key, value) in values.into_iter() {
|
for (key, value) in values {
|
||||||
self.write(col, &key, &value);
|
self.write(col, &key, &value);
|
||||||
cache.insert(key, value);
|
cache.insert(key, value);
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ pub trait Writable {
|
|||||||
R: Deref<Target = [u8]> {
|
R: Deref<Target = [u8]> {
|
||||||
match policy {
|
match policy {
|
||||||
CacheUpdatePolicy::Overwrite => {
|
CacheUpdatePolicy::Overwrite => {
|
||||||
for (key, value) in values.into_iter() {
|
for (key, value) in values {
|
||||||
match value {
|
match value {
|
||||||
Some(ref v) => self.write(col, &key, v),
|
Some(ref v) => self.write(col, &key, v),
|
||||||
None => self.delete(col, &key),
|
None => self.delete(col, &key),
|
||||||
@ -144,7 +144,7 @@ pub trait Writable {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CacheUpdatePolicy::Remove => {
|
CacheUpdatePolicy::Remove => {
|
||||||
for (key, value) in values.into_iter() {
|
for (key, value) in values {
|
||||||
match value {
|
match value {
|
||||||
Some(v) => self.write(col, &key, &v),
|
Some(v) => self.write(col, &key, &v),
|
||||||
None => self.delete(col, &key),
|
None => self.delete(col, &key),
|
||||||
|
429
ethcore/src/engines/authority_round.rs
Normal file
429
ethcore/src/engines/authority_round.rs
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
// 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 blockchain engine that supports a non-instant BFT proof-of-authority.
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||||
|
use std::sync::Weak;
|
||||||
|
use std::time::{UNIX_EPOCH, Duration};
|
||||||
|
use util::*;
|
||||||
|
use ethkey::{verify_address, Signature};
|
||||||
|
use rlp::{UntrustedRlp, View, encode};
|
||||||
|
use account_provider::AccountProvider;
|
||||||
|
use block::*;
|
||||||
|
use spec::CommonParams;
|
||||||
|
use engines::Engine;
|
||||||
|
use header::Header;
|
||||||
|
use error::{Error, BlockError};
|
||||||
|
use evm::Schedule;
|
||||||
|
use ethjson;
|
||||||
|
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
|
||||||
|
use service::ClientIoMessage;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
|
use env_info::EnvInfo;
|
||||||
|
use builtin::Builtin;
|
||||||
|
|
||||||
|
/// `AuthorityRound` params.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct AuthorityRoundParams {
|
||||||
|
/// Gas limit divisor.
|
||||||
|
pub gas_limit_bound_divisor: U256,
|
||||||
|
/// Time to wait before next block or authority switching.
|
||||||
|
pub step_duration: Duration,
|
||||||
|
/// Valid authorities.
|
||||||
|
pub authorities: Vec<Address>,
|
||||||
|
/// Number of authorities.
|
||||||
|
pub authority_n: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||||
|
fn from(p: ethjson::spec::AuthorityRoundParams) -> Self {
|
||||||
|
AuthorityRoundParams {
|
||||||
|
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||||
|
step_duration: Duration::from_secs(p.step_duration.into()),
|
||||||
|
authority_n: p.authorities.len(),
|
||||||
|
authorities: p.authorities.into_iter().map(Into::into).collect::<Vec<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Engine using `AuthorityRound` proof-of-work consensus algorithm, suitable for Ethereum
|
||||||
|
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||||
|
pub struct AuthorityRound {
|
||||||
|
params: CommonParams,
|
||||||
|
our_params: AuthorityRoundParams,
|
||||||
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
|
transition_service: IoService<BlockArrived>,
|
||||||
|
message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
|
||||||
|
step: AtomicUsize,
|
||||||
|
proposed: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||||
|
UntrustedRlp::new(&header.seal()[0]).as_val()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header_signature(header: &Header) -> Result<Signature, ::rlp::DecoderError> {
|
||||||
|
UntrustedRlp::new(&header.seal()[1]).as_val::<H520>().map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AsMillis {
|
||||||
|
fn as_millis(&self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMillis for Duration {
|
||||||
|
fn as_millis(&self) -> u64 {
|
||||||
|
self.as_secs()*1_000 + (self.subsec_nanos()/1_000_000) as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthorityRound {
|
||||||
|
/// Create a new instance of AuthorityRound engine.
|
||||||
|
pub fn new(params: CommonParams, our_params: AuthorityRoundParams, builtins: BTreeMap<Address, Builtin>) -> Result<Arc<Self>, Error> {
|
||||||
|
let initial_step = (unix_now().as_secs() / our_params.step_duration.as_secs()) as usize;
|
||||||
|
let engine = Arc::new(
|
||||||
|
AuthorityRound {
|
||||||
|
params: params,
|
||||||
|
our_params: our_params,
|
||||||
|
builtins: builtins,
|
||||||
|
transition_service: try!(IoService::<BlockArrived>::start()),
|
||||||
|
message_channel: Mutex::new(None),
|
||||||
|
step: AtomicUsize::new(initial_step),
|
||||||
|
proposed: AtomicBool::new(false)
|
||||||
|
});
|
||||||
|
let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
|
||||||
|
try!(engine.transition_service.register_handler(Arc::new(handler)));
|
||||||
|
Ok(engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&self) -> usize {
|
||||||
|
self.step.load(AtomicOrdering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remaining_step_duration(&self) -> Duration {
|
||||||
|
let now = unix_now();
|
||||||
|
let step_end = self.our_params.step_duration * (self.step() as u32 + 1);
|
||||||
|
if step_end > now {
|
||||||
|
step_end - now
|
||||||
|
} else {
|
||||||
|
Duration::from_secs(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_proposer(&self, step: usize) -> &Address {
|
||||||
|
let ref p = self.our_params;
|
||||||
|
p.authorities.get(step % p.authority_n).expect("There are authority_n authorities; taking number modulo authority_n gives number in authority_n range; qed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
|
||||||
|
self.step_proposer(step) == address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unix_now() -> Duration {
|
||||||
|
UNIX_EPOCH.elapsed().expect("Valid time has to be set in your system.")
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransitionHandler {
|
||||||
|
engine: Weak<AuthorityRound>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct BlockArrived;
|
||||||
|
|
||||||
|
const ENGINE_TIMEOUT_TOKEN: TimerToken = 23;
|
||||||
|
|
||||||
|
impl IoHandler<BlockArrived> for TransitionHandler {
|
||||||
|
fn initialize(&self, io: &IoContext<BlockArrived>) {
|
||||||
|
if let Some(engine) = self.engine.upgrade() {
|
||||||
|
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
|
||||||
|
.unwrap_or_else(|e| warn!(target: "poa", "Failed to start consensus step timer: {}.", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timeout(&self, io: &IoContext<BlockArrived>, timer: TimerToken) {
|
||||||
|
if timer == ENGINE_TIMEOUT_TOKEN {
|
||||||
|
if let Some(engine) = self.engine.upgrade() {
|
||||||
|
engine.step.fetch_add(1, AtomicOrdering::SeqCst);
|
||||||
|
engine.proposed.store(false, AtomicOrdering::SeqCst);
|
||||||
|
if let Some(ref channel) = *engine.message_channel.lock() {
|
||||||
|
match channel.send(ClientIoMessage::UpdateSealing) {
|
||||||
|
Ok(_) => trace!(target: "poa", "timeout: UpdateSealing message sent for step {}.", engine.step.load(AtomicOrdering::Relaxed)),
|
||||||
|
Err(err) => trace!(target: "poa", "timeout: Could not send a sealing message {} for step {}.", err, engine.step.load(AtomicOrdering::Relaxed)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
|
||||||
|
.unwrap_or_else(|e| warn!(target: "poa", "Failed to restart consensus step timer: {}.", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for AuthorityRound {
|
||||||
|
fn name(&self) -> &str { "AuthorityRound" }
|
||||||
|
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||||
|
/// Two fields - consensus step and the corresponding proposer signature.
|
||||||
|
fn seal_fields(&self) -> usize { 2 }
|
||||||
|
|
||||||
|
fn params(&self) -> &CommonParams { &self.params }
|
||||||
|
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
|
||||||
|
|
||||||
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
|
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
|
||||||
|
map![
|
||||||
|
"step".into() => header_step(header).as_ref().map(ToString::to_string).unwrap_or("".into()),
|
||||||
|
"signature".into() => header_signature(header).as_ref().map(ToString::to_string).unwrap_or("".into())
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||||
|
Schedule::new_post_eip150(usize::max_value(), true, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
|
||||||
|
header.set_difficulty(parent.difficulty().clone());
|
||||||
|
header.set_gas_limit({
|
||||||
|
let gas_limit = parent.gas_limit().clone();
|
||||||
|
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||||
|
if gas_limit < gas_floor_target {
|
||||||
|
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||||
|
} else {
|
||||||
|
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply the block reward on finalisation of the block.
|
||||||
|
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||||
|
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
||||||
|
|
||||||
|
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
||||||
|
let ref p = self.our_params;
|
||||||
|
Some(p.authorities.contains(author))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to seal the block internally.
|
||||||
|
///
|
||||||
|
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
|
||||||
|
/// be returned.
|
||||||
|
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
|
||||||
|
if self.proposed.load(AtomicOrdering::SeqCst) { return None; }
|
||||||
|
let header = block.header();
|
||||||
|
let step = self.step();
|
||||||
|
if self.is_step_proposer(step, header.author()) {
|
||||||
|
if let Some(ap) = accounts {
|
||||||
|
// Account should be permanently unlocked, otherwise sealing will fail.
|
||||||
|
if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) {
|
||||||
|
trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
|
||||||
|
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||||
|
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
|
} else {
|
||||||
|
warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(target: "poa", "generate_seal: FAIL: Accounts not provided.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check the number of seal fields.
|
||||||
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
|
if header.seal().len() != self.seal_fields() {
|
||||||
|
trace!(target: "poa", "verify_block_basic: wrong number of seal fields");
|
||||||
|
Err(From::from(BlockError::InvalidSealArity(
|
||||||
|
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the signature belongs to the correct proposer.
|
||||||
|
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
|
let header_step = try!(header_step(header));
|
||||||
|
// Give one step slack if step is lagging, double vote is still not possible.
|
||||||
|
if header_step <= self.step() + 1 {
|
||||||
|
let proposer_signature = try!(header_signature(header));
|
||||||
|
let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash()));
|
||||||
|
if ok_sig {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
trace!(target: "poa", "verify_block_unordered: invalid seal signature");
|
||||||
|
try!(Err(BlockError::InvalidSeal))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace!(target: "poa", "verify_block_unordered: block from the future");
|
||||||
|
try!(Err(BlockError::InvalidSeal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
|
// Don't calculate difficulty for genesis blocks.
|
||||||
|
if header.number() == 0 {
|
||||||
|
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
|
||||||
|
}
|
||||||
|
|
||||||
|
let step = try!(header_step(header));
|
||||||
|
// Check if parent is from a previous step.
|
||||||
|
if step == try!(header_step(parent)) {
|
||||||
|
trace!(target: "poa", "Multiple blocks proposed for step {}.", step);
|
||||||
|
try!(Err(BlockError::DoubleVote(header.author().clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check difficulty is correct given the two timestamps.
|
||||||
|
if header.difficulty() != parent.difficulty() {
|
||||||
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
|
||||||
|
}
|
||||||
|
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||||
|
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||||
|
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||||
|
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||||
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() })));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||||
|
try!(t.check_low_s());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
|
||||||
|
t.sender().map(|_|()) // Perform EC recovery and cache sender
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
|
||||||
|
let mut guard = self.message_channel.lock();
|
||||||
|
*guard = Some(message_channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use util::*;
|
||||||
|
use env_info::EnvInfo;
|
||||||
|
use header::Header;
|
||||||
|
use error::{Error, BlockError};
|
||||||
|
use rlp::encode;
|
||||||
|
use block::*;
|
||||||
|
use tests::helpers::*;
|
||||||
|
use account_provider::AccountProvider;
|
||||||
|
use spec::Spec;
|
||||||
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn has_valid_metadata() {
|
||||||
|
let engine = Spec::new_test_round().engine;
|
||||||
|
assert!(!engine.name().is_empty());
|
||||||
|
assert!(engine.version().major >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_return_schedule() {
|
||||||
|
let engine = Spec::new_test_round().engine;
|
||||||
|
let schedule = engine.schedule(&EnvInfo {
|
||||||
|
number: 10000000,
|
||||||
|
author: 0.into(),
|
||||||
|
timestamp: 0,
|
||||||
|
difficulty: 0.into(),
|
||||||
|
last_hashes: Arc::new(vec![]),
|
||||||
|
gas_used: 0.into(),
|
||||||
|
gas_limit: 0.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(schedule.stack_limit > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verification_fails_on_short_seal() {
|
||||||
|
let engine = Spec::new_test_round().engine;
|
||||||
|
let header: Header = Header::default();
|
||||||
|
|
||||||
|
let verify_result = engine.verify_block_basic(&header, None);
|
||||||
|
|
||||||
|
match verify_result {
|
||||||
|
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
|
||||||
|
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
|
||||||
|
_ => { panic!("Should be error, got Ok"); },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_do_signature_verification_fail() {
|
||||||
|
let engine = Spec::new_test_round().engine;
|
||||||
|
let mut header: Header = Header::default();
|
||||||
|
header.set_seal(vec![encode(&H520::default()).to_vec()]);
|
||||||
|
|
||||||
|
let verify_result = engine.verify_block_unordered(&header, None);
|
||||||
|
assert!(verify_result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generates_seal_and_does_not_double_propose() {
|
||||||
|
let tap = AccountProvider::transient_provider();
|
||||||
|
let addr1 = tap.insert_account("1".sha3(), "1").unwrap();
|
||||||
|
tap.unlock_account_permanently(addr1, "1".into()).unwrap();
|
||||||
|
let addr2 = tap.insert_account("2".sha3(), "2").unwrap();
|
||||||
|
tap.unlock_account_permanently(addr2, "2".into()).unwrap();
|
||||||
|
|
||||||
|
let spec = Spec::new_test_round();
|
||||||
|
let engine = &*spec.engine;
|
||||||
|
let genesis_header = spec.genesis_header();
|
||||||
|
let mut db1 = get_temp_state_db().take();
|
||||||
|
spec.ensure_db_good(&mut db1).unwrap();
|
||||||
|
let mut db2 = get_temp_state_db().take();
|
||||||
|
spec.ensure_db_good(&mut db2).unwrap();
|
||||||
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
|
let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
|
let b1 = b1.close_and_lock();
|
||||||
|
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
|
let b2 = b2.close_and_lock();
|
||||||
|
|
||||||
|
if let Some(seal) = engine.generate_seal(b1.block(), Some(&tap)) {
|
||||||
|
assert!(b1.clone().try_seal(engine, seal).is_ok());
|
||||||
|
// Second proposal is forbidden.
|
||||||
|
assert!(engine.generate_seal(b1.block(), Some(&tap)).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(seal) = engine.generate_seal(b2.block(), Some(&tap)) {
|
||||||
|
assert!(b2.clone().try_seal(engine, seal).is_ok());
|
||||||
|
// Second proposal is forbidden.
|
||||||
|
assert!(engine.generate_seal(b2.block(), Some(&tap)).is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn proposer_switching() {
|
||||||
|
let mut header: Header = Header::default();
|
||||||
|
let tap = AccountProvider::transient_provider();
|
||||||
|
let addr = tap.insert_account("0".sha3(), "0").unwrap();
|
||||||
|
|
||||||
|
header.set_author(addr);
|
||||||
|
|
||||||
|
let engine = Spec::new_test_round().engine;
|
||||||
|
|
||||||
|
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||||
|
let mut step = UNIX_EPOCH.elapsed().unwrap().as_secs();
|
||||||
|
header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
|
let first_ok = engine.verify_block_seal(&header).is_ok();
|
||||||
|
step = step + 1;
|
||||||
|
header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
|
let second_ok = engine.verify_block_seal(&header).is_ok();
|
||||||
|
|
||||||
|
assert!(first_ok ^ second_ok);
|
||||||
|
}
|
||||||
|
}
|
@ -16,14 +16,20 @@
|
|||||||
|
|
||||||
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
||||||
|
|
||||||
use common::*;
|
|
||||||
use ethkey::{recover, public_to_address};
|
use ethkey::{recover, public_to_address};
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use block::*;
|
use block::*;
|
||||||
|
use builtin::Builtin;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
|
use env_info::EnvInfo;
|
||||||
|
use error::{BlockError, Error};
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
use header::Header;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
|
|
||||||
|
use util::*;
|
||||||
|
|
||||||
/// `BasicAuthority` params.
|
/// `BasicAuthority` params.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -75,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()
|
||||||
@ -112,7 +118,7 @@ impl Engine for BasicAuthority {
|
|||||||
let header = block.header();
|
let header = block.header();
|
||||||
let message = header.bare_hash();
|
let message = header.bare_hash();
|
||||||
// account should be pernamently unlocked, otherwise sealing will fail
|
// account should be pernamently unlocked, otherwise sealing will fail
|
||||||
if let Ok(signature) = ap.sign(*block.header().author(), message) {
|
if let Ok(signature) = ap.sign(*block.header().author(), None, message) {
|
||||||
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||||
@ -175,24 +181,20 @@ impl Engine for BasicAuthority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
|
||||||
/// Get the none field of the header.
|
|
||||||
pub fn signature(&self) -> H520 {
|
|
||||||
::rlp::decode(&self.seal()[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common::*;
|
use util::*;
|
||||||
use block::*;
|
use block::*;
|
||||||
|
use env_info::EnvInfo;
|
||||||
|
use error::{BlockError, Error};
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
|
use header::Header;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
|
|
||||||
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
||||||
fn new_test_authority() -> Spec {
|
fn new_test_authority() -> Spec {
|
||||||
let bytes: &[u8] = include_bytes!("../../res/test_authority.json");
|
let bytes: &[u8] = include_bytes!("../../res/basic_authority.json");
|
||||||
Spec::load(bytes).expect("invalid chain spec")
|
Spec::load(bytes).expect("invalid chain spec")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@ use std::collections::BTreeMap;
|
|||||||
use util::Address;
|
use util::Address;
|
||||||
use builtin::Builtin;
|
use builtin::Builtin;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
|
use env_info::EnvInfo;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use env_info::EnvInfo;
|
|
||||||
use block::ExecutedBlock;
|
use block::ExecutedBlock;
|
||||||
use common::Bytes;
|
use util::Bytes;
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
|
|
||||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||||
@ -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(usize::max_value(), false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sealer(&self, _author: &Address) -> Option<bool> { Some(true) }
|
fn is_sealer(&self, _author: &Address) -> Option<bool> { Some(true) }
|
||||||
@ -67,10 +67,11 @@ impl Engine for InstantSeal {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common::*;
|
use util::*;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
|
use header::Header;
|
||||||
use block::*;
|
use block::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -78,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();
|
||||||
@ -94,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());
|
||||||
|
@ -19,16 +19,25 @@
|
|||||||
mod null_engine;
|
mod null_engine;
|
||||||
mod instant_seal;
|
mod instant_seal;
|
||||||
mod basic_authority;
|
mod basic_authority;
|
||||||
|
mod authority_round;
|
||||||
|
|
||||||
pub use self::null_engine::NullEngine;
|
pub use self::null_engine::NullEngine;
|
||||||
pub use self::instant_seal::InstantSeal;
|
pub use self::instant_seal::InstantSeal;
|
||||||
pub use self::basic_authority::BasicAuthority;
|
pub use self::basic_authority::BasicAuthority;
|
||||||
|
pub use self::authority_round::AuthorityRound;
|
||||||
|
|
||||||
use common::*;
|
use util::*;
|
||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use block::ExecutedBlock;
|
use block::ExecutedBlock;
|
||||||
|
use builtin::Builtin;
|
||||||
|
use env_info::EnvInfo;
|
||||||
|
use error::Error;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
|
use io::IoChannel;
|
||||||
|
use service::ClientIoMessage;
|
||||||
|
use header::Header;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
|
|
||||||
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
||||||
/// Provides hooks into each of the major parts of block import.
|
/// Provides hooks into each of the major parts of block import.
|
||||||
@ -42,7 +51,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() }
|
||||||
@ -103,6 +112,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.
|
||||||
@ -123,10 +135,16 @@ pub trait Engine : Sync + Send {
|
|||||||
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
||||||
/// Determine the code execution cost of the builtin contract with address `a`.
|
/// Determine the code execution cost of the builtin contract with address `a`.
|
||||||
/// Panics if `is_builtin(a)` is not true.
|
/// Panics if `is_builtin(a)` is not true.
|
||||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.builtins().get(a).unwrap().cost(input.len()) }
|
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 {
|
||||||
|
self.builtins().get(a).expect("queried cost of nonexistent builtin").cost(input.len())
|
||||||
|
}
|
||||||
/// Execution the builtin contract `a` on `input` and return `output`.
|
/// Execution the builtin contract `a` on `input` and return `output`.
|
||||||
/// Panics if `is_builtin(a)` is not true.
|
/// Panics if `is_builtin(a)` is not true.
|
||||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) { self.builtins().get(a).unwrap().execute(input, output); }
|
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) {
|
||||||
|
self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a channel for communication with Client which can be used for sealing.
|
||||||
|
fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
|
||||||
// TODO: sealing stuff - though might want to leave this for later.
|
// TODO: sealing stuff - though might want to leave this for later.
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,13 @@ pub enum TransactionError {
|
|||||||
/// Transaction gas price
|
/// Transaction gas price
|
||||||
got: U256,
|
got: U256,
|
||||||
},
|
},
|
||||||
|
/// Transaction's gas is below currently set minimal gas requirement.
|
||||||
|
InsufficientGas {
|
||||||
|
/// Minimal expected gas
|
||||||
|
minimal: U256,
|
||||||
|
/// Transaction gas
|
||||||
|
got: U256,
|
||||||
|
},
|
||||||
/// Sender doesn't have enough funds to pay for this transaction
|
/// Sender doesn't have enough funds to pay for this transaction
|
||||||
InsufficientBalance {
|
InsufficientBalance {
|
||||||
/// Senders balance
|
/// Senders balance
|
||||||
@ -63,6 +70,14 @@ pub enum TransactionError {
|
|||||||
},
|
},
|
||||||
/// Transaction's gas limit (aka gas) is invalid.
|
/// Transaction's gas limit (aka gas) is invalid.
|
||||||
InvalidGasLimit(OutOfBounds<U256>),
|
InvalidGasLimit(OutOfBounds<U256>),
|
||||||
|
/// Transaction sender is banned.
|
||||||
|
SenderBanned,
|
||||||
|
/// Transaction receipient is banned.
|
||||||
|
RecipientBanned,
|
||||||
|
/// Contract creation code is banned.
|
||||||
|
CodeBanned,
|
||||||
|
/// Invalid network ID given.
|
||||||
|
InvalidNetworkId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TransactionError {
|
impl fmt::Display for TransactionError {
|
||||||
@ -75,12 +90,18 @@ impl fmt::Display for TransactionError {
|
|||||||
LimitReached => "Transaction limit reached".into(),
|
LimitReached => "Transaction limit reached".into(),
|
||||||
InsufficientGasPrice { minimal, got } =>
|
InsufficientGasPrice { minimal, got } =>
|
||||||
format!("Insufficient gas price. Min={}, Given={}", minimal, got),
|
format!("Insufficient gas price. Min={}, Given={}", minimal, got),
|
||||||
|
InsufficientGas { minimal, got } =>
|
||||||
|
format!("Insufficient gas. Min={}, Given={}", minimal, got),
|
||||||
InsufficientBalance { balance, cost } =>
|
InsufficientBalance { balance, cost } =>
|
||||||
format!("Insufficient balance for transaction. Balance={}, Cost={}",
|
format!("Insufficient balance for transaction. Balance={}, Cost={}",
|
||||||
balance, cost),
|
balance, cost),
|
||||||
GasLimitExceeded { limit, got } =>
|
GasLimitExceeded { limit, got } =>
|
||||||
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
||||||
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
||||||
|
SenderBanned => "Sender is temporarily banned.".into(),
|
||||||
|
RecipientBanned => "Recipient is temporarily banned.".into(),
|
||||||
|
CodeBanned => "Contract code is temporarily banned.".into(),
|
||||||
|
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))
|
||||||
@ -146,6 +167,8 @@ pub enum BlockError {
|
|||||||
UnknownParent(H256),
|
UnknownParent(H256),
|
||||||
/// Uncle parent given is unknown.
|
/// Uncle parent given is unknown.
|
||||||
UnknownUncleParent(H256),
|
UnknownUncleParent(H256),
|
||||||
|
/// The same author issued different votes at the same step.
|
||||||
|
DoubleVote(H160),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for BlockError {
|
impl fmt::Display for BlockError {
|
||||||
@ -179,6 +202,7 @@ impl fmt::Display for BlockError {
|
|||||||
RidiculousNumber(ref oob) => format!("Implausible block number. {}", oob),
|
RidiculousNumber(ref oob) => format!("Implausible block number. {}", oob),
|
||||||
UnknownParent(ref hash) => format!("Unknown parent: {}", hash),
|
UnknownParent(ref hash) => format!("Unknown parent: {}", hash),
|
||||||
UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash),
|
UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash),
|
||||||
|
DoubleVote(ref address) => format!("Author {} issued too many blocks.", address),
|
||||||
};
|
};
|
||||||
|
|
||||||
f.write_fmt(format_args!("Block error ({})", msg))
|
f.write_fmt(format_args!("Block error ({})", msg))
|
||||||
|
@ -15,9 +15,15 @@
|
|||||||
// 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};
|
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager};
|
||||||
use common::*;
|
use util::*;
|
||||||
use block::*;
|
use block::*;
|
||||||
|
use builtin::Builtin;
|
||||||
|
use env_info::EnvInfo;
|
||||||
|
use error::{BlockError, TransactionError, Error};
|
||||||
|
use header::Header;
|
||||||
|
use state::CleanupMode;
|
||||||
use spec::CommonParams;
|
use spec::CommonParams;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
@ -41,7 +47,7 @@ pub struct EthashParams {
|
|||||||
/// Namereg contract address.
|
/// Namereg contract address.
|
||||||
pub registrar: Address,
|
pub registrar: Address,
|
||||||
/// Homestead transition block number.
|
/// Homestead transition block number.
|
||||||
pub frontier_compatibility_mode_limit: u64,
|
pub homestead_transition: u64,
|
||||||
/// DAO hard-fork transition block (X).
|
/// DAO hard-fork transition block (X).
|
||||||
pub dao_hardfork_transition: u64,
|
pub dao_hardfork_transition: u64,
|
||||||
/// DAO hard-fork refund contract address (C).
|
/// DAO hard-fork refund contract address (C).
|
||||||
@ -54,6 +60,22 @@ 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,
|
||||||
|
/// Number of first block where EIP-150 rules begin.
|
||||||
|
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,
|
||||||
|
/// Maximum amount of code that can be deploying into a contract.
|
||||||
|
pub max_code_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||||
@ -66,13 +88,21 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
|||||||
duration_limit: p.duration_limit.into(),
|
duration_limit: p.duration_limit.into(),
|
||||||
block_reward: p.block_reward.into(),
|
block_reward: p.block_reward.into(),
|
||||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map_or(0, Into::into),
|
homestead_transition: p.homestead_transition.map_or(0, Into::into),
|
||||||
dao_hardfork_transition: p.dao_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into),
|
||||||
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
|
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
|
||||||
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
|
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
|
||||||
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into),
|
||||||
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(u64::max_value(), 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(u64::max_value(), Into::into),
|
||||||
|
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into),
|
||||||
|
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into),
|
||||||
|
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,17 +142,32 @@ 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 {
|
||||||
trace!(target: "client", "Creating schedule. fCML={}", self.ethash_params.frontier_compatibility_mode_limit);
|
trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition);
|
||||||
|
|
||||||
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
|
if env_info.number < self.ethash_params.homestead_transition {
|
||||||
Schedule::new_frontier()
|
Schedule::new_frontier()
|
||||||
} else {
|
} else if env_info.number < self.ethash_params.eip150_transition {
|
||||||
Schedule::new_homestead()
|
Schedule::new_homestead()
|
||||||
|
} else {
|
||||||
|
Schedule::new_post_eip150(
|
||||||
|
self.ethash_params.max_code_size as usize,
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +204,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);
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
@ -172,12 +217,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.
|
||||||
@ -264,9 +309,16 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||||
if header.number() >= self.ethash_params.frontier_compatibility_mode_limit {
|
if header.number() >= self.ethash_params.homestead_transition {
|
||||||
try!(t.check_low_s());
|
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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +342,7 @@ impl Ethash {
|
|||||||
false => self.ethash_params.difficulty_bound_divisor,
|
false => self.ethash_params.difficulty_bound_divisor,
|
||||||
};
|
};
|
||||||
let duration_limit = self.ethash_params.duration_limit;
|
let duration_limit = self.ethash_params.duration_limit;
|
||||||
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
let frontier_limit = self.ethash_params.homestead_transition;
|
||||||
|
|
||||||
let mut target = if header.number() < frontier_limit {
|
let mut target = if header.number() < frontier_limit {
|
||||||
if header.timestamp() >= parent.timestamp() + duration_limit {
|
if header.timestamp() >= parent.timestamp() + duration_limit {
|
||||||
@ -311,9 +363,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
|
||||||
@ -358,11 +421,14 @@ impl Header {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common::*;
|
use util::*;
|
||||||
use block::*;
|
use block::*;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use super::super::new_morden;
|
use env_info::EnvInfo;
|
||||||
use super::Ethash;
|
use error::{BlockError, Error};
|
||||||
|
use header::Header;
|
||||||
|
use super::super::{new_morden, new_homestead_test};
|
||||||
|
use super::{Ethash, EthashParams};
|
||||||
use rlp;
|
use rlp;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -584,5 +650,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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,14 @@ pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/fro
|
|||||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||||
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||||
|
|
||||||
|
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
||||||
|
pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
||||||
|
|
||||||
|
/// Create a new 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_daohardfork_test() -> Spec { load(include_bytes!("../../res/ethereum/daohardfork_test.json")) }
|
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||||
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||||
@ -62,10 +68,11 @@ pub fn new_morden() -> Spec { load(include_bytes!("../../res/ethereum/morden.jso
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common::*;
|
use util::*;
|
||||||
use state::*;
|
use state::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
|
use views::BlockView;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ensure_db_good() {
|
fn ensure_db_good() {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user