Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e810e79327 | ||
|
|
10e17a5009 | ||
|
|
96f68b5130 | ||
|
|
0c6670d918 | ||
|
|
abf60cdf31 | ||
|
|
d229fd4c95 | ||
|
|
1b36add0c6 | ||
|
|
b22ff1ad03 | ||
|
|
9f0ba42f25 | ||
|
|
a3992dd347 | ||
|
|
b94fc2ed62 | ||
|
|
7d38fcaab9 | ||
|
|
be920b490f | ||
|
|
2467408a30 | ||
|
|
a71766db8f | ||
|
|
2dc9c23d07 | ||
|
|
e152d012b4 | ||
|
|
e50874daa1 | ||
|
|
091a59ef0b | ||
|
|
a6204ab7bf | ||
|
|
28648b26cb | ||
|
|
f45fe8a1fd | ||
|
|
dee93c6ac2 | ||
|
|
81191848d7 | ||
|
|
da5b506603 | ||
|
|
84021a6148 | ||
|
|
fee9053c9a | ||
|
|
18c2a599d7 |
@@ -1,2 +0,0 @@
|
||||
[target.armv7-unknown-linux-gnueabihf]
|
||||
linker= "arm-linux-gnueabihf-gcc"
|
||||
@@ -43,6 +43,41 @@ linux-stable:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
||||
linux-stable-debian:
|
||||
stage: build
|
||||
image: ethcore/rust-debian:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- cargo build -j $(nproc) --release $CARGOFLAGS
|
||||
- strip target/release/parity
|
||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||
- md5sum target/release/parity > parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
|
||||
tags:
|
||||
- rust
|
||||
- rust-debian
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "stable-x86_64-unknown-debian-gnu_parity"
|
||||
linux-beta:
|
||||
stage: build
|
||||
image: ethcore/rust:beta
|
||||
@@ -227,41 +262,6 @@ linux-arm:
|
||||
- target/arm-unknown-linux-gnueabihf/release/parity
|
||||
name: "arm-unknown-linux-gnueabihf_parity"
|
||||
allow_failure: true
|
||||
linux-armv6:
|
||||
stage: build
|
||||
image: ethcore/rust-armv6:latest
|
||||
only:
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
- triggers
|
||||
script:
|
||||
- export CC=arm-linux-gnueabi-gcc
|
||||
- export CXX=arm-linux-gnueabi-g++
|
||||
- export HOST_CC=gcc
|
||||
- export HOST_CXX=g++
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabi --release $CARGOFLAGS
|
||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity > parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity --body target/arm-unknown-linux-gnueabi/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- target/arm-unknown-linux-gnueabi/release/parity
|
||||
name: "arm-unknown-linux-gnueabi_parity"
|
||||
allow_failure: true
|
||||
linux-aarch64:
|
||||
stage: build
|
||||
image: ethcore/rust-aarch64:latest
|
||||
@@ -399,18 +399,19 @@ windows:
|
||||
- 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.md5
|
||||
- md5sums parity.exe > parity.exe.md5
|
||||
- zip parity.zip parity.exe parity.pdb parity.md5
|
||||
- zip parity.zip parity.exe parity.md5
|
||||
- md5sums parity.zip > parity.zip.md5
|
||||
- cd ..\..
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
- aws configure set aws_secret_access_key %s3_secret%
|
||||
- echo %CI_BUILD_REF_NAME%
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "master" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "beta" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "stable" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "master" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "beta" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
|
||||
- echo %CI_BUILD_REF_NAME% | findstr /R "stable" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
|
||||
- echo %S3_BUCKET%
|
||||
- echo s3://%S3_BUCKET%/%CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc
|
||||
- aws s3 rm --recursive s3://%S3_BUCKET%/%CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
||||
@@ -425,9 +426,22 @@ windows:
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity.exe
|
||||
- target/release/parity.pdb
|
||||
- nsis/InstallParity.exe
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
docker-build:
|
||||
stage: build
|
||||
only:
|
||||
- tags
|
||||
before_script:
|
||||
- docker info
|
||||
script:
|
||||
- cd docker/hub
|
||||
- if [ "$CI_BUILD_REF_NAME" == "nightly" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
||||
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
|
||||
- docker build --no-cache=true --tag ethcore/parity:$DOCKER_TAG .
|
||||
- docker push ethcore/parity:$DOCKER_TAG
|
||||
tags:
|
||||
- docker
|
||||
test-darwin:
|
||||
stage: test
|
||||
only:
|
||||
|
||||
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
[root]
|
||||
name = "parity"
|
||||
version = "1.4.6"
|
||||
version = "1.4.12"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -21,7 +21,7 @@ dependencies = [
|
||||
"ethcore-rpc 1.4.0",
|
||||
"ethcore-signer 1.4.0",
|
||||
"ethcore-stratum 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"ethsync 1.4.0",
|
||||
"fdlimit 0.1.0",
|
||||
"hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -289,7 +289,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.4.0",
|
||||
"ethcore-ipc-codegen 1.4.0",
|
||||
"ethcore-ipc-nano 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
@@ -336,7 +336,7 @@ dependencies = [
|
||||
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-rpc 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"fetch 0.1.0",
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -381,7 +381,7 @@ name = "ethcore-ipc"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"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)",
|
||||
]
|
||||
@@ -428,7 +428,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.4.0",
|
||||
"ethcore-ipc-codegen 1.4.0",
|
||||
"ethcore-ipc-nano 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -439,7 +439,7 @@ name = "ethcore-logger"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"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)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -455,7 +455,7 @@ dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-io 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -482,7 +482,7 @@ dependencies = [
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-io 1.4.0",
|
||||
"ethcore-ipc 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
@@ -511,7 +511,7 @@ dependencies = [
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-io 1.4.0",
|
||||
"ethcore-rpc 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -530,7 +530,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.4.0",
|
||||
"ethcore-ipc-codegen 1.4.0",
|
||||
"ethcore-ipc-nano 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"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)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -541,7 +541,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.4.6"
|
||||
version = "1.4.12"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -590,7 +590,7 @@ dependencies = [
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"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_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -643,7 +643,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.4.0",
|
||||
"ethcore-ipc-nano 1.4.0",
|
||||
"ethcore-network 1.4.0",
|
||||
"ethcore-util 1.4.6",
|
||||
"ethcore-util 1.4.12",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -737,7 +737,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.9.4"
|
||||
source = "git+https://github.com/ethcore/hyper#9e346c1d4bc30cd4142dea9d8a0b117d30858ca4"
|
||||
source = "git+https://github.com/ethcore/hyper#4379a3629abc10e42e6d3fbcb21ecef5c1c86a77"
|
||||
dependencies = [
|
||||
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1249,7 +1249,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git?branch=beta#a4da6ebd3e2a02fad22ae5ce6d73ff4f0e0bbfaf"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git?branch=beta#8ac9a3a3fc5a26e7f43fd0535018cc263cdfde64"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Ethcore client."
|
||||
name = "parity"
|
||||
version = "1.4.6"
|
||||
version = "1.4.12"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
78
docker/hub/Dockerfile
Normal file
78
docker/hub/Dockerfile
Normal file
@@ -0,0 +1,78 @@
|
||||
FROM ubuntu:14.04
|
||||
MAINTAINER Parity Technologies <devops@parity.io>
|
||||
WORKDIR /build
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
make \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
g++ \
|
||||
gcc \
|
||||
libc6 \
|
||||
libc6-dev \
|
||||
binutils \
|
||||
file \
|
||||
openssl \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
dpkg-dev \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev \
|
||||
libudev-dev &&\
|
||||
# cmake and llvm ppa's. then update ppa's
|
||||
add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
|
||||
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --force-yes cmake llvm-3.7-dev && \
|
||||
# install evmjit
|
||||
git clone https://github.com/debris/evmjit && \
|
||||
cd evmjit && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make && make install && cd && \
|
||||
# install rustup
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y && \
|
||||
# rustup directory
|
||||
PATH=/root/.cargo/bin:$PATH && \
|
||||
# show backtraces
|
||||
RUST_BACKTRACE=1 && \
|
||||
# build parity
|
||||
cd /build&&git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git pull&& \
|
||||
git checkout $CI_BUILD_REF_NAME && \
|
||||
cargo build --verbose --release && \
|
||||
#ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity && \
|
||||
file /build/parity/target/release/parity&&mkdir -p /parity&& cp /build/parity/target/release/parity /parity&&\
|
||||
#cleanup Docker image
|
||||
rm -rf /root/.cargo&&rm -rf /root/.multirust&&rm -rf /root/.rustup&&rm -rf /build&&\
|
||||
apt-get purge -y \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
make \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
g++ \
|
||||
gcc \
|
||||
binutils \
|
||||
file \
|
||||
pkg-config \
|
||||
dpkg-dev \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev \
|
||||
cmake llvm-3.7-dev&&\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
# setup ENTRYPOINT
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/parity/parity"]
|
||||
@@ -10,10 +10,13 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"eip150Transition": "0x2625a0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"homesteadTransition": 1150000,
|
||||
"eip150Transition": 2500000,
|
||||
"eip155Transition": 3000000,
|
||||
"eip160Transition": 3000000,
|
||||
"ecip1010PauseTransition": 3000000,
|
||||
"ecip1010ContinueTransition": 5000000,
|
||||
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
@@ -24,6 +27,7 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"chainID": "0x3d",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
|
||||
},
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
||||
"homesteadTransition": "0x789b0",
|
||||
"eip150Transition": "0x1b34d8",
|
||||
"eip155Transition": 1885000,
|
||||
"eip160Transition": 1885000,
|
||||
"eip161abcTransition": 1885000,
|
||||
"eip161dTransition": 1885000
|
||||
"homesteadTransition": 494000,
|
||||
"eip150Transition": 1783000,
|
||||
"eip155Transition": 1915000,
|
||||
"eip160Transition": 1915000,
|
||||
"ecip1010PauseTransition": 1915000,
|
||||
"ecip1010ContinueTransition": 3415000,
|
||||
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23,6 +26,7 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2",
|
||||
"chainID": "0x3e",
|
||||
"forkBlock": "0x1b34d8",
|
||||
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
|
||||
},
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"eip155Transition": 10,
|
||||
"eip160Transition": 10,
|
||||
"eip161abcTransition": 10,
|
||||
"eip161dTransition": 10
|
||||
"eip161dTransition": 10,
|
||||
"maxCodeSize": 24576
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -266,7 +266,8 @@ impl<'x> OpenBlock<'x> {
|
||||
r.block.base.header.set_extra_data(extra_data);
|
||||
r.block.base.header.note_dirty();
|
||||
|
||||
engine.populate_from_parent(&mut r.block.base.header, parent, gas_range_target.0, gas_range_target.1);
|
||||
let gas_floor_target = ::std::cmp::max(gas_range_target.0, engine.params().min_gas_limit);
|
||||
engine.populate_from_parent(&mut r.block.base.header, parent, gas_floor_target, gas_range_target.1);
|
||||
engine.on_new_block(&mut r.block);
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ pub trait BlockProvider {
|
||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
|
||||
|
||||
/// Returns logs matching given filter.
|
||||
fn logs<F>(&self, mut blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
fn logs<F>(&self, blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
where F: Fn(&LogEntry) -> bool, Self: Sized;
|
||||
}
|
||||
|
||||
@@ -371,7 +371,8 @@ impl BlockProvider for BlockChain {
|
||||
.enumerate()
|
||||
.flat_map(move |(index, (mut logs, tx_hash))| {
|
||||
let current_log_index = log_index;
|
||||
log_index -= logs.len();
|
||||
let no_of_logs = logs.len();
|
||||
log_index -= no_of_logs;
|
||||
|
||||
logs.reverse();
|
||||
logs.into_iter()
|
||||
@@ -383,6 +384,7 @@ impl BlockProvider for BlockChain {
|
||||
transaction_hash: tx_hash,
|
||||
// iterating in reverse order
|
||||
transaction_index: receipts_len - index - 1,
|
||||
transaction_log_index: no_of_logs - i - 1,
|
||||
log_index: current_log_index - i - 1,
|
||||
})
|
||||
})
|
||||
@@ -1924,6 +1926,7 @@ mod tests {
|
||||
block_number: block1.header().number(),
|
||||
transaction_hash: tx_hash1.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
},
|
||||
LocalizedLogEntry {
|
||||
@@ -1932,6 +1935,7 @@ mod tests {
|
||||
block_number: block1.header().number(),
|
||||
transaction_hash: tx_hash1.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 1,
|
||||
log_index: 1,
|
||||
},
|
||||
LocalizedLogEntry {
|
||||
@@ -1940,6 +1944,7 @@ mod tests {
|
||||
block_number: block1.header().number(),
|
||||
transaction_hash: tx_hash2.clone(),
|
||||
transaction_index: 1,
|
||||
transaction_log_index: 0,
|
||||
log_index: 2,
|
||||
},
|
||||
LocalizedLogEntry {
|
||||
@@ -1948,6 +1953,7 @@ mod tests {
|
||||
block_number: block2.header().number(),
|
||||
transaction_hash: tx_hash3.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
}
|
||||
]);
|
||||
@@ -1958,6 +1964,7 @@ mod tests {
|
||||
block_number: block2.header().number(),
|
||||
transaction_hash: tx_hash3.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -40,6 +40,14 @@ pub trait ChainNotify : Send + Sync {
|
||||
fn stop(&self) {
|
||||
// does nothing by default
|
||||
}
|
||||
|
||||
/// fires when new transactions are received from a peer
|
||||
fn transactions_received(&self,
|
||||
_hashes: Vec<H256>,
|
||||
_peer_id: usize,
|
||||
) {
|
||||
// does nothing by default
|
||||
}
|
||||
}
|
||||
|
||||
impl IpcConfig for ChainNotify { }
|
||||
|
||||
@@ -46,6 +46,7 @@ use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
||||
use blockchain::extras::TransactionAddress;
|
||||
use types::filter::Filter;
|
||||
use types::mode::Mode as IpcMode;
|
||||
use types::pruning_info::PruningInfo;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use verification::queue::BlockQueue;
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||
@@ -57,7 +58,7 @@ use client::{
|
||||
use client::Error as ClientError;
|
||||
use env_info::EnvInfo;
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
use receipt::LocalizedReceipt;
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||
use trace;
|
||||
use trace::FlatTransactionTraces;
|
||||
@@ -262,7 +263,7 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// Register an action to be done if a mode change happens.
|
||||
/// 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));
|
||||
}
|
||||
@@ -553,10 +554,15 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Import transactions from the IO queue
|
||||
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
|
||||
pub fn import_queued_transactions(&self, transactions: &[Bytes], peer_id: usize) -> usize {
|
||||
trace!(target: "external_tx", "Importing queued");
|
||||
let _timer = PerfTimer::new("import_queued_transactions");
|
||||
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: Vec<SignedTransaction> = transactions.iter().filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok()).collect();
|
||||
let hashes: Vec<_> = txs.iter().map(|tx| tx.hash()).collect();
|
||||
self.notify(|notify| {
|
||||
notify.transactions_received(hashes.clone(), peer_id);
|
||||
});
|
||||
let results = self.miner.import_external_transactions(self, txs);
|
||||
results.len()
|
||||
}
|
||||
@@ -786,7 +792,6 @@ impl snapshot::DatabaseRestore for Client {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl BlockChainClient for Client {
|
||||
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
|
||||
@@ -890,7 +895,7 @@ impl BlockChainClient for Client {
|
||||
trace!(target: "mode", "Making callback...");
|
||||
f(&*mode)
|
||||
},
|
||||
_ => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
match new_mode {
|
||||
@@ -1011,53 +1016,23 @@ impl BlockChainClient for Client {
|
||||
let chain = self.chain.read();
|
||||
self.transaction_address(id)
|
||||
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
|
||||
let t = chain.block_body(&address.block_hash)
|
||||
.and_then(|block| {
|
||||
BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
|
||||
});
|
||||
let transaction = chain.block_body(&address.block_hash)
|
||||
.and_then(|body| BodyView::new(&body).localized_transaction_at(&address.block_hash, block_number, address.index));
|
||||
|
||||
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
|
||||
|
||||
match (tx_and_sender, chain.transaction_receipt(&address)) {
|
||||
(Some((tx, sender)), Some(receipt)) => {
|
||||
let block_hash = tx.block_hash.clone();
|
||||
let block_number = tx.block_number.clone();
|
||||
let transaction_hash = tx.hash();
|
||||
let transaction_index = tx.transaction_index;
|
||||
let prior_gas_used = match tx.transaction_index {
|
||||
0 => U256::zero(),
|
||||
i => {
|
||||
let prior_address = TransactionAddress { block_hash: address.block_hash, index: i - 1 };
|
||||
let prior_receipt = chain.transaction_receipt(&prior_address).expect("Transaction receipt at `address` exists; `prior_address` has lower index in same block; qed");
|
||||
prior_receipt.gas_used
|
||||
}
|
||||
};
|
||||
Some(LocalizedReceipt {
|
||||
transaction_hash: tx.hash(),
|
||||
transaction_index: tx.transaction_index,
|
||||
block_hash: tx.block_hash,
|
||||
block_number: tx.block_number,
|
||||
cumulative_gas_used: receipt.gas_used,
|
||||
gas_used: receipt.gas_used - prior_gas_used,
|
||||
contract_address: match tx.action {
|
||||
Action::Call(_) => None,
|
||||
Action::Create => Some(contract_address(&sender, &tx.nonce))
|
||||
},
|
||||
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
block_hash: block_hash.clone(),
|
||||
block_number: block_number,
|
||||
transaction_hash: transaction_hash.clone(),
|
||||
transaction_index: transaction_index,
|
||||
log_index: i
|
||||
}).collect(),
|
||||
log_bloom: receipt.log_bloom,
|
||||
state_root: receipt.state_root,
|
||||
let previous_receipts = (0..address.index + 1)
|
||||
.map(|index| {
|
||||
let mut address = address.clone();
|
||||
address.index = index;
|
||||
chain.transaction_receipt(&address)
|
||||
})
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
match (transaction, previous_receipts) {
|
||||
(Some(transaction), Some(previous_receipts)) => {
|
||||
Some(transaction_receipt(transaction, previous_receipts))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||
@@ -1081,7 +1056,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
use verification::queue::kind::HasHash;
|
||||
use verification::queue::kind::BlockLike;
|
||||
use verification::queue::kind::blocks::Unverified;
|
||||
|
||||
// create unverified block here so the `sha3` calculation can be cached.
|
||||
@@ -1121,7 +1096,9 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo {
|
||||
self.chain.read().chain_info()
|
||||
let mut chain_info = self.chain.read().chain_info();
|
||||
chain_info.pending_total_difficulty = chain_info.total_difficulty + self.block_queue.total_difficulty();
|
||||
chain_info
|
||||
}
|
||||
|
||||
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||
@@ -1192,12 +1169,14 @@ impl BlockChainClient for Client {
|
||||
(*self.build_last_hashes(self.chain.read().best_block_hash())).clone()
|
||||
}
|
||||
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>) {
|
||||
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>, peer_id: usize) {
|
||||
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());
|
||||
} else {
|
||||
let len = transactions.len();
|
||||
match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions)) {
|
||||
match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions, peer_id)) {
|
||||
Ok(_) => {
|
||||
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
|
||||
}
|
||||
@@ -1212,7 +1191,7 @@ impl BlockChainClient for Client {
|
||||
self.miner.pending_transactions(self.chain.read().best_block_number())
|
||||
}
|
||||
|
||||
fn signing_network_id(&self) -> Option<u8> {
|
||||
fn signing_network_id(&self) -> Option<u64> {
|
||||
self.engine.signing_network_id(&self.latest_env_info())
|
||||
}
|
||||
|
||||
@@ -1226,6 +1205,12 @@ impl BlockChainClient for Client {
|
||||
self.uncle(id)
|
||||
.map(|header| self.engine.extra_info(&decode(&header)))
|
||||
}
|
||||
|
||||
fn pruning_info(&self) -> PruningInfo {
|
||||
PruningInfo {
|
||||
history_size: Some(self.history),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for Client {
|
||||
@@ -1310,32 +1295,169 @@ impl MayPanic for Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
|
||||
/// and a vector of receipts from given block up to transaction index.
|
||||
fn transaction_receipt(tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
|
||||
assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");
|
||||
|
||||
#[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
|
||||
let sender = tx.sender()
|
||||
.expect("LocalizedTransaction is part of the blockchain; We have only valid transactions in chain; qed");
|
||||
let receipt = receipts.pop().expect("Current receipt is provided; qed");
|
||||
let prior_gas_used = match tx.transaction_index {
|
||||
0 => 0.into(),
|
||||
i => receipts.get(i - 1).expect("All previous receipts are provided; qed").gas_used,
|
||||
};
|
||||
let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::<usize>();
|
||||
let transaction_hash = tx.hash();
|
||||
let block_hash = tx.block_hash;
|
||||
let block_number = tx.block_number;
|
||||
let transaction_index = tx.transaction_index;
|
||||
|
||||
while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); }
|
||||
|
||||
assert!(client.tree_route(&genesis, &new_hash).is_none());
|
||||
LocalizedReceipt {
|
||||
transaction_hash: transaction_hash,
|
||||
transaction_index: transaction_index,
|
||||
block_hash: block_hash,
|
||||
block_number:block_number,
|
||||
cumulative_gas_used: receipt.gas_used,
|
||||
gas_used: receipt.gas_used - prior_gas_used,
|
||||
contract_address: match tx.action {
|
||||
Action::Call(_) => None,
|
||||
Action::Create => Some(contract_address(&sender, &tx.nonce))
|
||||
},
|
||||
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
transaction_hash: transaction_hash,
|
||||
transaction_index: transaction_index,
|
||||
transaction_log_index: i,
|
||||
log_index: no_of_logs + i,
|
||||
}).collect(),
|
||||
log_bloom: receipt.log_bloom,
|
||||
state_root: receipt.state_root,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_not_cache_details_before_commit() {
|
||||
use client::BlockChainClient;
|
||||
use tests::helpers::*;
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use util::kvdb::DBTransaction;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_correct_log_index() {
|
||||
use super::transaction_receipt;
|
||||
use ethkey::KeyPair;
|
||||
use log_entry::{LogEntry, LocalizedLogEntry};
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use transaction::{Transaction, LocalizedTransaction, Action};
|
||||
use util::Hashable;
|
||||
|
||||
// given
|
||||
let key = KeyPair::from_secret("test".sha3()).unwrap();
|
||||
let secret = key.secret();
|
||||
|
||||
let block_number = 1;
|
||||
let block_hash = 5.into();
|
||||
let state_root = 99.into();
|
||||
let gas_used = 10.into();
|
||||
let raw_tx = Transaction {
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 21000.into(),
|
||||
action: Action::Call(10.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
};
|
||||
let tx1 = raw_tx.clone().sign(secret, None);
|
||||
let transaction = LocalizedTransaction {
|
||||
signed: tx1.clone(),
|
||||
block_number: block_number,
|
||||
block_hash: block_hash,
|
||||
transaction_index: 1,
|
||||
};
|
||||
let logs = vec![LogEntry {
|
||||
address: 5.into(),
|
||||
topics: vec![],
|
||||
data: vec![],
|
||||
}, LogEntry {
|
||||
address: 15.into(),
|
||||
topics: vec![],
|
||||
data: vec![],
|
||||
}];
|
||||
let receipts = vec![Receipt {
|
||||
state_root: state_root,
|
||||
gas_used: 5.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![logs[0].clone()],
|
||||
}, Receipt {
|
||||
state_root: state_root,
|
||||
gas_used: gas_used,
|
||||
log_bloom: Default::default(),
|
||||
logs: logs.clone(),
|
||||
}];
|
||||
|
||||
// when
|
||||
let receipt = transaction_receipt(transaction, receipts);
|
||||
|
||||
// then
|
||||
assert_eq!(receipt, LocalizedReceipt {
|
||||
transaction_hash: tx1.hash(),
|
||||
transaction_index: 1,
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
cumulative_gas_used: gas_used,
|
||||
gas_used: gas_used - 5.into(),
|
||||
contract_address: None,
|
||||
logs: vec![LocalizedLogEntry {
|
||||
entry: logs[0].clone(),
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
transaction_hash: tx1.hash(),
|
||||
transaction_index: 1,
|
||||
transaction_log_index: 0,
|
||||
log_index: 1,
|
||||
}, LocalizedLogEntry {
|
||||
entry: logs[1].clone(),
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
transaction_hash: tx1.hash(),
|
||||
transaction_index: 1,
|
||||
transaction_log_index: 1,
|
||||
log_index: 2,
|
||||
}],
|
||||
log_bloom: Default::default(),
|
||||
state_root: state_root,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ use evm::{Factory as EvmFactory, VMType, Schedule};
|
||||
use miner::{Miner, MinerService, TransactionImportResult};
|
||||
use spec::Spec;
|
||||
use types::mode::Mode;
|
||||
use types::pruning_info::PruningInfo;
|
||||
use views::BlockView;
|
||||
|
||||
use verification::queue::QueueInfo;
|
||||
@@ -89,6 +90,8 @@ pub struct TestBlockChainClient {
|
||||
pub ancient_block: RwLock<Option<(H256, u64)>>,
|
||||
/// First block info.
|
||||
pub first_block: RwLock<Option<(H256, u64)>>,
|
||||
/// Pruning history size to report.
|
||||
pub history: RwLock<Option<u64>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -140,6 +143,7 @@ impl TestBlockChainClient {
|
||||
latest_block_timestamp: RwLock::new(10_000_000),
|
||||
ancient_block: RwLock::new(None),
|
||||
first_block: RwLock::new(None),
|
||||
history: RwLock::new(None),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
@@ -300,6 +304,11 @@ impl TestBlockChainClient {
|
||||
let res = res.into_iter().next().unwrap().expect("Successful import");
|
||||
assert_eq!(res, TransactionImportResult::Current);
|
||||
}
|
||||
|
||||
/// Set reported history size.
|
||||
pub fn set_history(&self, h: Option<u64>) {
|
||||
*self.history.write() = h;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||
@@ -635,7 +644,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>) {
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>, _peer_id: usize) {
|
||||
// import right here
|
||||
let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
|
||||
self.miner.import_external_transactions(self, txs);
|
||||
@@ -645,9 +654,15 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
|
||||
fn signing_network_id(&self) -> Option<u8> { None }
|
||||
fn signing_network_id(&self) -> Option<u64> { None }
|
||||
|
||||
fn mode(&self) -> Mode { Mode::Active }
|
||||
|
||||
fn set_mode(&self, _: Mode) { unimplemented!(); }
|
||||
|
||||
fn pruning_info(&self) -> PruningInfo {
|
||||
PruningInfo {
|
||||
history_size: *self.history.read(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ use types::call_analytics::CallAnalytics;
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::block_status::BlockStatus;
|
||||
use types::mode::Mode;
|
||||
use types::pruning_info::PruningInfo;
|
||||
|
||||
#[ipc(client_ident="RemoteClient")]
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
@@ -187,7 +188,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn last_hashes(&self) -> LastHashes;
|
||||
|
||||
/// Queue transactions for importing.
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>);
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>, peer_id: usize);
|
||||
|
||||
/// list all transactions
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
@@ -228,7 +229,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
}
|
||||
|
||||
/// Get the preferred network ID to sign on
|
||||
fn signing_network_id(&self) -> Option<u8>;
|
||||
fn signing_network_id(&self) -> Option<u64>;
|
||||
|
||||
/// Get the mode.
|
||||
fn mode(&self) -> Mode;
|
||||
@@ -241,6 +242,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
|
||||
/// Returns engine-related extra info for `UncleID`.
|
||||
fn uncle_extra_info(&self, id: UncleID) -> Option<BTreeMap<String, String>>;
|
||||
|
||||
/// Get pruning settings.
|
||||
fn pruning_info(&self) -> PruningInfo;
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
@@ -263,3 +267,4 @@ pub trait MiningBlockChainClient : BlockChainClient {
|
||||
}
|
||||
|
||||
impl IpcConfig for BlockChainClient { }
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ pub trait Engine : Sync + Send {
|
||||
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 }
|
||||
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> { None }
|
||||
|
||||
/// 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
|
||||
@@ -118,8 +118,9 @@ pub trait Engine : Sync + Send {
|
||||
self.verify_block_basic(header, None).and_then(|_| self.verify_block_unordered(header, None))
|
||||
}
|
||||
|
||||
/// Don't forget to call Super::populate_from_parent when subclassing & overriding.
|
||||
// TODO: consider including State in the params.
|
||||
/// Populate a header's fields based on its parent's header.
|
||||
/// Takes gas floor and ceiling targets.
|
||||
/// The gas floor target must not be lower than the engine's minimum gas limit.
|
||||
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(parent.gas_limit().clone());
|
||||
|
||||
@@ -29,6 +29,9 @@ use evm::Schedule;
|
||||
use ethjson;
|
||||
use rlp::{self, UntrustedRlp, View};
|
||||
|
||||
/// Parity tries to round block.gas_limit to multiple of this constant
|
||||
pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]);
|
||||
|
||||
/// Ethash params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct EthashParams {
|
||||
@@ -70,6 +73,10 @@ pub struct EthashParams {
|
||||
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,
|
||||
}
|
||||
@@ -96,6 +103,8 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
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(0x7fffffffffffffff, Into::into),
|
||||
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
|
||||
}
|
||||
}
|
||||
@@ -157,9 +166,9 @@ impl Engine for Ethash {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
fn signing_network_id(&self, env_info: &EnvInfo) -> Option<u64> {
|
||||
if env_info.number >= self.ethash_params.eip155_transition {
|
||||
Some(self.params().chain_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -170,16 +179,25 @@ impl Engine for Ethash {
|
||||
let gas_limit = {
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
let lower_limit = gas_limit - gas_limit / bound_divisor + 1.into();
|
||||
let upper_limit = gas_limit + gas_limit / bound_divisor - 1.into();
|
||||
let gas_limit = if gas_limit < gas_floor_target {
|
||||
let gas_limit = min(gas_floor_target, upper_limit);
|
||||
round_block_gas_limit(gas_limit, lower_limit, upper_limit)
|
||||
} else if gas_limit > gas_ceil_target {
|
||||
max(gas_ceil_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||
let gas_limit = max(gas_ceil_target, lower_limit);
|
||||
round_block_gas_limit(gas_limit, lower_limit, upper_limit)
|
||||
} else {
|
||||
min(gas_ceil_target,
|
||||
max(gas_floor_target,
|
||||
gas_limit - gas_limit / bound_divisor + 1.into() +
|
||||
(header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor))
|
||||
}
|
||||
let total_lower_limit = max(lower_limit, gas_floor_target);
|
||||
let total_upper_limit = min(upper_limit, gas_ceil_target);
|
||||
let gas_limit = max(gas_floor_target, min(total_upper_limit,
|
||||
lower_limit + (header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor));
|
||||
round_block_gas_limit(gas_limit, total_lower_limit, total_upper_limit)
|
||||
};
|
||||
// ensure that we are not violating protocol limits
|
||||
debug_assert!(gas_limit >= lower_limit);
|
||||
debug_assert!(gas_limit <= upper_limit);
|
||||
gas_limit
|
||||
};
|
||||
header.set_difficulty(difficulty);
|
||||
header.set_gas_limit(gas_limit);
|
||||
@@ -308,7 +326,7 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if header.number() < self.ethash_params.eip155_transition || n as usize != self.params().network_id {
|
||||
if header.number() < self.ethash_params.eip155_transition || n != self.params().chain_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into())
|
||||
}
|
||||
}
|
||||
@@ -321,7 +339,24 @@ impl Engine for Ethash {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
|
||||
// Try to round gas_limit a bit so that:
|
||||
// 1) it will still be in desired range
|
||||
// 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT
|
||||
fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) -> U256 {
|
||||
let increased_gas_limit = gas_limit + (PARITY_GAS_LIMIT_DETERMINANT - gas_limit % PARITY_GAS_LIMIT_DETERMINANT);
|
||||
if increased_gas_limit > upper_limit {
|
||||
let decreased_gas_limit = increased_gas_limit - PARITY_GAS_LIMIT_DETERMINANT;
|
||||
if decreased_gas_limit < lower_limit {
|
||||
gas_limit
|
||||
} else {
|
||||
decreased_gas_limit
|
||||
}
|
||||
} else {
|
||||
increased_gas_limit
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
|
||||
impl Ethash {
|
||||
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
|
||||
const EXP_DIFF_PERIOD: u64 = 100000;
|
||||
@@ -357,9 +392,20 @@ impl Ethash {
|
||||
};
|
||||
target = max(min_difficulty, target);
|
||||
if header.number() < self.ethash_params.bomb_defuse_transition {
|
||||
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
|
||||
if period > 1 {
|
||||
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
|
||||
if header.number() < self.ethash_params.ecip1010_pause_transition {
|
||||
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
|
||||
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
|
||||
@@ -415,11 +461,12 @@ mod tests {
|
||||
use util::*;
|
||||
use block::*;
|
||||
use tests::helpers::*;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use error::{BlockError, Error};
|
||||
use header::Header;
|
||||
use super::super::new_morden;
|
||||
use super::Ethash;
|
||||
use super::super::{new_morden, new_homestead_test};
|
||||
use super::{Ethash, EthashParams, PARITY_GAS_LIMIT_DETERMINANT};
|
||||
use rlp;
|
||||
|
||||
#[test]
|
||||
@@ -641,5 +688,183 @@ mod tests {
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gas_limit_is_multiple_of_determinant() {
|
||||
let spec = new_homestead_test();
|
||||
let ethash = Ethash::new(spec.params, get_default_ethash_params(), BTreeMap::new());
|
||||
let mut parent = Header::new();
|
||||
let mut header = Header::new();
|
||||
header.set_number(1);
|
||||
|
||||
// this test will work for this constant only
|
||||
assert_eq!(PARITY_GAS_LIMIT_DETERMINANT, U256::from(37));
|
||||
|
||||
// when parent.gas_limit < gas_floor_target:
|
||||
parent.set_gas_limit(U256::from(50_000));
|
||||
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000));
|
||||
assert_eq!(*header.gas_limit(), U256::from(50_024));
|
||||
|
||||
// when parent.gas_limit > gas_ceil_target:
|
||||
parent.set_gas_limit(U256::from(250_000));
|
||||
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000));
|
||||
assert_eq!(*header.gas_limit(), U256::from(249_787));
|
||||
|
||||
// when parent.gas_limit is in miner's range
|
||||
header.set_gas_used(U256::from(150_000));
|
||||
parent.set_gas_limit(U256::from(150_000));
|
||||
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000));
|
||||
assert_eq!(*header.gas_limit(), U256::from(150_035));
|
||||
|
||||
// when parent.gas_limit is in miner's range
|
||||
// && we can NOT increase it to be multiple of constant
|
||||
header.set_gas_used(U256::from(150_000));
|
||||
parent.set_gas_limit(U256::from(150_000));
|
||||
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002));
|
||||
assert_eq!(*header.gas_limit(), U256::from(149_998));
|
||||
|
||||
// when parent.gas_limit is in miner's range
|
||||
// && we can NOT increase it to be multiple of constant
|
||||
// && we can NOT decrease it to be multiple of constant
|
||||
header.set_gas_used(U256::from(150_000));
|
||||
parent.set_gas_limit(U256::from(150_000));
|
||||
ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002));
|
||||
assert_eq!(*header.gas_limit(), U256::from(150_002));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn difficulty_max_timestamp() {
|
||||
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(u64::max_value());
|
||||
|
||||
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
|
||||
assert_eq!(U256::from(12543204905719u64), difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ impl Decodable for Header {
|
||||
number: try!(r.val_at(8)),
|
||||
gas_limit: try!(r.val_at(9)),
|
||||
gas_used: try!(r.val_at(10)),
|
||||
timestamp: try!(r.val_at(11)),
|
||||
timestamp: min(try!(r.val_at::<U256>(11)), u64::max_value().into()).as_u64(),
|
||||
extra_data: try!(r.val_at(12)),
|
||||
seal: vec![],
|
||||
hash: RefCell::new(Some(r.as_raw().sha3())),
|
||||
|
||||
@@ -150,7 +150,7 @@ impl GasPriceCalibrator {
|
||||
if Instant::now() >= self.next_calibration {
|
||||
let usd_per_tx = self.options.usd_per_tx;
|
||||
trace!(target: "miner", "Getting price info");
|
||||
if let Ok(_) = PriceInfo::get(move |price: PriceInfo| {
|
||||
PriceInfo::get(move |price: PriceInfo| {
|
||||
trace!(target: "miner", "Price info arrived: {:?}", price);
|
||||
let usd_per_eth = price.ethusd;
|
||||
let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
|
||||
@@ -158,11 +158,9 @@ impl GasPriceCalibrator {
|
||||
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
|
||||
info!(target: "miner", "Updated conversion rate to Ξ1 = {} ({} wei/gas)", Colour::White.bold().paint(format!("US${}", usd_per_eth)), Colour::Yellow.bold().paint(format!("{}", wei_per_gas)));
|
||||
set_price(U256::from(wei_per_gas as u64));
|
||||
}) {
|
||||
self.next_calibration = Instant::now() + self.options.recalibration_period;
|
||||
} else {
|
||||
warn!(target: "miner", "Unable to update Ether price.");
|
||||
}
|
||||
});
|
||||
|
||||
self.next_calibration = Instant::now() + self.options.recalibration_period;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::time::Duration;
|
||||
use std::str::FromStr;
|
||||
use std::sync::mpsc;
|
||||
use hyper::client::{Handler, Request, Response, Client};
|
||||
use hyper::{Next, Encoder, Decoder};
|
||||
use hyper::{Url, Next, Encoder, Decoder};
|
||||
use hyper::net::HttpStream;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -29,12 +29,12 @@ pub struct PriceInfo {
|
||||
pub ethusd: f32,
|
||||
}
|
||||
|
||||
pub struct SetPriceHandler<F: Fn(PriceInfo) + Sync + Send + 'static> {
|
||||
pub struct SetPriceHandler<F> {
|
||||
set_price: F,
|
||||
channel: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
impl<F: Fn(PriceInfo) + Sync + Send + 'static> Drop for SetPriceHandler<F> {
|
||||
impl<F> Drop for SetPriceHandler<F> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.channel.send(());
|
||||
}
|
||||
@@ -47,37 +47,61 @@ impl<F: Fn(PriceInfo) + Sync + Send + 'static> Handler<HttpStream> for SetPriceH
|
||||
|
||||
fn on_response_readable(&mut self, r: &mut Decoder<HttpStream>) -> Next {
|
||||
let mut body = String::new();
|
||||
let _ = r.read_to_string(&mut body).ok()
|
||||
.and_then(|_| Json::from_str(&body).ok())
|
||||
.and_then(|json| json.find_path(&["result", "ethusd"])
|
||||
.and_then(|obj| match *obj {
|
||||
Json::String(ref s) => Some((self.set_price)(PriceInfo {
|
||||
ethusd: FromStr::from_str(s)
|
||||
.expect("Etherscan API will always return properly formatted price; qed")
|
||||
})),
|
||||
_ => None,
|
||||
}));
|
||||
let info = r.read_to_string(&mut body)
|
||||
.map_err(|e| format!("Unable to read response: {:?}", e))
|
||||
.and_then(|_| self.process_response(&body));
|
||||
|
||||
if let Err(e) = info {
|
||||
warn!("Failed to auto-update latest ETH price: {:?}", e);
|
||||
}
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(PriceInfo) + Sync + Send + 'static> SetPriceHandler<F> {
|
||||
fn process_response(&self, body: &str) -> Result<(), String> {
|
||||
let json = Json::from_str(body).map_err(|e| format!("Invalid JSON returned: {:?}", e))?;
|
||||
let obj = json.find_path(&["result", "ethusd"]).ok_or("USD price not found".to_owned())?;
|
||||
let ethusd = match *obj {
|
||||
Json::String(ref s) => FromStr::from_str(s).ok(),
|
||||
_ => None,
|
||||
}.ok_or("Unexpected price format.".to_owned())?;
|
||||
|
||||
(self.set_price)(PriceInfo {
|
||||
ethusd: ethusd,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PriceInfo {
|
||||
pub fn get<F: Fn(PriceInfo) + Sync + Send + 'static>(set_price: F) -> Result<(), ()> {
|
||||
// TODO: Handle each error type properly
|
||||
let client = try!(Client::new().map_err(|_| ()));
|
||||
pub fn get<F: Fn(PriceInfo) + Sync + Send + 'static>(set_price: F) {
|
||||
thread::spawn(move || {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let url = FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice")
|
||||
.expect("string known to be a valid URL; qed");
|
||||
let _ = client.request(
|
||||
url,
|
||||
SetPriceHandler {
|
||||
set_price: set_price,
|
||||
channel: tx,
|
||||
}).ok().and_then(|_| rx.recv().ok());
|
||||
client.close();
|
||||
|
||||
if let Err(e) = Self::request(url, set_price) {
|
||||
warn!("Failed to auto-update latest ETH price: {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn request<F: Fn(PriceInfo) + Send + Sync + 'static>(url: Url, set_price: F) -> Result<(), String> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let client = Client::new().map_err(|e| format!("Unable to start client: {:?}", e))?;
|
||||
|
||||
client.request(
|
||||
url,
|
||||
SetPriceHandler {
|
||||
set_price: set_price,
|
||||
channel: tx,
|
||||
},
|
||||
).map_err(|_| "Request failed.".to_owned())?;
|
||||
|
||||
// Wait for exit
|
||||
let _ = rx.recv().map_err(|e| format!("Request interrupted: {:?}", e))?;
|
||||
client.close();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -93,7 +117,7 @@ fn should_get_price_info() {
|
||||
let done = Arc::new((Mutex::new(PriceInfo { ethusd: 0f32 }), Condvar::new()));
|
||||
let rdone = done.clone();
|
||||
|
||||
PriceInfo::get(move |price| { let mut p = rdone.0.lock(); *p = price; rdone.1.notify_one(); }).unwrap();
|
||||
PriceInfo::get(move |price| { let mut p = rdone.0.lock(); *p = price; rdone.1.notify_one(); });
|
||||
let mut p = done.0.lock();
|
||||
let t = done.1.wait_for(&mut p, Duration::from_millis(10000));
|
||||
assert!(!t.timed_out());
|
||||
|
||||
@@ -39,7 +39,7 @@ pub enum ClientIoMessage {
|
||||
/// A block is ready
|
||||
BlockVerified,
|
||||
/// New transaction RLPs are ready to be imported
|
||||
NewTransactions(Vec<Bytes>),
|
||||
NewTransactions(Vec<Bytes>, usize),
|
||||
/// Begin snapshot restoration
|
||||
BeginRestoration(ManifestData),
|
||||
/// Feed a state chunk to the snapshot service
|
||||
@@ -192,7 +192,9 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
|
||||
|
||||
match *net_message {
|
||||
ClientIoMessage::BlockVerified => { self.client.import_verified_blocks(); }
|
||||
ClientIoMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(transactions); }
|
||||
ClientIoMessage::NewTransactions(ref transactions, peer_id) => {
|
||||
self.client.import_queued_transactions(transactions, peer_id);
|
||||
}
|
||||
ClientIoMessage::BeginRestoration(ref manifest) => {
|
||||
if let Err(e) = self.snapshot.init_restore(manifest.clone(), true) {
|
||||
warn!("Failed to initialize snapshot restoration: {}", e);
|
||||
|
||||
@@ -38,7 +38,9 @@ pub struct CommonParams {
|
||||
/// Maximum size of extra data.
|
||||
pub maximum_extra_data_size: usize,
|
||||
/// Network id.
|
||||
pub network_id: usize,
|
||||
pub network_id: u64,
|
||||
/// Chain id.
|
||||
pub chain_id: u64,
|
||||
/// Main subprotocol name.
|
||||
pub subprotocol_name: String,
|
||||
/// Minimum gas limit.
|
||||
@@ -53,6 +55,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
account_start_nonce: p.account_start_nonce.into(),
|
||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||
network_id: p.network_id.into(),
|
||||
chain_id: if let Some(n) = p.chain_id { n.into() } else { p.network_id.into() },
|
||||
subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()),
|
||||
min_gas_limit: p.min_gas_limit.into(),
|
||||
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
|
||||
@@ -160,7 +163,7 @@ impl Spec {
|
||||
pub fn nodes(&self) -> &[String] { &self.nodes }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn network_id(&self) -> usize { self.params.network_id }
|
||||
pub fn network_id(&self) -> u64 { self.params.network_id }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn subprotocol_name(&self) -> String { self.params.subprotocol_name.clone() }
|
||||
|
||||
@@ -28,6 +28,7 @@ use evm::Schedule;
|
||||
use engines::Engine;
|
||||
use env_info::EnvInfo;
|
||||
use ethereum;
|
||||
use ethereum::ethash::EthashParams;
|
||||
use devtools::*;
|
||||
use miner::Miner;
|
||||
use header::Header;
|
||||
@@ -421,3 +422,30 @@ pub fn get_bad_state_dummy_block() -> Bytes {
|
||||
|
||||
create_test_block(&block_header)
|
||||
}
|
||||
|
||||
pub fn get_default_ethash_params() -> EthashParams {
|
||||
EthashParams {
|
||||
gas_limit_bound_divisor: U256::from(1024),
|
||||
minimum_difficulty: U256::from(131072),
|
||||
difficulty_bound_divisor: U256::from(2048),
|
||||
difficulty_increment_divisor: 10,
|
||||
duration_limit: 13,
|
||||
block_reward: U256::from(0),
|
||||
registrar: "0000000000000000000000000000000000000001".into(),
|
||||
homestead_transition: 1150000,
|
||||
dao_hardfork_transition: 0x7fffffffffffffff,
|
||||
dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(),
|
||||
dao_hardfork_accounts: vec![],
|
||||
difficulty_hardfork_transition: 0x7fffffffffffffff,
|
||||
difficulty_hardfork_bound_divisor: U256::from(0),
|
||||
bomb_defuse_transition: 0x7fffffffffffffff,
|
||||
eip150_transition: 0x7fffffffffffffff,
|
||||
eip155_transition: 0x7fffffffffffffff,
|
||||
eip160_transition: 0x7fffffffffffffff,
|
||||
eip161abc_transition: 0x7fffffffffffffff,
|
||||
eip161d_transition: 0x7fffffffffffffff,
|
||||
ecip1010_pause_transition: 0x7fffffffffffffff,
|
||||
ecip1010_continue_transition: 0x7fffffffffffffff,
|
||||
max_code_size: 0x7fffffffffffffff,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ pub struct LocalizedLogEntry {
|
||||
pub transaction_index: usize,
|
||||
/// Log position in the block.
|
||||
pub log_index: usize,
|
||||
/// Log position in the transaction.
|
||||
pub transaction_log_index: usize,
|
||||
}
|
||||
|
||||
impl Deref for LocalizedLogEntry {
|
||||
|
||||
@@ -33,4 +33,5 @@ pub mod transaction_import;
|
||||
pub mod block_import_error;
|
||||
pub mod restoration_status;
|
||||
pub mod snapshot_manifest;
|
||||
pub mod mode;
|
||||
pub mod mode;
|
||||
pub mod pruning_info;
|
||||
|
||||
29
ethcore/src/types/pruning_info.rs
Normal file
29
ethcore/src/types/pruning_info.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Information about portions of the state and chain which the client may serve.
|
||||
//!
|
||||
//! Currently assumes that a client will store everything past a certain point
|
||||
//! or everything. Will be extended in the future to support a definition
|
||||
//! of which portions of the ancient chain and current state trie are stored as well.
|
||||
|
||||
/// Client pruning info. See module-level docs for more details.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct PruningInfo {
|
||||
/// Pruning history size
|
||||
pub history_size: Option<u64>,
|
||||
}
|
||||
@@ -72,8 +72,8 @@ pub struct Transaction {
|
||||
|
||||
impl Transaction {
|
||||
/// Append object with a without signature into RLP stream
|
||||
pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option<u8>) {
|
||||
s.begin_list(if let None = network_id { 6 } else { 9 });
|
||||
pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option<u64>) {
|
||||
s.begin_list(if network_id.is_none() { 6 } else { 9 });
|
||||
s.append(&self.nonce);
|
||||
s.append(&self.gas_price);
|
||||
s.append(&self.gas);
|
||||
@@ -140,26 +140,26 @@ impl From<ethjson::transaction::Transaction> for SignedTransaction {
|
||||
|
||||
impl Transaction {
|
||||
/// The message hash of the transaction.
|
||||
pub fn hash(&self, network_id: Option<u8>) -> H256 {
|
||||
pub fn hash(&self, network_id: Option<u64>) -> H256 {
|
||||
let mut stream = RlpStream::new();
|
||||
self.rlp_append_unsigned_transaction(&mut stream, network_id);
|
||||
stream.out().sha3()
|
||||
}
|
||||
|
||||
/// Signs the transaction as coming from `sender`.
|
||||
pub fn sign(self, secret: &Secret, network_id: Option<u8>) -> SignedTransaction {
|
||||
pub fn sign(self, secret: &Secret, network_id: Option<u64>) -> SignedTransaction {
|
||||
let sig = ::ethkey::sign(secret, &self.hash(network_id))
|
||||
.expect("data is valid and context has signing capabilities; qed");
|
||||
self.with_signature(sig, network_id)
|
||||
}
|
||||
|
||||
/// Signs the transaction with signature.
|
||||
pub fn with_signature(self, sig: Signature, network_id: Option<u8>) -> SignedTransaction {
|
||||
pub fn with_signature(self, sig: Signature, network_id: Option<u64>) -> SignedTransaction {
|
||||
SignedTransaction {
|
||||
unsigned: self,
|
||||
r: sig.r().into(),
|
||||
s: sig.s().into(),
|
||||
v: sig.v() + if let Some(n) = network_id { 35 + n * 2 } else { 27 },
|
||||
v: sig.v() as u64 + if let Some(n) = network_id { 35 + n * 2 } else { 27 },
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
}
|
||||
@@ -210,8 +210,8 @@ pub struct SignedTransaction {
|
||||
/// Plain Transaction.
|
||||
unsigned: Transaction,
|
||||
/// The V field of the signature; the LS bit described which half of the curve our point falls
|
||||
/// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks.
|
||||
v: u8,
|
||||
/// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks.
|
||||
v: u64,
|
||||
/// The R field of the signature; helps describe the point on the curve.
|
||||
r: U256,
|
||||
/// The S field of the signature; helps describe the point on the curve.
|
||||
@@ -302,10 +302,13 @@ impl SignedTransaction {
|
||||
}
|
||||
|
||||
/// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid.
|
||||
pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => (v - 1) % 2, _ => 4 } }
|
||||
pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => ((v - 1) % 2) as u8, _ => 4 } }
|
||||
|
||||
/// The network ID, or `None` if this is a global transaction.
|
||||
pub fn network_id(&self) -> Option<u8> {
|
||||
/// The `v` value that appears in the RLP.
|
||||
pub fn original_v(&self) -> u64 { self.v }
|
||||
|
||||
/// The network ID, or `None` if this is a global transaction.
|
||||
pub fn network_id(&self) -> Option<u64> {
|
||||
match self.v {
|
||||
v if v > 36 => Some((v - 35) / 2),
|
||||
_ => None,
|
||||
@@ -367,7 +370,7 @@ impl SignedTransaction {
|
||||
}
|
||||
|
||||
/// Signed Transaction that is a part of canon blockchain.
|
||||
#[derive(Debug, PartialEq, Eq, Binary)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Binary)]
|
||||
pub struct LocalizedTransaction {
|
||||
/// Signed part.
|
||||
pub signed: SignedTransaction,
|
||||
@@ -461,7 +464,7 @@ fn should_agree_with_vitalik() {
|
||||
let signed: SignedTransaction = decode(&FromHex::from_hex(tx_data).unwrap());
|
||||
signed.check_low_s().unwrap();
|
||||
assert_eq!(signed.sender().unwrap(), address.into());
|
||||
flushln!("networkid: {:?}", signed.network_id());
|
||||
flushln!("networkid: {:?}", signed.network_id());
|
||||
};
|
||||
|
||||
test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce")
|
||||
|
||||
@@ -19,18 +19,21 @@
|
||||
use engines::Engine;
|
||||
use error::Error;
|
||||
|
||||
use util::{HeapSizeOf, H256};
|
||||
use util::{HeapSizeOf, H256, U256};
|
||||
|
||||
pub use self::blocks::Blocks;
|
||||
pub use self::headers::Headers;
|
||||
|
||||
/// Something which can produce a hash and a parent hash.
|
||||
pub trait HasHash {
|
||||
pub trait BlockLike {
|
||||
/// Get the hash of this item.
|
||||
fn hash(&self) -> H256;
|
||||
|
||||
/// Get the hash of this item's parent.
|
||||
fn parent_hash(&self) -> H256;
|
||||
|
||||
/// Get the difficulty of this item.
|
||||
fn difficulty(&self) -> U256;
|
||||
}
|
||||
|
||||
/// Defines transitions between stages of verification.
|
||||
@@ -45,13 +48,13 @@ pub trait HasHash {
|
||||
/// consistent.
|
||||
pub trait Kind: 'static + Sized + Send + Sync {
|
||||
/// The first stage: completely unverified.
|
||||
type Input: Sized + Send + HasHash + HeapSizeOf;
|
||||
type Input: Sized + Send + BlockLike + HeapSizeOf;
|
||||
|
||||
/// The second stage: partially verified.
|
||||
type Unverified: Sized + Send + HasHash + HeapSizeOf;
|
||||
type Unverified: Sized + Send + BlockLike + HeapSizeOf;
|
||||
|
||||
/// The third stage: completely verified.
|
||||
type Verified: Sized + Send + HasHash + HeapSizeOf;
|
||||
type Verified: Sized + Send + BlockLike + HeapSizeOf;
|
||||
|
||||
/// Attempt to create the `Unverified` item from the input.
|
||||
fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error>;
|
||||
@@ -62,14 +65,14 @@ pub trait Kind: 'static + Sized + Send + Sync {
|
||||
|
||||
/// The blocks verification module.
|
||||
pub mod blocks {
|
||||
use super::{Kind, HasHash};
|
||||
use super::{Kind, BlockLike};
|
||||
|
||||
use engines::Engine;
|
||||
use error::Error;
|
||||
use header::Header;
|
||||
use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered};
|
||||
|
||||
use util::{Bytes, HeapSizeOf, H256};
|
||||
use util::{Bytes, HeapSizeOf, H256, U256};
|
||||
|
||||
/// A mode for verifying blocks.
|
||||
pub struct Blocks;
|
||||
@@ -126,7 +129,7 @@ pub mod blocks {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasHash for Unverified {
|
||||
impl BlockLike for Unverified {
|
||||
fn hash(&self) -> H256 {
|
||||
self.header.hash()
|
||||
}
|
||||
@@ -134,9 +137,13 @@ pub mod blocks {
|
||||
fn parent_hash(&self) -> H256 {
|
||||
self.header.parent_hash().clone()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header.difficulty().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasHash for PreverifiedBlock {
|
||||
impl BlockLike for PreverifiedBlock {
|
||||
fn hash(&self) -> H256 {
|
||||
self.header.hash()
|
||||
}
|
||||
@@ -144,12 +151,16 @@ pub mod blocks {
|
||||
fn parent_hash(&self) -> H256 {
|
||||
self.header.parent_hash().clone()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header.difficulty().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verification for headers.
|
||||
pub mod headers {
|
||||
use super::{Kind, HasHash};
|
||||
use super::{Kind, BlockLike};
|
||||
|
||||
use engines::Engine;
|
||||
use error::Error;
|
||||
@@ -157,10 +168,12 @@ pub mod headers {
|
||||
use verification::verify_header_params;
|
||||
|
||||
use util::hash::H256;
|
||||
use util::U256;
|
||||
|
||||
impl HasHash for Header {
|
||||
impl BlockLike for Header {
|
||||
fn hash(&self) -> H256 { self.hash() }
|
||||
fn parent_hash(&self) -> H256 { self.parent_hash().clone() }
|
||||
fn difficulty(&self) -> U256 { self.difficulty().clone() }
|
||||
}
|
||||
|
||||
/// A mode for verifying headers.
|
||||
@@ -173,7 +186,7 @@ pub mod headers {
|
||||
type Verified = Header;
|
||||
|
||||
fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error> {
|
||||
verify_header_params(&input, engine).map(|_| input)
|
||||
verify_header_params(&input, engine, true).map(|_| input)
|
||||
}
|
||||
|
||||
fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> {
|
||||
|
||||
@@ -26,7 +26,7 @@ use error::*;
|
||||
use engines::Engine;
|
||||
use service::*;
|
||||
|
||||
use self::kind::{HasHash, Kind};
|
||||
use self::kind::{BlockLike, Kind};
|
||||
|
||||
pub use types::verification_queue_info::VerificationQueueInfo as QueueInfo;
|
||||
|
||||
@@ -97,13 +97,14 @@ pub struct VerificationQueue<K: Kind> {
|
||||
engine: Arc<Engine>,
|
||||
more_to_verify: Arc<SCondvar>,
|
||||
verification: Arc<Verification<K>>,
|
||||
verifiers: Vec<JoinHandle<()>>,
|
||||
deleting: Arc<AtomicBool>,
|
||||
ready_signal: Arc<QueueSignal>,
|
||||
empty: Arc<SCondvar>,
|
||||
processing: RwLock<HashSet<H256>>,
|
||||
processing: RwLock<HashMap<H256, U256>>, // hash to difficulty
|
||||
max_queue_size: usize,
|
||||
max_mem_use: usize,
|
||||
total_difficulty: RwLock<U256>,
|
||||
verifier_handles: Vec<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
struct QueueSignal {
|
||||
@@ -185,7 +186,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
let empty = Arc::new(SCondvar::new());
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
|
||||
let mut verifier_handles: Vec<JoinHandle<()>> = Vec::new();
|
||||
let thread_count = max(::num_cpus::get(), 3) - 2;
|
||||
for i in 0..thread_count {
|
||||
let verification = verification.clone();
|
||||
@@ -195,7 +196,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
let empty = empty.clone();
|
||||
let deleting = deleting.clone();
|
||||
let panic_handler = panic_handler.clone();
|
||||
verifiers.push(
|
||||
verifier_handles.push(
|
||||
thread::Builder::new()
|
||||
.name(format!("Verifier #{}", i))
|
||||
.spawn(move || {
|
||||
@@ -212,12 +213,13 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
ready_signal: ready_signal.clone(),
|
||||
more_to_verify: more_to_verify.clone(),
|
||||
verification: verification.clone(),
|
||||
verifiers: verifiers,
|
||||
deleting: deleting.clone(),
|
||||
processing: RwLock::new(HashSet::new()),
|
||||
processing: RwLock::new(HashMap::new()),
|
||||
empty: empty.clone(),
|
||||
max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT),
|
||||
max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT),
|
||||
total_difficulty: RwLock::new(0.into()),
|
||||
verifier_handles: verifier_handles,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,6 +337,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
sizes.unverified.store(0, AtomicOrdering::Release);
|
||||
sizes.verifying.store(0, AtomicOrdering::Release);
|
||||
sizes.verified.store(0, AtomicOrdering::Release);
|
||||
*self.total_difficulty.write() = 0.into();
|
||||
|
||||
self.processing.write().clear();
|
||||
}
|
||||
@@ -349,7 +352,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
|
||||
/// Check if the item is currently in the queue
|
||||
pub fn status(&self, hash: &H256) -> Status {
|
||||
if self.processing.read().contains(hash) {
|
||||
if self.processing.read().contains_key(hash) {
|
||||
return Status::Queued;
|
||||
}
|
||||
if self.verification.bad.lock().contains(hash) {
|
||||
@@ -362,7 +365,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
pub fn import(&self, input: K::Input) -> ImportResult {
|
||||
let h = input.hash();
|
||||
{
|
||||
if self.processing.read().contains(&h) {
|
||||
if self.processing.read().contains_key(&h) {
|
||||
return Err(ImportError::AlreadyQueued.into());
|
||||
}
|
||||
|
||||
@@ -381,7 +384,11 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
Ok(item) => {
|
||||
self.verification.sizes.unverified.fetch_add(item.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
|
||||
self.processing.write().insert(h.clone());
|
||||
self.processing.write().insert(h.clone(), item.difficulty());
|
||||
{
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td + item.difficulty();
|
||||
}
|
||||
self.verification.unverified.lock().push_back(item);
|
||||
self.more_to_verify.notify_all();
|
||||
Ok(h)
|
||||
@@ -406,7 +413,10 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
bad.reserve(hashes.len());
|
||||
for hash in hashes {
|
||||
bad.insert(hash.clone());
|
||||
processing.remove(hash);
|
||||
if let Some(difficulty) = processing.remove(hash) {
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td - difficulty;
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_verified = VecDeque::new();
|
||||
@@ -415,7 +425,10 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
if bad.contains(&output.parent_hash()) {
|
||||
removed_size += output.heap_size_of_children();
|
||||
bad.insert(output.hash());
|
||||
processing.remove(&output.hash());
|
||||
if let Some(difficulty) = processing.remove(&output.hash()) {
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td - difficulty;
|
||||
}
|
||||
} else {
|
||||
new_verified.push_back(output);
|
||||
}
|
||||
@@ -433,7 +446,10 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
}
|
||||
let mut processing = self.processing.write();
|
||||
for hash in hashes {
|
||||
processing.remove(hash);
|
||||
if let Some(difficulty) = processing.remove(hash) {
|
||||
let mut td = self.total_difficulty.write();
|
||||
*td = *td - difficulty;
|
||||
}
|
||||
}
|
||||
processing.is_empty()
|
||||
}
|
||||
@@ -487,7 +503,13 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimise memory footprint of the heap fields.
|
||||
/// Get the total difficulty of all the blocks in the queue.
|
||||
pub fn total_difficulty(&self) -> U256 {
|
||||
self.total_difficulty.read().clone()
|
||||
}
|
||||
|
||||
/// Optimise memory footprint of the heap fields, and adjust the number of threads
|
||||
/// to better suit the workload.
|
||||
pub fn collect_garbage(&self) {
|
||||
{
|
||||
self.verification.unverified.lock().shrink_to_fit();
|
||||
@@ -509,8 +531,15 @@ impl<K: Kind> Drop for VerificationQueue<K> {
|
||||
trace!(target: "shutdown", "[VerificationQueue] Closing...");
|
||||
self.clear();
|
||||
self.deleting.store(true, AtomicOrdering::Release);
|
||||
self.more_to_verify.notify_all();
|
||||
for t in self.verifiers.drain(..) {
|
||||
|
||||
// acquire this lock to force threads to reach the waiting point
|
||||
// if they're in-between the exit check and the more_to_verify wait.
|
||||
{
|
||||
let _more = self.verification.more_to_verify.lock().unwrap();
|
||||
self.more_to_verify.notify_all();
|
||||
}
|
||||
|
||||
for t in self.verifier_handles.drain(..) {
|
||||
t.join().unwrap();
|
||||
}
|
||||
trace!(target: "shutdown", "[VerificationQueue] Closed.");
|
||||
@@ -569,6 +598,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_total_difficulty() {
|
||||
let queue = get_test_queue();
|
||||
let block = get_good_dummy_block();
|
||||
let hash = BlockView::new(&block).header().hash().clone();
|
||||
if let Err(e) = queue.import(Unverified::new(block)) {
|
||||
panic!("error importing block that is valid by definition({:?})", e);
|
||||
}
|
||||
queue.flush();
|
||||
assert_eq!(queue.total_difficulty(), 131072.into());
|
||||
queue.drain(10);
|
||||
assert_eq!(queue.total_difficulty(), 131072.into());
|
||||
queue.mark_as_good(&[ hash ]);
|
||||
assert_eq!(queue.total_difficulty(), 0.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_ok_for_drained_duplicates() {
|
||||
let queue = get_test_queue();
|
||||
|
||||
@@ -29,6 +29,7 @@ use header::{BlockNumber, Header};
|
||||
use rlp::{UntrustedRlp, View};
|
||||
use transaction::SignedTransaction;
|
||||
use views::BlockView;
|
||||
use time::get_time;
|
||||
|
||||
/// Preprocessed block data gathered in `verify_block_unordered` call
|
||||
pub struct PreverifiedBlock {
|
||||
@@ -50,12 +51,12 @@ impl HeapSizeOf for PreverifiedBlock {
|
||||
|
||||
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
|
||||
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
||||
try!(verify_header_params(&header, engine));
|
||||
try!(verify_header_params(&header, engine, true));
|
||||
try!(verify_block_integrity(bytes, &header.transactions_root(), &header.uncles_hash()));
|
||||
try!(engine.verify_block_basic(&header, Some(bytes)));
|
||||
for u in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||
let u = try!(u);
|
||||
try!(verify_header_params(&u, engine));
|
||||
try!(verify_header_params(&u, engine, false));
|
||||
try!(engine.verify_block_basic(&u, None));
|
||||
}
|
||||
// Verify transactions.
|
||||
@@ -194,7 +195,7 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>
|
||||
}
|
||||
|
||||
/// Check basic header parameters.
|
||||
pub fn verify_header_params(header: &Header, engine: &Engine) -> Result<(), Error> {
|
||||
pub fn verify_header_params(header: &Header, engine: &Engine, is_full: bool) -> Result<(), Error> {
|
||||
if header.number() >= From::from(BlockNumber::max_value()) {
|
||||
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() })))
|
||||
}
|
||||
@@ -209,6 +210,12 @@ pub fn verify_header_params(header: &Header, engine: &Engine) -> Result<(), Erro
|
||||
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
|
||||
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() })));
|
||||
}
|
||||
if is_full {
|
||||
let max_time = get_time().sec as u64 + 30;
|
||||
if header.timestamp() > max_time {
|
||||
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: header.timestamp() })))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,13 @@ pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="maxCodeSize")]
|
||||
pub max_code_size: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="ecip1010PauseTransition")]
|
||||
pub ecip1010_pause_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="ecip1010ContinueTransition")]
|
||||
pub ecip1010_continue_transition: Option<Uint>,
|
||||
}
|
||||
|
||||
/// Ethash engine deserialization.
|
||||
|
||||
@@ -35,6 +35,10 @@ pub struct Params {
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
pub network_id: Uint,
|
||||
/// Chain id.
|
||||
#[serde(rename="chainID")]
|
||||
pub chain_id: Option<Uint>,
|
||||
|
||||
/// Name of the main ("eth") subprotocol.
|
||||
#[serde(rename="subprotocolName")]
|
||||
pub subprotocol_name: Option<String>,
|
||||
@@ -58,6 +62,7 @@ mod tests {
|
||||
"homesteadTransition": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"networkID" : "0x1",
|
||||
"chainID" : "0x15",
|
||||
"subprotocolName" : "exp",
|
||||
"minGasLimit": "0x1388",
|
||||
"accountStartNonce": "0x00"
|
||||
|
||||
@@ -431,7 +431,7 @@
|
||||
<key>GID</key>
|
||||
<integer>0</integer>
|
||||
<key>PATH</key>
|
||||
<string>../target/release/deps/ethstore</string>
|
||||
<string>../target/release/ethstore</string>
|
||||
<key>PATH_TYPE</key>
|
||||
<integer>3</integer>
|
||||
<key>PERMISSIONS</key>
|
||||
@@ -578,7 +578,7 @@
|
||||
<key>OVERWRITE_PERMISSIONS</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.4.6</string>
|
||||
<string>1.4.12</string>
|
||||
</dict>
|
||||
<key>UUID</key>
|
||||
<string>2DCD5B81-7BAF-4DA1-9251-6274B089FD36</string>
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
!define WND_TITLE "Parity"
|
||||
!define WAIT_MS 5000
|
||||
!define SYNC_TERM 0x00100001
|
||||
|
||||
|
||||
!define APPNAME "Parity"
|
||||
!define COMPANYNAME "Ethcore"
|
||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||
!define VERSIONMAJOR 1
|
||||
!define VERSIONMINOR 4
|
||||
!define VERSIONBUILD 6
|
||||
!define VERSIONBUILD 12
|
||||
!define ARGS "--warp"
|
||||
!define FIRST_START_ARGS "ui --warp --mode=passive"
|
||||
|
||||
@@ -90,7 +90,7 @@ section "install"
|
||||
# Files added here should be removed by the uninstaller (see section "uninstall")
|
||||
file /oname=parity.exe ..\target\release\parity.exe
|
||||
file /oname=ptray.exe ..\windows\ptray\x64\Release\ptray.exe
|
||||
|
||||
|
||||
file "logo.ico"
|
||||
file vc_redist.x64.exe
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
643e43dbaf3050e8e22479a4112e4e79 target/release/parity
|
||||
@@ -32,8 +32,8 @@ Operating Options:
|
||||
(default: {flag_mode_alarm}).
|
||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a
|
||||
JSON chain specification file or olympic, frontier,
|
||||
homestead, mainnet, ropsten, morden, classic, expanse or
|
||||
testnet (default: {flag_chain}).
|
||||
homestead, mainnet, ropsten, morden, classic, expanse,
|
||||
or testnet (default: {flag_chain}).
|
||||
-d --db-path PATH Specify the database & configuration directory path
|
||||
(default: {flag_db_path}).
|
||||
--keys-path PATH Specify the path for JSON key files to be found
|
||||
|
||||
@@ -27,7 +27,7 @@ use user_defaults::UserDefaults;
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SpecType {
|
||||
Mainnet,
|
||||
Testnet,
|
||||
Morden,
|
||||
Ropsten,
|
||||
Olympic,
|
||||
Classic,
|
||||
@@ -48,8 +48,8 @@ impl str::FromStr for SpecType {
|
||||
let spec = match s {
|
||||
"frontier" | "homestead" | "mainnet" => SpecType::Mainnet,
|
||||
"frontier-dogmatic" | "homestead-dogmatic" | "classic" => SpecType::Classic,
|
||||
"morden" | "testnet" => SpecType::Testnet,
|
||||
"ropsten" => SpecType::Ropsten,
|
||||
"morden" => SpecType::Morden,
|
||||
"ropsten" | "testnet" => SpecType::Ropsten,
|
||||
"olympic" => SpecType::Olympic,
|
||||
"expanse" => SpecType::Expanse,
|
||||
other => SpecType::Custom(other.into()),
|
||||
@@ -62,7 +62,7 @@ impl SpecType {
|
||||
pub fn spec(&self) -> Result<Spec, String> {
|
||||
match *self {
|
||||
SpecType::Mainnet => Ok(ethereum::new_frontier()),
|
||||
SpecType::Testnet => Ok(ethereum::new_morden()),
|
||||
SpecType::Morden => Ok(ethereum::new_morden()),
|
||||
SpecType::Ropsten => Ok(ethereum::new_ropsten()),
|
||||
SpecType::Olympic => Ok(ethereum::new_olympic()),
|
||||
SpecType::Classic => Ok(ethereum::new_classic()),
|
||||
@@ -283,10 +283,11 @@ mod tests {
|
||||
assert_eq!(SpecType::Mainnet, "frontier".parse().unwrap());
|
||||
assert_eq!(SpecType::Mainnet, "homestead".parse().unwrap());
|
||||
assert_eq!(SpecType::Mainnet, "mainnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Testnet, "testnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Testnet, "morden".parse().unwrap());
|
||||
assert_eq!(SpecType::Ropsten, "testnet".parse().unwrap());
|
||||
assert_eq!(SpecType::Morden, "morden".parse().unwrap());
|
||||
assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap());
|
||||
assert_eq!(SpecType::Olympic, "olympic".parse().unwrap());
|
||||
assert_eq!(SpecType::Classic, "classic".parse().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -177,7 +177,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
|
||||
let mut sync_config = SyncConfig::default();
|
||||
sync_config.network_id = match cmd.network_id {
|
||||
Some(id) => id,
|
||||
None => spec.network_id(),
|
||||
None => spec.network_id() as usize,
|
||||
};
|
||||
if spec.subprotocol_name().len() != 3 {
|
||||
warn!("Your chain specification's subprotocol length is not 3. Ignoring.");
|
||||
|
||||
@@ -128,7 +128,13 @@ impl Default for UserDefaults {
|
||||
impl UserDefaults {
|
||||
pub fn load<P>(path: P) -> Result<Self, String> where P: AsRef<Path> {
|
||||
match File::open(path) {
|
||||
Ok(file) => from_reader(file).map_err(|e| e.to_string()),
|
||||
Ok(file) => match from_reader(file) {
|
||||
Ok(defaults) => Ok(defaults),
|
||||
Err(e) => {
|
||||
warn!("Error loading user defaults file: {:?}", e);
|
||||
Ok(UserDefaults::default())
|
||||
},
|
||||
},
|
||||
_ => Ok(UserDefaults::default()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
81fb6415b884badc17c15534271a457129a78564
|
||||
@@ -1 +0,0 @@
|
||||
7abf0c0c0f47873be9c1c4859d083ff4 parity_1.4.0_amd64.deb
|
||||
@@ -1 +0,0 @@
|
||||
34a11190d1457c0d32175c939cd43b9b9563b6f5
|
||||
@@ -1,2 +0,0 @@
|
||||
1f98044f343dd17447c4d73f00248bef parity_1.4.0_armhf.deb
|
||||
0e2dad031c1b2586bf21adecbf65bfea parity_1.4.0_armhf.deb
|
||||
@@ -74,7 +74,7 @@ impl SyncProvider for TestSyncProvider {
|
||||
PeerInfo {
|
||||
id: Some("node1".to_owned()),
|
||||
client_version: "Parity/1".to_owned(),
|
||||
capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()],
|
||||
capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()],
|
||||
remote_address: "127.0.0.1:7777".to_owned(),
|
||||
local_address: "127.0.0.1:8888".to_owned(),
|
||||
eth_version: 62,
|
||||
@@ -84,7 +84,7 @@ impl SyncProvider for TestSyncProvider {
|
||||
PeerInfo {
|
||||
id: None,
|
||||
client_version: "Parity/2".to_owned(),
|
||||
capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()],
|
||||
capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()],
|
||||
remote_address: "Handshake".to_owned(),
|
||||
local_address: "127.0.0.1:3333".to_owned(),
|
||||
eth_version: 64,
|
||||
|
||||
@@ -191,6 +191,7 @@ fn rpc_eth_logs() {
|
||||
data: vec![1,2,3],
|
||||
},
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
transaction_hash: H256::default(),
|
||||
log_index: 0,
|
||||
}, LocalizedLogEntry {
|
||||
@@ -202,8 +203,9 @@ fn rpc_eth_logs() {
|
||||
data: vec![1,2,3],
|
||||
},
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 1,
|
||||
transaction_hash: H256::default(),
|
||||
log_index: 0,
|
||||
log_index: 1,
|
||||
}]);
|
||||
|
||||
|
||||
@@ -211,8 +213,8 @@ fn rpc_eth_logs() {
|
||||
let request2 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1}], "id": 1}"#;
|
||||
let request3 = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":0}], "id": 1}"#;
|
||||
|
||||
let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"}],"id":1}"#;
|
||||
let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"}],"id":1}"#;
|
||||
let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#;
|
||||
let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#;
|
||||
let response3 = r#"{"jsonrpc":"2.0","result":[],"id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(request1), Some(response1.to_owned()));
|
||||
@@ -233,6 +235,7 @@ fn rpc_logs_filter() {
|
||||
data: vec![1,2,3],
|
||||
},
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
transaction_hash: H256::default(),
|
||||
log_index: 0,
|
||||
}, LocalizedLogEntry {
|
||||
@@ -244,8 +247,9 @@ fn rpc_logs_filter() {
|
||||
data: vec![1,2,3],
|
||||
},
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 1,
|
||||
transaction_hash: H256::default(),
|
||||
log_index: 0,
|
||||
log_index: 1,
|
||||
}]);
|
||||
|
||||
// Register filters first
|
||||
@@ -259,8 +263,8 @@ fn rpc_logs_filter() {
|
||||
|
||||
let request_changes1 = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x0"], "id": 1}"#;
|
||||
let request_changes2 = r#"{"jsonrpc": "2.0", "method": "eth_getFilterChanges", "params": ["0x1"], "id": 1}"#;
|
||||
let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"}],"id":1}"#;
|
||||
let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"}],"id":1}"#;
|
||||
let response1 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x0","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#;
|
||||
let response2 = r#"{"jsonrpc":"2.0","result":[{"address":"0x0000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","data":"0x010203","logIndex":"0x1","topics":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x1","type":"mined"}],"id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(request_changes1), Some(response1.to_owned()));
|
||||
assert_eq!(tester.io.handle_request_sync(request_changes2), Some(response2.to_owned()));
|
||||
@@ -485,7 +489,7 @@ fn rpc_eth_pending_transaction_by_hash() {
|
||||
tester.miner.pending_transactions.lock().insert(H256::zero(), tx);
|
||||
}
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":0,"value":"0xa"},"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#;
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionByHash",
|
||||
@@ -761,6 +765,7 @@ fn rpc_eth_send_transaction() {
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(&request), Some(response));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_eth_send_transaction_with_bad_to() {
|
||||
let tester = EthTester::default();
|
||||
@@ -878,6 +883,7 @@ fn rpc_eth_transaction_receipt() {
|
||||
block_number: 0x4510c,
|
||||
transaction_hash: H256::new(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 1,
|
||||
}],
|
||||
log_bloom: 0.into(),
|
||||
@@ -894,7 +900,7 @@ fn rpc_eth_transaction_receipt() {
|
||||
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
@@ -355,3 +355,4 @@ fn rpc_parity_next_nonce() {
|
||||
assert_eq!(io1.handle_request_sync(&request), Some(response1.to_owned()));
|
||||
assert_eq!(io2.handle_request_sync(&request), Some(response2.to_owned()));
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ mod tests {
|
||||
fn test_serialize_block_transactions() {
|
||||
let t = BlockTransactions::Full(vec![Transaction::default()]);
|
||||
let serialized = serde_json::to_string(&t).unwrap();
|
||||
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}]"#);
|
||||
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}]"#);
|
||||
|
||||
let t = BlockTransactions::Hashes(vec![H256::default().into()]);
|
||||
let serialized = serde_json::to_string(&t).unwrap();
|
||||
|
||||
@@ -124,7 +124,7 @@ impl Serialize for FilterChanges {
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use std::str::FromStr;
|
||||
use util::hash::*;
|
||||
use util::hash::H256;
|
||||
use super::*;
|
||||
use v1::types::BlockNumber;
|
||||
use ethcore::filter::Filter as EthFilter;
|
||||
|
||||
@@ -38,9 +38,12 @@ pub struct Log {
|
||||
/// Transaction Index
|
||||
#[serde(rename="transactionIndex")]
|
||||
pub transaction_index: Option<U256>,
|
||||
/// Log Index
|
||||
/// Log Index in Block
|
||||
#[serde(rename="logIndex")]
|
||||
pub log_index: Option<U256>,
|
||||
/// Log Index in Transaction
|
||||
#[serde(rename="transactionLogIndex")]
|
||||
pub transaction_log_index: Option<U256>,
|
||||
/// Log Type
|
||||
#[serde(rename="type")]
|
||||
pub log_type: String,
|
||||
@@ -57,6 +60,7 @@ impl From<LocalizedLogEntry> for Log {
|
||||
transaction_hash: Some(e.transaction_hash.into()),
|
||||
transaction_index: Some(e.transaction_index.into()),
|
||||
log_index: Some(e.log_index.into()),
|
||||
transaction_log_index: Some(e.transaction_log_index.into()),
|
||||
log_type: "mined".to_owned(),
|
||||
}
|
||||
}
|
||||
@@ -73,6 +77,7 @@ impl From<LogEntry> for Log {
|
||||
transaction_hash: None,
|
||||
transaction_index: None,
|
||||
log_index: None,
|
||||
transaction_log_index: None,
|
||||
log_type: "pending".to_owned(),
|
||||
}
|
||||
}
|
||||
@@ -86,7 +91,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn log_serialization() {
|
||||
let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","type":"mined"}"#;
|
||||
let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":"0x1","type":"mined"}"#;
|
||||
|
||||
let log = Log {
|
||||
address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(),
|
||||
@@ -99,6 +104,7 @@ mod tests {
|
||||
block_number: Some(U256::from(0x4510c)),
|
||||
transaction_hash: Some(H256::default()),
|
||||
transaction_index: Some(U256::default()),
|
||||
transaction_log_index: Some(1.into()),
|
||||
log_index: Some(U256::from(1)),
|
||||
log_type: "mined".to_owned(),
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn receipt_serialization() {
|
||||
let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f"}"#;
|
||||
let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f"}"#;
|
||||
|
||||
let receipt = Receipt {
|
||||
transaction_hash: Some(0.into()),
|
||||
@@ -130,6 +130,7 @@ mod tests {
|
||||
block_number: Some(0x4510c.into()),
|
||||
transaction_hash: Some(0.into()),
|
||||
transaction_index: Some(0.into()),
|
||||
transaction_log_index: None,
|
||||
log_index: Some(1.into()),
|
||||
log_type: "mined".into(),
|
||||
}],
|
||||
|
||||
@@ -54,12 +54,18 @@ pub struct Transaction {
|
||||
/// Public key of the signer.
|
||||
#[serde(rename="publicKey")]
|
||||
pub public_key: Option<H512>,
|
||||
/// The V field of the signature.
|
||||
pub v: u8,
|
||||
/// The network id of the transaction, if any.
|
||||
#[serde(rename="networkId")]
|
||||
pub network_id: Option<u64>,
|
||||
/// The standardised V field of the signature (0 or 1).
|
||||
#[serde(rename="standardV")]
|
||||
pub standard_v: U256,
|
||||
/// The standardised V field of the signature.
|
||||
pub v: U256,
|
||||
/// The R field of the signature.
|
||||
pub r: H256,
|
||||
pub r: U256,
|
||||
/// The S field of the signature.
|
||||
pub s: H256,
|
||||
pub s: U256,
|
||||
}
|
||||
|
||||
impl From<LocalizedTransaction> for Transaction {
|
||||
@@ -86,7 +92,9 @@ impl From<LocalizedTransaction> for Transaction {
|
||||
},
|
||||
raw: ::rlp::encode(&t.signed).to_vec().into(),
|
||||
public_key: t.public_key().ok().map(Into::into),
|
||||
v: signature.v(),
|
||||
network_id: t.network_id(),
|
||||
standard_v: t.standard_v().into(),
|
||||
v: t.original_v().into(),
|
||||
r: signature.r().into(),
|
||||
s: signature.s().into(),
|
||||
}
|
||||
@@ -117,7 +125,9 @@ impl From<SignedTransaction> for Transaction {
|
||||
},
|
||||
raw: ::rlp::encode(&t).to_vec().into(),
|
||||
public_key: t.public_key().ok().map(Into::into),
|
||||
v: signature.v(),
|
||||
network_id: t.network_id(),
|
||||
standard_v: t.standard_v().into(),
|
||||
v: t.original_v().into(),
|
||||
r: signature.r().into(),
|
||||
s: signature.s().into(),
|
||||
}
|
||||
@@ -133,7 +143,7 @@ mod tests {
|
||||
fn test_transaction_serialize() {
|
||||
let t = Transaction::default();
|
||||
let serialized = serde_json::to_string(&t).unwrap();
|
||||
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#);
|
||||
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}"#);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use std::sync::Arc;
|
||||
use devtools::{http_client, RandomTempPath};
|
||||
|
||||
@@ -73,7 +73,7 @@ pub trait SyncProvider: Send + Sync {
|
||||
|
||||
/// Get peers information
|
||||
fn peers(&self) -> Vec<PeerInfo>;
|
||||
|
||||
|
||||
/// Get the enode if available.
|
||||
fn enode(&self) -> Option<String>;
|
||||
}
|
||||
@@ -233,6 +233,10 @@ impl ChainNotify for EthSync {
|
||||
fn stop(&self) {
|
||||
self.network.stop().unwrap_or_else(|e| warn!("Error stopping network: {:?}", e));
|
||||
}
|
||||
|
||||
fn transactions_received(&self, hashes: Vec<H256>, peer_id: PeerId) {
|
||||
self.handler.sync.write().transactions_received(hashes, peer_id);
|
||||
}
|
||||
}
|
||||
|
||||
impl IpcConfig for ManageNetwork { }
|
||||
|
||||
@@ -32,9 +32,8 @@ const MAX_HEADERS_TO_REQUEST: usize = 128;
|
||||
const MAX_BODIES_TO_REQUEST: usize = 64;
|
||||
const MAX_RECEPITS_TO_REQUEST: usize = 128;
|
||||
const SUBCHAIN_SIZE: u64 = 256;
|
||||
const MAX_ROUND_PARENTS: usize = 32;
|
||||
const MAX_ROUND_PARENTS: usize = 16;
|
||||
const MAX_PARALLEL_SUBCHAIN_DOWNLOAD: usize = 5;
|
||||
const MAX_REORG_BLOCKS: u64 = 20;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
/// Downloader state
|
||||
@@ -95,27 +94,38 @@ pub struct BlockDownloader {
|
||||
last_imported_hash: H256,
|
||||
/// Number of blocks imported this round
|
||||
imported_this_round: Option<usize>,
|
||||
/// Block number the last round started with.
|
||||
last_round_start: BlockNumber,
|
||||
last_round_start_hash: H256,
|
||||
/// Block parents imported this round (hash, parent)
|
||||
round_parents: VecDeque<(H256, H256)>,
|
||||
/// Do we need to download block recetips.
|
||||
download_receipts: bool,
|
||||
/// Sync up to the block with this hash.
|
||||
target_hash: Option<H256>,
|
||||
/// Reorganize up to this many blocks. Up to genesis if `None`,
|
||||
max_reorg_blocks: Option<BlockNumber>,
|
||||
/// Probing range for seeking common best block.
|
||||
retract_step: u64,
|
||||
}
|
||||
|
||||
impl BlockDownloader {
|
||||
/// Create a new instance of syncing strategy.
|
||||
pub fn new(sync_receipts: bool, start_hash: &H256, start_number: BlockNumber) -> BlockDownloader {
|
||||
pub fn new(sync_receipts: bool, start_hash: &H256, start_number: BlockNumber, max_reorg: Option<BlockNumber>) -> BlockDownloader {
|
||||
BlockDownloader {
|
||||
state: State::Idle,
|
||||
highest_block: None,
|
||||
last_imported_block: start_number,
|
||||
last_imported_hash: start_hash.clone(),
|
||||
last_round_start: start_number,
|
||||
last_round_start_hash: start_hash.clone(),
|
||||
blocks: BlockCollection::new(sync_receipts),
|
||||
imported_this_round: None,
|
||||
round_parents: VecDeque::new(),
|
||||
download_receipts: sync_receipts,
|
||||
target_hash: None,
|
||||
max_reorg_blocks: max_reorg,
|
||||
retract_step: 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +137,12 @@ impl BlockDownloader {
|
||||
|
||||
/// Mark a block as known in the chain
|
||||
pub fn mark_as_known(&mut self, hash: &H256, number: BlockNumber) {
|
||||
if number == self.last_imported_block + 1 {
|
||||
if number >= self.last_imported_block + 1 {
|
||||
self.last_imported_block = number;
|
||||
self.last_imported_hash = hash.clone();
|
||||
self.imported_this_round = Some(self.imported_this_round.unwrap_or(0) + 1);
|
||||
self.last_round_start = number;
|
||||
self.last_round_start_hash = hash.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,12 +161,6 @@ impl BlockDownloader {
|
||||
self.target_hash = Some(hash.clone());
|
||||
}
|
||||
|
||||
/// Set starting sync block
|
||||
pub fn _set_start(&mut self, hash: &H256, number: BlockNumber) {
|
||||
self.last_imported_hash = hash.clone();
|
||||
self.last_imported_block = number;
|
||||
}
|
||||
|
||||
/// Unmark header as being downloaded.
|
||||
pub fn clear_header_download(&mut self, hash: &H256) {
|
||||
self.blocks.clear_header_download(hash)
|
||||
@@ -172,6 +179,7 @@ impl BlockDownloader {
|
||||
pub fn reset_to(&mut self, hashes: Vec<H256>) {
|
||||
self.reset();
|
||||
self.blocks.reset_to(hashes);
|
||||
self.state = State::Blocks;
|
||||
}
|
||||
|
||||
/// Returns used heap memory size.
|
||||
@@ -260,7 +268,7 @@ impl BlockDownloader {
|
||||
return Ok(DownloadAction::Reset);
|
||||
} else {
|
||||
let best = io.chain().chain_info().best_block_number;
|
||||
if best > self.last_imported_block && best - self.last_imported_block > MAX_REORG_BLOCKS {
|
||||
if best > self.last_imported_block && (self.last_imported_block == 0 || best - self.last_imported_block > self.max_reorg_blocks.unwrap_or(u64::max_value())) {
|
||||
trace!(target: "sync", "No common block, disabling peer");
|
||||
return Err(BlockDownloaderImportError::Invalid);
|
||||
}
|
||||
@@ -336,39 +344,47 @@ impl BlockDownloader {
|
||||
|
||||
fn start_sync_round(&mut self, io: &mut SyncIo) {
|
||||
self.state = State::ChainHead;
|
||||
trace!(target: "sync", "Starting round (last imported count = {:?}, block = {:?}", self.imported_this_round, self.last_imported_block);
|
||||
trace!(target: "sync", "Starting round (last imported count = {:?}, last started = {}, block = {:?}", self.imported_this_round, self.last_round_start, self.last_imported_block);
|
||||
// Check if need to retract to find the common block. The problem is that the peers still return headers by hash even
|
||||
// from the non-canonical part of the tree. So we also retract if nothing has been imported last round.
|
||||
let start = self.last_round_start;
|
||||
let start_hash = self.last_round_start_hash;
|
||||
match self.imported_this_round {
|
||||
Some(n) if n == 0 && self.last_imported_block > 0 => {
|
||||
Some(n) if n == 0 && start > 0 => {
|
||||
// nothing was imported last round, step back to a previous block
|
||||
// search parent in last round known parents first
|
||||
if let Some(&(_, p)) = self.round_parents.iter().find(|&&(h, _)| h == self.last_imported_hash) {
|
||||
self.last_imported_block -= 1;
|
||||
if let Some(&(_, p)) = self.round_parents.iter().find(|&&(h, _)| h == start_hash) {
|
||||
self.last_imported_block = start - 1;
|
||||
self.last_imported_hash = p.clone();
|
||||
trace!(target: "sync", "Searching common header from the last round {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||
} else {
|
||||
let best = io.chain().chain_info().best_block_number;
|
||||
if best > self.last_imported_block && best - self.last_imported_block > MAX_REORG_BLOCKS {
|
||||
debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||
if best > start && (start == 0 || best - start > self.max_reorg_blocks.unwrap_or(u64::max_value())) {
|
||||
debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", start, start_hash);
|
||||
self.reset();
|
||||
} else {
|
||||
match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) {
|
||||
let n = start - min(self.retract_step, start);
|
||||
self.retract_step *= 2;
|
||||
match io.chain().block_hash(BlockID::Number(n)) {
|
||||
Some(h) => {
|
||||
self.last_imported_block -= 1;
|
||||
self.last_imported_block = n;
|
||||
self.last_imported_hash = h;
|
||||
trace!(target: "sync", "Searching common header in the blockchain {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||
trace!(target: "sync", "Searching common header in the blockchain {} ({})", start, self.last_imported_hash);
|
||||
}
|
||||
None => {
|
||||
debug!(target: "sync", "Could not revert to previous block, last: {} ({})", self.last_imported_block, self.last_imported_hash);
|
||||
debug!(target: "sync", "Could not revert to previous block, last: {} ({})", start, self.last_imported_hash);
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
_ => {
|
||||
self.retract_step = 1;
|
||||
},
|
||||
}
|
||||
self.last_round_start = self.last_imported_block;
|
||||
self.last_round_start_hash = self.last_imported_hash;
|
||||
self.imported_this_round = None;
|
||||
}
|
||||
|
||||
@@ -474,6 +490,9 @@ impl BlockDownloader {
|
||||
self.block_imported(&h, number, &parent);
|
||||
},
|
||||
Err(BlockImportError::Block(BlockError::UnknownParent(_))) if allow_out_of_order => {
|
||||
break;
|
||||
},
|
||||
Err(BlockImportError::Block(BlockError::UnknownParent(_))) => {
|
||||
trace!(target: "sync", "Unknown new block parent, restarting sync");
|
||||
break;
|
||||
},
|
||||
|
||||
@@ -94,15 +94,15 @@ use rlp::*;
|
||||
use network::*;
|
||||
use ethcore::views::{HeaderView};
|
||||
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo, BlockImportError};
|
||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo, BlockImportError, BlockQueueInfo};
|
||||
use ethcore::error::*;
|
||||
use ethcore::snapshot::{ManifestData, RestorationStatus};
|
||||
use sync_io::SyncIo;
|
||||
use time;
|
||||
use super::SyncConfig;
|
||||
use block_sync::{BlockDownloader, BlockRequest, BlockDownloaderImportError as DownloaderImportError, DownloadAction};
|
||||
use rand::Rng;
|
||||
use snapshot::{Snapshot, ChunkType};
|
||||
use rand::{thread_rng, Rng};
|
||||
use api::{PeerInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID};
|
||||
|
||||
known_heap_size!(0, PeerInfo);
|
||||
@@ -231,6 +231,13 @@ impl SyncStatus {
|
||||
min_peers
|
||||
}
|
||||
}
|
||||
|
||||
/// Is it doing a major sync?
|
||||
pub fn is_syncing(&self, queue_info: BlockQueueInfo) -> bool {
|
||||
let is_syncing_state = match self.state { SyncState::Idle | SyncState::NewBlocks => false, _ => true };
|
||||
let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3;
|
||||
is_verifying || is_syncing_state
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
@@ -319,6 +326,17 @@ impl PeerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
mod random {
|
||||
use rand;
|
||||
pub fn new() -> rand::ThreadRng { rand::thread_rng() }
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod random {
|
||||
use rand::{self, SeedableRng};
|
||||
pub fn new() -> rand::XorShiftRng { rand::XorShiftRng::from_seed([0, 1, 2, 3]) }
|
||||
}
|
||||
|
||||
/// Blockchain sync handler.
|
||||
/// See module documentation for more details.
|
||||
pub struct ChainSync {
|
||||
@@ -357,6 +375,7 @@ impl ChainSync {
|
||||
/// Create a new instance of syncing strategy.
|
||||
pub fn new(config: SyncConfig, chain: &BlockChainClient) -> ChainSync {
|
||||
let chain_info = chain.chain_info();
|
||||
let pruning = chain.pruning_info();
|
||||
let mut sync = ChainSync {
|
||||
state: if config.warp_sync { SyncState::WaitingPeers } else { SyncState::Idle },
|
||||
starting_block: chain.chain_info().best_block_number,
|
||||
@@ -364,7 +383,7 @@ impl ChainSync {
|
||||
peers: HashMap::new(),
|
||||
handshaking_peers: HashMap::new(),
|
||||
active_peers: HashSet::new(),
|
||||
new_blocks: BlockDownloader::new(false, &chain_info.best_block_hash, chain_info.best_block_number),
|
||||
new_blocks: BlockDownloader::new(false, &chain_info.best_block_hash, chain_info.best_block_number, pruning.history_size),
|
||||
old_blocks: None,
|
||||
last_sent_block_number: 0,
|
||||
network_id: config.network_id,
|
||||
@@ -419,6 +438,13 @@ impl ChainSync {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Updates transactions were received by a peer
|
||||
pub fn transactions_received(&mut self, hashes: Vec<H256>, peer_id: PeerId) {
|
||||
if let Some(mut peer_info) = self.peers.get_mut(&peer_id) {
|
||||
peer_info.last_sent_transactions.extend(&hashes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Abort all sync activity
|
||||
pub fn abort(&mut self, io: &mut SyncIo) {
|
||||
self.reset_and_continue(io);
|
||||
@@ -430,6 +456,7 @@ impl ChainSync {
|
||||
fn reset(&mut self, io: &mut SyncIo) {
|
||||
self.new_blocks.reset();
|
||||
self.snapshot.clear();
|
||||
let chain_info = io.chain().chain_info();
|
||||
if self.state == SyncState::SnapshotData {
|
||||
debug!(target:"sync", "Aborting snapshot restore");
|
||||
io.snapshot_service().abort_restore();
|
||||
@@ -437,6 +464,10 @@ impl ChainSync {
|
||||
for (_, ref mut p) in &mut self.peers {
|
||||
if p.block_set != Some(BlockSet::OldBlocks) {
|
||||
p.reset_asking();
|
||||
if p.difficulty.is_none() {
|
||||
// assume peer has up to date difficulty
|
||||
p.difficulty = Some(chain_info.pending_total_difficulty);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.state = SyncState::Idle;
|
||||
@@ -528,19 +559,19 @@ impl ChainSync {
|
||||
/// Update sync after the blockchain has been changed externally.
|
||||
pub fn update_targets(&mut self, chain: &BlockChainClient) {
|
||||
// Do not assume that the block queue/chain still has our last_imported_block
|
||||
let pruning = chain.pruning_info();
|
||||
let chain = chain.chain_info();
|
||||
self.new_blocks = BlockDownloader::new(false, &chain.best_block_hash, chain.best_block_number);
|
||||
self.new_blocks = BlockDownloader::new(false, &chain.best_block_hash, chain.best_block_number, pruning.history_size);
|
||||
self.old_blocks = None;
|
||||
if let (Some(ancient_block_hash), Some(ancient_block_number)) = (chain.ancient_block_hash, chain.ancient_block_number) {
|
||||
|
||||
trace!(target: "sync", "Downloading old blocks from {:?} (#{}) till {:?} (#{:?})", ancient_block_hash, ancient_block_number, chain.first_block_hash, chain.first_block_number);
|
||||
let mut downloader = BlockDownloader::new(true, &ancient_block_hash, ancient_block_number);
|
||||
let mut downloader = BlockDownloader::new(true, &ancient_block_hash, ancient_block_number, None);
|
||||
if let Some(hash) = chain.first_block_hash {
|
||||
trace!(target: "sync", "Downloader target set to {:?}", hash);
|
||||
downloader.set_target(&hash);
|
||||
}
|
||||
self.old_blocks = Some(downloader);
|
||||
} else {
|
||||
self.old_blocks = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,6 +859,12 @@ impl ChainSync {
|
||||
trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id);
|
||||
return Ok(());
|
||||
}
|
||||
let difficulty: U256 = try!(r.val_at(1));
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
if peer.difficulty.map_or(true, |pd| difficulty > pd) {
|
||||
peer.difficulty = Some(difficulty);
|
||||
}
|
||||
}
|
||||
let block_rlp = try!(r.at(0));
|
||||
let header_rlp = try!(block_rlp.at(0));
|
||||
let h = header_rlp.as_raw().sha3();
|
||||
@@ -856,6 +893,8 @@ impl ChainSync {
|
||||
trace!(target: "sync", "New block already queued {:?}", h);
|
||||
},
|
||||
Ok(_) => {
|
||||
// abort current download of the same block
|
||||
self.complete_sync(io);
|
||||
self.new_blocks.mark_as_known(&header.hash(), header.number());
|
||||
trace!(target: "sync", "New block queued {:?} ({})", h, header.number());
|
||||
},
|
||||
@@ -874,16 +913,10 @@ impl ChainSync {
|
||||
} else {
|
||||
trace!(target: "sync", "New unknown block {:?}", h);
|
||||
//TODO: handle too many unknown blocks
|
||||
let difficulty: U256 = try!(r.val_at(1));
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
if peer.difficulty.map_or(true, |pd| difficulty > pd) {
|
||||
peer.difficulty = Some(difficulty);
|
||||
trace!(target: "sync", "Received block {:?} with no known parent. Peer needs syncing...", h);
|
||||
}
|
||||
}
|
||||
self.sync_peer(io, peer_id, true);
|
||||
}
|
||||
}
|
||||
self.continue_sync(io);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -893,16 +926,24 @@ impl ChainSync {
|
||||
trace!(target: "sync", "Ignoring new hashes from unconfirmed peer {}", peer_id);
|
||||
return Ok(());
|
||||
}
|
||||
let hashes: Vec<_> = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::<H256>(0), item.val_at::<BlockNumber>(1))).collect();
|
||||
if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
|
||||
// Peer has new blocks with unknown difficulty
|
||||
peer.difficulty = None;
|
||||
if let Some(&(Ok(ref h), _)) = hashes.last() {
|
||||
peer.latest_hash = h.clone();
|
||||
}
|
||||
}
|
||||
if self.state != SyncState::Idle {
|
||||
trace!(target: "sync", "Ignoring new hashes since we're already downloading.");
|
||||
let max = r.iter().take(MAX_NEW_HASHES).map(|item| item.val_at::<BlockNumber>(1).unwrap_or(0)).fold(0u64, max);
|
||||
if max > self.highest_block.unwrap_or(0) {
|
||||
self.highest_block = Some(max);
|
||||
}
|
||||
self.continue_sync(io);
|
||||
return Ok(());
|
||||
}
|
||||
trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count());
|
||||
let hashes = r.iter().take(MAX_NEW_HASHES).map(|item| (item.val_at::<H256>(0), item.val_at::<BlockNumber>(1)));
|
||||
let mut max_height: BlockNumber = 0;
|
||||
let mut new_hashes = Vec::new();
|
||||
let last_imported_number = self.new_blocks.last_imported_block_number();
|
||||
@@ -950,6 +991,7 @@ impl ChainSync {
|
||||
self.state = SyncState::NewBlocks;
|
||||
self.sync_peer(io, peer_id, true);
|
||||
}
|
||||
self.continue_sync(io);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1071,10 +1113,10 @@ impl ChainSync {
|
||||
fn continue_sync(&mut self, io: &mut SyncIo) {
|
||||
let mut peers: Vec<(PeerId, U256, u8)> = self.peers.iter().filter_map(|(k, p)|
|
||||
if p.can_sync() { Some((*k, p.difficulty.unwrap_or_else(U256::zero), p.protocol_version)) } else { None }).collect();
|
||||
thread_rng().shuffle(&mut peers); //TODO: sort by rating
|
||||
random::new().shuffle(&mut peers); //TODO: sort by rating
|
||||
// prefer peers with higher protocol version
|
||||
peers.sort_by(|&(_, _, ref v1), &(_, _, ref v2)| v1.cmp(v2));
|
||||
trace!(target: "sync", "Syncing with {}/{} peers", self.active_peers.len(), peers.len());
|
||||
trace!(target: "sync", "Syncing with peers: {} active, {} confirmed, {} total", self.active_peers.len(), peers.len(), self.peers.len());
|
||||
for (p, _, _) in peers {
|
||||
if self.active_peers.contains(&p) {
|
||||
self.sync_peer(io, p, false);
|
||||
@@ -1103,12 +1145,13 @@ impl ChainSync {
|
||||
/// Find something to do for a peer. Called for a new peer or when a peer is done with its task.
|
||||
fn sync_peer(&mut self, io: &mut SyncIo, peer_id: PeerId, force: bool) {
|
||||
if !self.active_peers.contains(&peer_id) {
|
||||
trace!(target: "sync", "Skipping deactivated peer");
|
||||
trace!(target: "sync", "Skipping deactivated peer {}", peer_id);
|
||||
return;
|
||||
}
|
||||
let (peer_latest, peer_difficulty, peer_snapshot_number, peer_snapshot_hash) = {
|
||||
if let Some(peer) = self.peers.get_mut(&peer_id) {
|
||||
if peer.asking != PeerAsking::Nothing || !peer.can_sync() {
|
||||
trace!(target: "sync", "Skipping busy peer {}", peer_id);
|
||||
return;
|
||||
}
|
||||
if self.state == SyncState::Waiting {
|
||||
@@ -1129,7 +1172,7 @@ impl ChainSync {
|
||||
let num_active_peers = self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count();
|
||||
|
||||
let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty);
|
||||
if force || self.state == SyncState::NewBlocks || higher_difficulty || self.old_blocks.is_some() {
|
||||
if force || higher_difficulty || self.old_blocks.is_some() {
|
||||
match self.state {
|
||||
SyncState::WaitingPeers => {
|
||||
trace!(target: "sync", "Checking snapshot sync: {} vs {}", peer_snapshot_number, chain_info.best_block_number);
|
||||
@@ -1142,9 +1185,10 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown;
|
||||
trace!(target: "sync", "Considering peer {}, force={}, td={:?}, our td={}, latest={}, have_latest={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, peer_latest, have_latest, self.state);
|
||||
if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) {
|
||||
// check if got new blocks to download
|
||||
trace!(target: "sync", "Syncing with {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state);
|
||||
trace!(target: "sync", "Syncing with peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state);
|
||||
if let Some(request) = self.new_blocks.request_blocks(io, num_active_peers) {
|
||||
self.request_blocks(io, peer_id, request, BlockSet::NewBlocks);
|
||||
if self.state == SyncState::Idle {
|
||||
@@ -1174,6 +1218,8 @@ impl ChainSync {
|
||||
SyncState::SnapshotManifest | //already downloading from other peer
|
||||
SyncState::Waiting | SyncState::SnapshotWaiting => ()
|
||||
}
|
||||
} else {
|
||||
trace!(target: "sync", "Skipping peer {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1383,7 +1429,7 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
let mut item_count = r.item_count();
|
||||
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
|
||||
trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count);
|
||||
item_count = min(item_count, MAX_TX_TO_IMPORT);
|
||||
let mut transactions = Vec::with_capacity(item_count);
|
||||
for i in 0 .. item_count {
|
||||
@@ -1395,7 +1441,7 @@ impl ChainSync {
|
||||
let tx = rlp.as_raw().to_vec();
|
||||
transactions.push(tx);
|
||||
}
|
||||
io.chain().queue_transactions(transactions);
|
||||
io.chain().queue_transactions(transactions, peer_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1808,14 +1854,13 @@ impl ChainSync {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn select_random_lagging_peers(&mut self, peers: &[PeerId]) -> Vec<PeerId> {
|
||||
use rand::Rng;
|
||||
fn select_random_peers(peers: &[PeerId]) -> Vec<PeerId> {
|
||||
// take sqrt(x) peers
|
||||
let mut peers = peers.to_vec();
|
||||
let mut count = (self.peers.len() as f64).powf(0.5).round() as usize;
|
||||
let mut count = (peers.len() as f64).powf(0.5).round() as usize;
|
||||
count = min(count, MAX_PEERS_PROPAGATION);
|
||||
count = max(count, MIN_PEERS_PROPAGATION);
|
||||
::rand::thread_rng().shuffle(&mut peers);
|
||||
random::new().shuffle(&mut peers);
|
||||
peers.truncate(count);
|
||||
peers
|
||||
}
|
||||
@@ -1888,14 +1933,15 @@ impl ChainSync {
|
||||
let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32;
|
||||
let small = self.peers.len() < MIN_PEERS_PROPAGATION;
|
||||
|
||||
let mut random = random::new();
|
||||
let lucky_peers = self.peers.iter_mut()
|
||||
.filter(|_| small || ::rand::random::<u32>() < fraction)
|
||||
.filter(|_| small || random.next_u32() < fraction)
|
||||
.take(MAX_PEERS_PROPAGATION)
|
||||
.filter_map(|(peer_id, mut peer_info)| {
|
||||
// Send all transactions
|
||||
if peer_info.last_sent_transactions.is_empty() {
|
||||
peer_info.last_sent_transactions = all_transactions_hashes.clone();
|
||||
return Some((*peer_id, all_transactions_rlp.clone()));
|
||||
return Some((*peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone()));
|
||||
}
|
||||
|
||||
// Get hashes of all transactions to send to this peer
|
||||
@@ -1913,20 +1959,22 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
peer_info.last_sent_transactions = all_transactions_hashes.clone();
|
||||
Some((*peer_id, packet.out()))
|
||||
Some((*peer_id, to_send.len(), packet.out()))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Send RLPs
|
||||
let sent = lucky_peers.len();
|
||||
if sent > 0 {
|
||||
for (peer_id, rlp) in lucky_peers {
|
||||
let peers = lucky_peers.len();
|
||||
if peers > 0 {
|
||||
let mut max_sent = 0;
|
||||
for (peer_id, sent, rlp) in lucky_peers {
|
||||
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
||||
trace!(target: "sync", "{:02} <- Transactions ({} entries)", peer_id, sent);
|
||||
max_sent = max(max_sent, sent);
|
||||
}
|
||||
|
||||
trace!(target: "sync", "Sent up to {} transactions to {} peers.", transactions.len(), sent);
|
||||
debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, peers);
|
||||
}
|
||||
sent
|
||||
peers
|
||||
}
|
||||
|
||||
fn propagate_latest_blocks(&mut self, io: &mut SyncIo, sealed: &[H256]) {
|
||||
@@ -1935,7 +1983,7 @@ impl ChainSync {
|
||||
let mut peers = self.get_lagging_peers(&chain_info, io);
|
||||
if sealed.is_empty() {
|
||||
let hashes = self.propagate_new_hashes(&chain_info, io, &peers);
|
||||
peers = self.select_random_lagging_peers(&peers);
|
||||
peers = ChainSync::select_random_peers(&peers);
|
||||
let blocks = self.propagate_blocks(&chain_info, io, sealed, &peers);
|
||||
if blocks != 0 || hashes != 0 {
|
||||
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
||||
@@ -1946,7 +1994,6 @@ impl ChainSync {
|
||||
trace!(target: "sync", "Sent sealed block to all peers");
|
||||
};
|
||||
}
|
||||
self.propagate_new_transactions(io);
|
||||
self.last_sent_block_number = chain_info.best_block_number;
|
||||
}
|
||||
|
||||
@@ -1957,16 +2004,26 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
/// called when block is imported to chain - propagates the blocks and updates transactions sent to peers
|
||||
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256], sealed: &[H256]) {
|
||||
if io.is_chain_queue_empty() {
|
||||
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], enacted: &[H256], _retracted: &[H256], sealed: &[H256]) {
|
||||
let queue_info = io.chain().queue_info();
|
||||
let is_syncing = self.status().is_syncing(queue_info);
|
||||
|
||||
if !is_syncing || !sealed.is_empty() {
|
||||
trace!(target: "sync", "Propagating blocks, state={:?}", self.state);
|
||||
self.propagate_latest_blocks(io, sealed);
|
||||
}
|
||||
if !invalid.is_empty() {
|
||||
trace!(target: "sync", "Bad blocks in the queue, restarting");
|
||||
self.restart(io);
|
||||
}
|
||||
for peer_info in self.peers.values_mut() {
|
||||
peer_info.last_sent_transactions.clear();
|
||||
|
||||
if !is_syncing && !enacted.is_empty() && !self.peers.is_empty() {
|
||||
// Select random peer to re-broadcast transactions to.
|
||||
let peer = random::new().gen_range(0, self.peers.len());
|
||||
trace!(target: "sync", "Re-broadcasting transactions to a random peer.");
|
||||
self.peers.values_mut().nth(peer).map(|mut peer_info|
|
||||
peer_info.last_sent_transactions.clear()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2304,7 +2361,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn propagates_transactions_again_after_new_block() {
|
||||
fn does_not_propagate_new_transactions_after_new_block() {
|
||||
let mut client = TestBlockChainClient::new();
|
||||
client.add_blocks(100, EachBlockWith::Uncle);
|
||||
client.insert_transaction_to_queue();
|
||||
@@ -2313,18 +2370,39 @@ mod tests {
|
||||
let ss = TestSnapshotService::new();
|
||||
let mut io = TestIo::new(&mut client, &ss, &mut queue, None);
|
||||
let peer_count = sync.propagate_new_transactions(&mut io);
|
||||
// New block import should not trigger propagation.
|
||||
// (we only propagate on timeout)
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[]);
|
||||
// Try to propagate same transactions for the second time
|
||||
let peer_count2 = sync.propagate_new_transactions(&mut io);
|
||||
|
||||
// 2 message should be send
|
||||
assert_eq!(2, io.queue.len());
|
||||
// 1 peer should be updated twice
|
||||
// 1 message should be send
|
||||
assert_eq!(1, io.queue.len());
|
||||
// 1 peer should be updated only for the first time
|
||||
assert_eq!(1, peer_count);
|
||||
assert_eq!(1, peer_count2);
|
||||
assert_eq!(0, peer_count2);
|
||||
// TRANSACTIONS_PACKET
|
||||
assert_eq!(0x02, io.queue[0].packet_id);
|
||||
assert_eq!(0x02, io.queue[1].packet_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_fail_for_no_peers() {
|
||||
let mut client = TestBlockChainClient::new();
|
||||
client.add_blocks(100, EachBlockWith::Uncle);
|
||||
client.insert_transaction_to_queue();
|
||||
// Sync with no peers
|
||||
let mut sync = ChainSync::new(SyncConfig::default(), &client);
|
||||
let mut queue = VecDeque::new();
|
||||
let ss = TestSnapshotService::new();
|
||||
let mut io = TestIo::new(&mut client, &ss, &mut queue, None);
|
||||
let peer_count = sync.propagate_new_transactions(&mut io);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[]);
|
||||
// Try to propagate same transactions for the second time
|
||||
let peer_count2 = sync.propagate_new_transactions(&mut io);
|
||||
|
||||
assert_eq!(0, io.queue.len());
|
||||
assert_eq!(0, peer_count);
|
||||
assert_eq!(0, peer_count2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -255,8 +255,12 @@ fn high_td_attach() {
|
||||
fn disconnect_on_unrelated_chain() {
|
||||
::env_logger::init().ok();
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer_mut(0).chain.add_blocks(200, EachBlockWith::Uncle);
|
||||
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Nothing);
|
||||
net.peer(0).chain.set_history(Some(20));
|
||||
net.peer(1).chain.set_history(Some(20));
|
||||
net.restart_peer(0);
|
||||
net.restart_peer(1);
|
||||
net.peer(0).chain.add_blocks(500, EachBlockWith::Uncle);
|
||||
net.peer(1).chain.add_blocks(300, EachBlockWith::Nothing);
|
||||
net.sync();
|
||||
assert_eq!(net.disconnect_events, vec![(0, 0)]);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ description = "Ethcore utility library"
|
||||
homepage = "http://ethcore.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-util"
|
||||
version = "1.4.6"
|
||||
version = "1.4.12"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
//! implementations for even more speed, hidden behind the `x64_arithmetic`
|
||||
//! feature flag.
|
||||
|
||||
use std::{mem, fmt};
|
||||
use std::{mem, fmt, cmp};
|
||||
use std::str::{FromStr};
|
||||
use std::hash::Hash;
|
||||
use std::ops::{Shr, Shl, BitAnd, BitOr, BitXor, Not, Div, Rem, Mul, Add, Sub};
|
||||
use std::cmp::Ordering;
|
||||
use rustc_serialize::hex::{FromHex, FromHexError};
|
||||
use rustc_serialize::hex::{ToHex, FromHex, FromHexError};
|
||||
|
||||
/// Conversion from decimal string error
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -520,8 +520,10 @@ pub trait Uint: Sized + Default + FromStr + From<u64> + fmt::Debug + fmt::Displa
|
||||
fn bit(&self, index: usize) -> bool;
|
||||
/// Return single byte
|
||||
fn byte(&self, index: usize) -> u8;
|
||||
/// Convert U256 to the sequence of bytes with a big endian
|
||||
/// Convert to the sequence of bytes with a big endian
|
||||
fn to_big_endian(&self, bytes: &mut[u8]);
|
||||
/// Convert to a non-zero-prefixed hex representation (not prefixed by `0x`).
|
||||
fn to_hex(&self) -> String;
|
||||
/// Create `Uint(10**n)`
|
||||
fn exp10(n: usize) -> Self;
|
||||
/// Return eponentation `self**other`. Panic on overflow.
|
||||
@@ -683,6 +685,17 @@ macro_rules! construct_uint {
|
||||
bytes[i] = (arr[pos] >> ((rev % 8) * 8)) as u8;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn to_hex(&self) -> String {
|
||||
if self.is_zero() { return "0".to_owned(); } // special case.
|
||||
let mut bytes = [0u8; 8 * $n_words];
|
||||
self.to_big_endian(&mut bytes);
|
||||
let bp7 = self.bits() + 7;
|
||||
let len = cmp::max(bp7 / 8, 1);
|
||||
let bytes_hex = bytes[bytes.len() - len..].to_hex();
|
||||
(&bytes_hex[1 - bp7 % 8 / 4..]).to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exp10(n: usize) -> Self {
|
||||
match n {
|
||||
@@ -1636,7 +1649,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint256_pow () {
|
||||
fn uint256_pow() {
|
||||
assert_eq!(U256::from(10).pow(U256::from(0)), U256::from(1));
|
||||
assert_eq!(U256::from(10).pow(U256::from(1)), U256::from(10));
|
||||
assert_eq!(U256::from(10).pow(U256::from(2)), U256::from(100));
|
||||
@@ -1646,12 +1659,24 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn uint256_pow_overflow_panic () {
|
||||
fn uint256_pow_overflow_panic() {
|
||||
U256::from(2).pow(U256::from(0x100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint256_overflowing_pow () {
|
||||
fn should_format_hex_correctly() {
|
||||
assert_eq!(&U256::from(0).to_hex(), &"0");
|
||||
assert_eq!(&U256::from(0x1).to_hex(), &"1");
|
||||
assert_eq!(&U256::from(0xf).to_hex(), &"f");
|
||||
assert_eq!(&U256::from(0x10).to_hex(), &"10");
|
||||
assert_eq!(&U256::from(0xff).to_hex(), &"ff");
|
||||
assert_eq!(&U256::from(0x100).to_hex(), &"100");
|
||||
assert_eq!(&U256::from(0xfff).to_hex(), &"fff");
|
||||
assert_eq!(&U256::from(0x1000).to_hex(), &"1000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uint256_overflowing_pow() {
|
||||
// assert_eq!(
|
||||
// U256::from(2).overflowing_pow(U256::from(0xff)),
|
||||
// (U256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap(), false)
|
||||
|
||||
@@ -158,8 +158,8 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
}
|
||||
if self.send_queue.is_empty() {
|
||||
self.interest.remove(Ready::writable());
|
||||
try!(io.update_registration(self.token));
|
||||
}
|
||||
try!(io.update_registration(self.token));
|
||||
Ok(r)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn version() -> String {
|
||||
let date_dash = if commit_date.is_empty() { "" } else { "-" };
|
||||
let env = Target::env();
|
||||
let env_dash = if env.is_empty() { "" } else { "-" };
|
||||
format!("Parity/v{}-beta{}{}{}{}/{}-{}{}{}/rustc{}", env!("CARGO_PKG_VERSION"), sha3_dash, sha3, date_dash, commit_date, Target::arch(), Target::os(), env_dash, env, rustc_version())
|
||||
format!("Parity/v{}-stable{}{}{}{}/{}-{}{}{}/rustc{}", env!("CARGO_PKG_VERSION"), sha3_dash, sha3, date_dash, commit_date, Target::arch(), Target::os(), env_dash, env, rustc_version())
|
||||
}
|
||||
|
||||
/// Get the standard version data for this software.
|
||||
|
||||
@@ -23,7 +23,7 @@ const SNAPPY_OK: c_int = 0;
|
||||
const SNAPPY_INVALID_INPUT: c_int = 1;
|
||||
const SNAPPY_BUFFER_TOO_SMALL: c_int = 2;
|
||||
|
||||
#[link(name = "snappy")]
|
||||
#[link(name = "snappy", kind = "static")]
|
||||
extern {
|
||||
fn snappy_compress(
|
||||
input: *const c_char,
|
||||
@@ -154,4 +154,4 @@ pub fn decompress_into(input: &[u8], output: &mut Vec<u8>) -> Result<usize, Inva
|
||||
pub fn validate_compressed_buffer(input: &[u8]) -> bool {
|
||||
let status = unsafe { snappy_validate_compressed_buffer(input.as_ptr() as *const c_char, input.len() as size_t )};
|
||||
status == SNAPPY_OK
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user