Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a65a556239 | ||
|
|
268c6d15c0 | ||
|
|
88c3520563 | ||
|
|
1c45c910cc | ||
|
|
d6df25e871 | ||
|
|
036518d767 | ||
|
|
d8d7d9b2bc | ||
|
|
a50974e61b | ||
|
|
b669dbb390 | ||
|
|
45d8fc9bf1 | ||
|
|
2981a23880 | ||
|
|
76952ff363 | ||
|
|
1cd3e0f1cd | ||
|
|
8d76dd3127 | ||
|
|
e09d99b94c | ||
|
|
0cb2c866b9 | ||
|
|
b1fdafea3f | ||
|
|
116e18d2c6 | ||
|
|
652bb6bd94 | ||
|
|
08a6ce9642 | ||
|
|
e4ab13b4fa | ||
|
|
18939f7f69 | ||
|
|
9442e6c26a | ||
|
|
c2803d3d45 | ||
|
|
45e0d28129 | ||
|
|
080ec8043f | ||
|
|
7972c27b61 | ||
|
|
17c133cd01 | ||
|
|
6a4408cebc | ||
|
|
78d3f5fce9 | ||
|
|
93327e8c73 | ||
|
|
54da6aef33 | ||
|
|
22dc6e9ee9 | ||
|
|
ee0f1250ac | ||
|
|
8c01766d90 | ||
|
|
eb15c82fff | ||
|
|
e9f4819640 | ||
|
|
5618fa7a09 | ||
|
|
2f2614e0eb | ||
|
|
91903ac9e7 | ||
|
|
3327dc9aef | ||
|
|
b80aa9c3e4 | ||
|
|
d5ecd8f20f | ||
|
|
e9987c448e | ||
|
|
aac7815712 | ||
|
|
b3097c8036 | ||
|
|
e5ae24dfb6 | ||
|
|
1b36a2cd87 | ||
|
|
b078d71293 | ||
|
|
1df84a8e28 | ||
|
|
e0778fc069 | ||
|
|
2ce43434f1 | ||
|
|
18062f11dc | ||
|
|
d66bd3c83a | ||
|
|
935e8dcf56 | ||
|
|
3c2c356f73 | ||
|
|
6b4d0cea6b | ||
|
|
13de1ebc8e | ||
|
|
1fdb033db4 | ||
|
|
c1f0ea2016 | ||
|
|
964f556284 | ||
|
|
8fba01a208 | ||
|
|
77b49623ff | ||
|
|
2a29d7a76f | ||
|
|
66f684548d | ||
|
|
ccebe473d0 | ||
|
|
9707e61525 | ||
|
|
039594de92 | ||
|
|
69441b1322 | ||
|
|
8492e3e61d | ||
|
|
4b7c97783b | ||
|
|
6d14c5458b | ||
|
|
9239a7cd9e | ||
|
|
d27ecb6527 | ||
|
|
5502340dea | ||
|
|
2313f9b4f1 | ||
|
|
324a5873d7 | ||
|
|
b45e7f1c41 | ||
|
|
64af073bd4 | ||
|
|
fe83046198 | ||
|
|
8031910892 | ||
|
|
73a3dac38c | ||
|
|
bbaf5ed4f5 | ||
|
|
9cf777510f | ||
|
|
9b398421ce | ||
|
|
a618dcaaf0 | ||
|
|
50021c7611 | ||
|
|
f62d2e6ea8 | ||
|
|
7da8e7926d | ||
|
|
5363e75f3a | ||
|
|
31d667a29a | ||
|
|
ca2bc92dda | ||
|
|
a0b71a9f52 | ||
|
|
300c0c120b | ||
|
|
b228dc7b54 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,3 +30,5 @@
|
||||
|
||||
# Build artifacts
|
||||
out/
|
||||
|
||||
.vscode
|
||||
|
||||
105
.gitlab-ci.yml
105
.gitlab-ci.yml
@@ -5,6 +5,7 @@ variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
RUST_BACKTRACE: "1"
|
||||
RUSTFLAGS: "-D warnings"
|
||||
cache:
|
||||
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
||||
untracked: true
|
||||
@@ -19,11 +20,18 @@ linux-stable:
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
@@ -42,11 +50,18 @@ linux-stable-14.04:
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-14.04
|
||||
@@ -105,11 +120,11 @@ linux-centos:
|
||||
- export CC="gcc"
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- rust
|
||||
- rust-centos
|
||||
@@ -121,7 +136,6 @@ linux-armv7:
|
||||
stage: build
|
||||
image: ethcore/rust-armv7:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
@@ -133,11 +147,18 @@ linux-armv7:
|
||||
- cat .cargo/config
|
||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> checksum
|
||||
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh armhf
|
||||
- cp target/armv7-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -150,7 +171,6 @@ linux-arm:
|
||||
stage: build
|
||||
image: ethcore/rust-arm:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
@@ -162,11 +182,18 @@ linux-arm:
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> checksum
|
||||
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh armhf
|
||||
- cp target/arm-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -179,7 +206,6 @@ linux-armv6:
|
||||
stage: build
|
||||
image: ethcore/rust-armv6:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
@@ -191,11 +217,11 @@ linux-armv6:
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> checksum
|
||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity --body target/arm-unknown-linux-gnueabi/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -208,7 +234,6 @@ linux-aarch64:
|
||||
stage: build
|
||||
image: ethcore/rust-aarch64:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
@@ -220,11 +245,18 @@ linux-aarch64:
|
||||
- cat .cargo/config
|
||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> checksum
|
||||
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh arm64
|
||||
- cp target/aarch64-unknown-linux-gnu/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
||||
- md5sum "parity_"$VER"_arm64.deb" >> "parity_"$VER"_arm64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -242,11 +274,12 @@ darwin:
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- md5sum target/release/parity >> checksum
|
||||
- rm -rf parity.md5
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity.md5 --body parity.md5
|
||||
tags:
|
||||
- osx
|
||||
artifacts:
|
||||
@@ -264,20 +297,42 @@ windows:
|
||||
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
|
||||
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
|
||||
- set RUST_BACKTRACE=1
|
||||
- rustup default 1.11.0-x86_64-pc-windows-msvc
|
||||
- set RUSTFLAGS=%RUSTFLAGS% -D warnings
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo build --release --verbose
|
||||
- md5sum target\release\parity >> checksum
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||
- cd nsis
|
||||
- makensis.exe installer.nsi
|
||||
- copy installer.exe InstallParity.exe
|
||||
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
|
||||
- md5sums InstallParity.exe > InstallParity.exe.md5
|
||||
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||
- md5sums win-installer.zip > win-installer.zip.md5
|
||||
- cd ..\target\release\
|
||||
- md5sums parity.exe parity.pdb > parity.md5
|
||||
- md5sums parity.exe > parity.exe.md5
|
||||
- zip parity.zip parity.exe parity.pdb parity.md5
|
||||
- md5sums parity.zip > parity.zip.md5
|
||||
- cd ..\..
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
- aws configure set aws_secret_access_key %s3_secret%
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.pdb
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/checksum --body checksum
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
||||
tags:
|
||||
- rust-windows
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity.exe
|
||||
- target/release/parity.pdb
|
||||
- nsis/InstallParity.exe
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
test-linux:
|
||||
stage: test
|
||||
|
||||
94
Cargo.lock
generated
94
Cargo.lock
generated
@@ -1,12 +1,12 @@
|
||||
[root]
|
||||
name = "parity"
|
||||
version = "1.3.3"
|
||||
version = "1.3.15"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
|
||||
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.3.0",
|
||||
"ethcore-dapps 1.3.0",
|
||||
@@ -20,18 +20,18 @@ dependencies = [
|
||||
"ethcore-logger 1.3.0",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-signer 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"ethsync 1.3.0",
|
||||
"fdlimit 0.1.0",
|
||||
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)",
|
||||
"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)",
|
||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -192,7 +192,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -208,12 +208,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "docopt"
|
||||
version = "0.6.80"
|
||||
version = "0.6.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -236,7 +237,7 @@ version = "0.5.4"
|
||||
source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
|
||||
dependencies = [
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -269,7 +270,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"ethjson 0.1.0",
|
||||
"ethstore 0.1.0",
|
||||
"evmjit 1.3.0",
|
||||
@@ -293,10 +294,10 @@ version = "1.3.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
@@ -335,7 +336,7 @@ name = "ethcore-ipc"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"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)",
|
||||
]
|
||||
@@ -380,7 +381,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"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)",
|
||||
@@ -392,7 +393,7 @@ name = "ethcore-logger"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"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)",
|
||||
@@ -407,7 +408,7 @@ dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -431,12 +432,12 @@ dependencies = [
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"ethjson 0.1.0",
|
||||
"ethsync 1.3.0",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -454,7 +455,7 @@ dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
@@ -465,7 +466,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.3.3"
|
||||
version = "1.3.15"
|
||||
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)",
|
||||
@@ -498,7 +499,7 @@ dependencies = [
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -546,7 +547,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-network 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.15",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -571,8 +572,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.28"
|
||||
version = "0.3.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
@@ -706,7 +710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "json-ipc-server"
|
||||
version = "0.2.4"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git#56b6307130710ebc73cb9be087b6ed0b6c400bcf"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git?branch=beta#d9c0811fb0c4aaa5b8b98905b2d72d277675d60f"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -732,7 +736,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "6.1.0"
|
||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#4e3f93eb79125e91a46e04d77c25ff8885498b86"
|
||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta#ab0e2cdc8856a4ee26f65c395583e69fd4609c88"
|
||||
dependencies = [
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -885,7 +889,7 @@ name = "nanomsg-sys"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1012,7 +1016,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1221,7 +1225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
@@ -1230,9 +1234,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocksdb-sys"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1249,7 +1253,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1263,7 +1267,7 @@ name = "rust-crypto"
|
||||
version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1331,7 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "sha3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1375,7 +1379,7 @@ source = "git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47#3c5cd1ca4
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.3.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -1641,13 +1645,13 @@ dependencies = [
|
||||
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
||||
"checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc"
|
||||
"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "<none>"
|
||||
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
|
||||
"checksum daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0239832c1b4ca406d5ec73728cf4c7336d25cf85dd32db9e047e9e706ee0e935"
|
||||
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
||||
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
|
||||
"checksum docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)" = "4a7ef30445607f6fc8720f0a0a2c7442284b629cf0d049286860fae23e71c4d9"
|
||||
"checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
|
||||
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
||||
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
||||
"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
|
||||
"checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
|
||||
"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
|
||||
@@ -1660,9 +1664,9 @@ dependencies = [
|
||||
"checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484"
|
||||
"checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c"
|
||||
"checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76"
|
||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)" = "<none>"
|
||||
"checksum jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec4477e4e8218da23caa5dd31f4eb39999aa0ea9035660617eccfb19a23bf5ad"
|
||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||
@@ -1693,7 +1697,7 @@ dependencies = [
|
||||
"checksum num-rational 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "48cdcc9ff4ae2a8296805ac15af88b3d88ce62128ded0cb74ffb63a587502a84"
|
||||
"checksum num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39"
|
||||
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
||||
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
||||
"checksum number_prefix 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b09f7c223751819881e1b95670d36fb12cdaa89848147f2222f1a2b691d5f0a3"
|
||||
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
||||
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
@@ -1721,7 +1725,7 @@ dependencies = [
|
||||
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||
"checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "<none>"
|
||||
"checksum rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d3a99497c5c544e629cc8b359ae5ede321eba5fa8e5a8078f3ced727a976c3f"
|
||||
"checksum rpassword 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "320da1dfcf5c570a6c07ff60bb7cd4cdc986d2ea89caea139f2247371ab6a1df"
|
||||
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
||||
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||
@@ -1738,7 +1742,7 @@ dependencies = [
|
||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||
"checksum stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)" = "<none>"
|
||||
"checksum strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825"
|
||||
"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e"
|
||||
"checksum syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393b6dd0889df2b064beeea954cfda6bc2571604ac460deeae0fed55a53988af"
|
||||
"checksum syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44bded3cabafc65c90b663b1071bd2d198a9ab7515e6ce729e4570aaf53c407e"
|
||||
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Ethcore client."
|
||||
name = "parity"
|
||||
version = "1.3.3"
|
||||
version = "1.3.15"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
@@ -39,7 +39,7 @@ ethcore-ipc-nano = { path = "ipc/nano" }
|
||||
ethcore-ipc = { path = "ipc/rpc" }
|
||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git", branch = "beta" }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ environment:
|
||||
certpass:
|
||||
secure: 0BgXJqxq9Ei34/hZ7121FQ==
|
||||
keyfile: C:\users\appveyor\Certificates.p12
|
||||
RUSTFLAGS: -Zorbit=off
|
||||
|
||||
branches:
|
||||
only:
|
||||
@@ -18,10 +19,10 @@ branches:
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
- ps: Install-Product node 6
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.10.0-x86_64-pc-windows-msvc.exe"
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.12.0-x86_64-pc-windows-msvc.exe"
|
||||
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll
|
||||
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe
|
||||
- rust-1.10.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- rust-1.12.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
@@ -11,7 +11,7 @@ build = "build.rs"
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
jsonrpc-core = "2.1"
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git", branch = "beta" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
unicase = "1.3"
|
||||
url = "1.0"
|
||||
|
||||
40
docker/ubuntu-stable/Dockerfile
Normal file
40
docker/ubuntu-stable/Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
g++ \
|
||||
curl \
|
||||
git \
|
||||
file \
|
||||
binutils \
|
||||
make
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
# rustup directory
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
git checkout stable && \
|
||||
git pull && \
|
||||
cargo build --release --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
|
||||
RUN file /build/parity/target/release/parity
|
||||
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/build/parity/target/release/parity"]
|
||||
@@ -18,6 +18,6 @@ extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/traits.rs").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/chain_notify.rs").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc_cond("src/client/traits.rs", cfg!(feature="ipc")).unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc_cond("src/client/chain_notify.rs", cfg!(feature="ipc")).unwrap();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
"homesteadTransition": "0x118c30",
|
||||
"eip150Transition": "0x2625a0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
47
ethcore/res/ethereum/eip150_test.json
Normal file
47
ethcore/res/ethereum/eip150_test.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
47
ethcore/res/ethereum/eip161_test.json
Normal file
47
ethcore/res/ethereum/eip161_test.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x0",
|
||||
"eip161abcTransition": "0x0",
|
||||
"eip161dTransition": "0x0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
74
ethcore/res/ethereum/expanse.json
Normal file
74
ethcore/res/ethereum/expanse.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "Expanse",
|
||||
"forkName": "expanse",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"difficultyIncrementDivisor": "60",
|
||||
"durationLimit": "0x3C",
|
||||
"blockReward": "0x6f05b59d3b200000",
|
||||
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
||||
"homesteadTransition": "0x30d40",
|
||||
"difficultyHardforkTransition": "0x59d9",
|
||||
"difficultyHardforkBoundDivisor": "0x0200",
|
||||
"bombDefuseTransition": "0x30d40",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID": "0x1",
|
||||
"subprotocolName": "exp"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x214652414e4b4f21",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x40000000",
|
||||
"author": "0x93decab0cd745598860f782ac1e8f046cb99e898",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x4672616e6b6f497346726565646f6d",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://7f335a047654f3e70d6f91312a7cf89c39704011f1a584e2698250db3d63817e74b88e26b7854111e16b2c9d0c7173c05419aeee2d0321850227b126d8b1be3f@46.101.156.249:42786",
|
||||
"enode://df872f81e25f72356152b44cab662caf1f2e57c3a156ecd20e9ac9246272af68a2031b4239a0bc831f2c6ab34733a041464d46b3ea36dce88d6c11714446e06b@178.62.208.109:42786",
|
||||
"enode://96d3919b903e7f5ad59ac2f73c43be9172d9d27e2771355db03fd194732b795829a31fe2ea6de109d0804786c39a807e155f065b4b94c6fce167becd0ac02383@45.55.22.34:42786",
|
||||
"enode://5f6c625bf287e3c08aad568de42d868781e961cbda805c8397cfb7be97e229419bef9a5a25a75f97632787106bba8a7caf9060fab3887ad2cfbeb182ab0f433f@46.101.182.53:42786",
|
||||
"enode://d33a8d4c2c38a08971ed975b750f21d54c927c0bf7415931e214465a8d01651ecffe4401e1db913f398383381413c78105656d665d83f385244ab302d6138414@128.199.183.48:42786",
|
||||
"enode://df872f81e25f72356152b44cab662caf1f2e57c3a156ecd20e9ac9246272af68a2031b4239a0bc831f2c6ab34733a041464d46b3ea36dce88d6c11714446e06b@178.62.208.109:42786",
|
||||
"enode://f6f0d6b9b7d02ec9e8e4a16e38675f3621ea5e69860c739a65c1597ca28aefb3cec7a6d84e471ac927d42a1b64c1cbdefad75e7ce8872d57548ddcece20afdd1@159.203.64.95:42786"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"bb94f0ceb32257275b2a7a9c094c13e469b4563e": {
|
||||
"balance": "10000000000000000000000000"
|
||||
},
|
||||
"15656715068ab0dbdf0ab00748a8a19e40f28192": {
|
||||
"balance": "1000000000000000000000000"
|
||||
},
|
||||
"c075fa11f85bda3aaba67106226aaf086ac16f4e": {
|
||||
"balance": "100000000000000000000000"
|
||||
},
|
||||
"93decab0cd745598860f782ac1e8f046cb99e898": {
|
||||
"balance": "10000000000000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
"daoHardforkAccounts": [
|
||||
"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728",
|
||||
"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425",
|
||||
"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f",
|
||||
@@ -129,7 +129,13 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0x259518",
|
||||
"eip155Transition": "0x28d138",
|
||||
"eip160Transition": "0x28d138",
|
||||
"eip161abcTransition": "0x28d138",
|
||||
"eip161dTransition": "0x28d138",
|
||||
"maxCodeSize": 24576
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -160,7 +166,7 @@
|
||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
|
||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||
"enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io:30303"
|
||||
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
@@ -129,7 +129,12 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
"homesteadTransition": "0x7fffffffffffffff",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x0"
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0x8e4e9b13d4b45cb0befc93c3061b1408f67316b2",
|
||||
"frontierCompatibilityModeLimit": "0x789b0"
|
||||
"homesteadTransition": "0x789b0",
|
||||
"eip150Transition": "0x1b34d8",
|
||||
"eip155Transition": "0x1cc348",
|
||||
"eip160Transition": "0x1cc348",
|
||||
"eip161abcTransition": "0x1cc348",
|
||||
"eip161dTransition": "0x1cc348"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -17,7 +22,9 @@
|
||||
"accountStartNonce": "0x0100000",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
"networkID" : "0x2",
|
||||
"forkBlock": "0x1b34d8",
|
||||
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"homesteadTransition": "0x7fffffffffffffff",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
304
ethcore/res/ethereum/ropsten.json
Normal file
304
ethcore/res/ethereum/ropsten.json
Normal file
@@ -0,0 +1,304 @@
|
||||
{
|
||||
"name": "Ropsten",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2",
|
||||
"homesteadTransition": 0,
|
||||
"eip150Transition": 0,
|
||||
"eip155Transition": 10,
|
||||
"eip160Transition": 10,
|
||||
"eip161abcTransition": 10,
|
||||
"eip161dTransition": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x3"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x100000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x3535353535353535353535353535353535353535353535353535353535353535",
|
||||
"gasLimit": "0x1000000"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://a22f0977ce02653bf95e38730106356342df48b5222e2c2a1a6f9ef34769bf593bae9ca0a888cf60839edd52efc1b6e393c63a57d76f4c4fe14e641f1f9e637e@128.199.55.137:30303",
|
||||
"enode://012239fccf3ff1d92b036983a430cb6705c6528c96c0354413f8854802138e5135c084ab36e7c54efb621c46728df8c3a6f4c1db9bb48a1330efe3f82f2dd7a6@52.169.94.142:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "0", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "0", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "0", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"0000000000000000000000000000000000000000": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000005": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000006": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000007": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000008": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000009": { "balance": "1" },
|
||||
"000000000000000000000000000000000000000a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000000b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000000c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000000d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000000e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000000f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000010": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000011": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000012": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000013": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000014": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000015": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000016": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000017": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000018": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000019": { "balance": "0" },
|
||||
"000000000000000000000000000000000000001a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000001b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000001c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000001d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000001e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000001f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000020": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000021": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000022": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000023": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000024": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000025": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000026": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000027": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000028": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000029": { "balance": "0" },
|
||||
"000000000000000000000000000000000000002a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000002b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000002c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000002d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000002e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000002f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000030": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000031": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000032": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000033": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000034": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000035": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000036": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000037": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000038": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000039": { "balance": "0" },
|
||||
"000000000000000000000000000000000000003a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000003b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000003c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000003d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000003e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000003f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000040": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000041": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000042": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000043": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000044": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000045": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000046": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000047": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000048": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000049": { "balance": "0" },
|
||||
"000000000000000000000000000000000000004a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000004b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000004c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000004d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000004e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000004f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000050": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000051": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000052": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000053": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000054": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000055": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000056": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000057": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000058": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000059": { "balance": "0" },
|
||||
"000000000000000000000000000000000000005a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000005b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000005c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000005d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000005e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000005f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000060": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000061": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000062": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000063": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000064": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000065": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000066": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000067": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000068": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000069": { "balance": "0" },
|
||||
"000000000000000000000000000000000000006a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000006b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000006c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000006d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000006e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000006f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000070": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000071": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000072": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000073": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000074": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000075": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000076": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000077": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000078": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000079": { "balance": "0" },
|
||||
"000000000000000000000000000000000000007a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000007b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000007c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000007d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000007e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000007f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000080": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000081": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000082": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000083": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000084": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000085": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000086": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000087": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000088": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000089": { "balance": "0" },
|
||||
"000000000000000000000000000000000000008a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000008b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000008c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000008d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000008e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000008f": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000090": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000091": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000092": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000093": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000094": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000095": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000096": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000097": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000098": { "balance": "0" },
|
||||
"0000000000000000000000000000000000000099": { "balance": "0" },
|
||||
"000000000000000000000000000000000000009a": { "balance": "0" },
|
||||
"000000000000000000000000000000000000009b": { "balance": "0" },
|
||||
"000000000000000000000000000000000000009c": { "balance": "0" },
|
||||
"000000000000000000000000000000000000009d": { "balance": "0" },
|
||||
"000000000000000000000000000000000000009e": { "balance": "0" },
|
||||
"000000000000000000000000000000000000009f": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a0": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a1": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a2": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a3": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a4": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a5": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a6": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a7": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a8": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000a9": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000aa": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ab": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ac": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ad": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ae": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000af": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b0": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b1": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b2": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b3": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b4": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b5": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b6": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b7": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b8": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000b9": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ba": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000bb": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000bc": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000bd": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000be": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000bf": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c0": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c1": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c2": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c3": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c4": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c5": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c6": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c7": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c8": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000c9": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ca": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000cb": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000cc": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000cd": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ce": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000cf": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d0": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d1": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d2": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d3": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d4": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d5": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d6": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d7": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d8": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000d9": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000da": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000db": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000dc": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000dd": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000de": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000df": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e0": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e1": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e2": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e3": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e4": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e5": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e6": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e7": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e8": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000e9": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ea": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000eb": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ec": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ed": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ee": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ef": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f0": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f1": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f2": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f3": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f4": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f5": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f6": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f7": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f8": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000f9": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000fa": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000fb": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000fc": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000fd": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000fe": { "balance": "0" },
|
||||
"00000000000000000000000000000000000000ff": { "balance": "0" },
|
||||
"874b54a8bd152966d63f706bae1ffeb0411921e5": { "balance": "1000000000000000000000000000000" }
|
||||
}
|
||||
}
|
||||
Submodule ethcore/res/ethereum/tests updated: ac5475d676...e8f4624b7f
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "DAO hard-fork consensus test",
|
||||
"name": "EIP150.1b hard-fork consensus test",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
@@ -9,7 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x5",
|
||||
"homesteadTransition": "0x5",
|
||||
"daoHardforkTransition": "0x8",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
@@ -129,7 +129,12 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0xa",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
//! Single account in the system.
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use util::*;
|
||||
use pod_account::*;
|
||||
use account_db::*;
|
||||
@@ -24,9 +23,11 @@ use lru_cache::LruCache;
|
||||
|
||||
use std::cell::{RefCell, Cell};
|
||||
|
||||
const STORAGE_CACHE_ITEMS: usize = 4096;
|
||||
const STORAGE_CACHE_ITEMS: usize = 8192;
|
||||
|
||||
/// Single account in the system.
|
||||
/// Keeps track of changes to the code and storage.
|
||||
/// The changes are applied in `commit_storage` and `commit_code`
|
||||
pub struct Account {
|
||||
// Balance of the account.
|
||||
balance: U256,
|
||||
@@ -46,8 +47,6 @@ pub struct Account {
|
||||
code_size: Option<u64>,
|
||||
// Code cache of the account.
|
||||
code_cache: Arc<Bytes>,
|
||||
// Account is new or has been modified.
|
||||
filth: Filth,
|
||||
// Account code new or has been modified.
|
||||
code_filth: Filth,
|
||||
// Cached address hash.
|
||||
@@ -67,7 +66,6 @@ impl Account {
|
||||
code_hash: code.sha3(),
|
||||
code_size: Some(code.len() as u64),
|
||||
code_cache: Arc::new(code),
|
||||
filth: Filth::Dirty,
|
||||
code_filth: Filth::Dirty,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -89,7 +87,6 @@ impl Account {
|
||||
code_filth: Filth::Dirty,
|
||||
code_size: Some(pod.code.as_ref().map_or(0, |c| c.len() as u64)),
|
||||
code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)),
|
||||
filth: Filth::Dirty,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
@@ -105,7 +102,6 @@ impl Account {
|
||||
code_hash: SHA3_EMPTY,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: Some(0),
|
||||
filth: Filth::Dirty,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -123,7 +119,6 @@ impl Account {
|
||||
code_hash: r.val_at(3),
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
filth: Filth::Clean,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -141,7 +136,6 @@ impl Account {
|
||||
code_hash: SHA3_EMPTY,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
filth: Filth::Dirty,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -153,7 +147,6 @@ impl Account {
|
||||
self.code_hash = code.sha3();
|
||||
self.code_cache = Arc::new(code);
|
||||
self.code_size = Some(self.code_cache.len() as u64);
|
||||
self.filth = Filth::Dirty;
|
||||
self.code_filth = Filth::Dirty;
|
||||
}
|
||||
|
||||
@@ -164,17 +157,7 @@ impl Account {
|
||||
|
||||
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
|
||||
pub fn set_storage(&mut self, key: H256, value: H256) {
|
||||
match self.storage_changes.entry(key) {
|
||||
Entry::Occupied(ref mut entry) if entry.get() != &value => {
|
||||
entry.insert(value);
|
||||
self.filth = Filth::Dirty;
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
self.filth = Filth::Dirty;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
self.storage_changes.insert(key, value);
|
||||
}
|
||||
|
||||
/// Get (and cache) the contents of the trie's storage at `key`.
|
||||
@@ -263,17 +246,6 @@ impl Account {
|
||||
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == SHA3_EMPTY)
|
||||
}
|
||||
|
||||
/// Is this a new or modified account?
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.filth == Filth::Dirty || self.code_filth == Filth::Dirty || !self.storage_is_clean()
|
||||
}
|
||||
|
||||
/// Mark account as clean.
|
||||
pub fn set_clean(&mut self) {
|
||||
assert!(self.storage_is_clean());
|
||||
self.filth = Filth::Clean
|
||||
}
|
||||
|
||||
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
|
||||
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
|
||||
// TODO: fill out self.code_cache;
|
||||
@@ -316,6 +288,21 @@ impl Account {
|
||||
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
||||
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
|
||||
|
||||
/// Check if account has zero nonce, balance, no code and no storage.
|
||||
///
|
||||
/// NOTE: Will panic if `!self.storage_is_clean()`
|
||||
pub fn is_empty(&self) -> bool {
|
||||
assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean.");
|
||||
self.is_null() && self.storage_root == SHA3_NULL_RLP
|
||||
}
|
||||
|
||||
/// Check if account has zero nonce, balance, no code.
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.balance.is_zero() &&
|
||||
self.nonce.is_zero() &&
|
||||
self.code_hash == SHA3_EMPTY
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// return the storage root associated with this account or None if it has been altered via the overlay.
|
||||
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
|
||||
@@ -326,25 +313,18 @@ impl Account {
|
||||
/// Increment the nonce of the account by one.
|
||||
pub fn inc_nonce(&mut self) {
|
||||
self.nonce = self.nonce + U256::from(1u8);
|
||||
self.filth = Filth::Dirty;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
/// Increase account balance.
|
||||
pub fn add_balance(&mut self, x: &U256) {
|
||||
if !x.is_zero() {
|
||||
self.balance = self.balance + *x;
|
||||
self.filth = Filth::Dirty;
|
||||
}
|
||||
self.balance = self.balance + *x;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
/// Decrease account balance.
|
||||
/// Panics if balance is less than `x`
|
||||
pub fn sub_balance(&mut self, x: &U256) {
|
||||
if !x.is_zero() {
|
||||
assert!(self.balance >= *x);
|
||||
self.balance = self.balance - *x;
|
||||
self.filth = Filth::Dirty;
|
||||
}
|
||||
assert!(self.balance >= *x);
|
||||
self.balance = self.balance - *x;
|
||||
}
|
||||
|
||||
/// Commit the `storage_changes` to the backing DB and update `storage_root`.
|
||||
@@ -406,7 +386,6 @@ impl Account {
|
||||
code_hash: self.code_hash.clone(),
|
||||
code_size: self.code_size.clone(),
|
||||
code_cache: self.code_cache.clone(),
|
||||
filth: self.filth,
|
||||
code_filth: self.code_filth,
|
||||
address_hash: self.address_hash.clone(),
|
||||
}
|
||||
@@ -427,10 +406,10 @@ impl Account {
|
||||
account
|
||||
}
|
||||
|
||||
/// Replace self with the data from other account merging storage cache
|
||||
pub fn merge_with(&mut self, other: Account) {
|
||||
assert!(self.storage_is_clean());
|
||||
assert!(other.storage_is_clean());
|
||||
/// Replace self with the data from other account merging storage cache.
|
||||
/// Basic account data and all modifications are overwritten
|
||||
/// with new values.
|
||||
pub fn overwrite_with(&mut self, other: Account) {
|
||||
self.balance = other.balance;
|
||||
self.nonce = other.nonce;
|
||||
self.storage_root = other.storage_root;
|
||||
@@ -443,6 +422,7 @@ impl Account {
|
||||
for (k, v) in other.storage_cache.into_inner().into_iter() {
|
||||
cache.insert(k.clone() , v.clone()); //TODO: cloning should not be required here
|
||||
}
|
||||
self.storage_changes = other.storage_changes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -395,6 +395,10 @@ impl<'x> OpenBlock<'x> {
|
||||
uncle_bytes: uncle_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Return mutable block reference. To be used in tests only.
|
||||
pub fn block_mut (&mut self) -> &mut ExecutedBlock { &mut self.block }
|
||||
}
|
||||
|
||||
impl<'x> IsBlock for OpenBlock<'x> {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//! A queue of blocks. Sits between network or other I/O and the `BlockChain`.
|
||||
//! Sorts them ready for blockchain insertion.
|
||||
use std::thread::{JoinHandle, self};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
|
||||
use std::sync::{Condvar as SCondvar, Mutex as SMutex};
|
||||
use util::*;
|
||||
use io::*;
|
||||
@@ -76,6 +76,13 @@ impl BlockQueueInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// the internal queue sizes.
|
||||
struct Sizes {
|
||||
unverified: AtomicUsize,
|
||||
verifying: AtomicUsize,
|
||||
verified: AtomicUsize,
|
||||
}
|
||||
|
||||
/// A queue of blocks. Sits between network or other I/O and the `BlockChain`.
|
||||
/// Sorts them ready for blockchain insertion.
|
||||
pub struct BlockQueue {
|
||||
@@ -110,7 +117,21 @@ struct QueueSignal {
|
||||
|
||||
impl QueueSignal {
|
||||
#[cfg_attr(feature="dev", allow(bool_comparison))]
|
||||
fn set(&self) {
|
||||
fn set_sync(&self) {
|
||||
// Do not signal when we are about to close
|
||||
if self.deleting.load(AtomicOrdering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||
if let Err(e) = self.message_channel.send_sync(ClientIoMessage::BlockVerified) {
|
||||
debug!("Error sending BlockVerified message: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(bool_comparison))]
|
||||
fn set_async(&self) {
|
||||
// Do not signal when we are about to close
|
||||
if self.deleting.load(AtomicOrdering::Relaxed) {
|
||||
return;
|
||||
@@ -131,11 +152,12 @@ impl QueueSignal {
|
||||
struct Verification {
|
||||
// All locks must be captured in the order declared here.
|
||||
unverified: Mutex<VecDeque<UnverifiedBlock>>,
|
||||
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||
verifying: Mutex<VecDeque<VerifyingBlock>>,
|
||||
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||
bad: Mutex<HashSet<H256>>,
|
||||
more_to_verify: SMutex<()>,
|
||||
empty: SMutex<()>,
|
||||
sizes: Sizes,
|
||||
}
|
||||
|
||||
impl BlockQueue {
|
||||
@@ -143,12 +165,16 @@ impl BlockQueue {
|
||||
pub fn new(config: BlockQueueConfig, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>) -> BlockQueue {
|
||||
let verification = Arc::new(Verification {
|
||||
unverified: Mutex::new(VecDeque::new()),
|
||||
verified: Mutex::new(VecDeque::new()),
|
||||
verifying: Mutex::new(VecDeque::new()),
|
||||
verified: Mutex::new(VecDeque::new()),
|
||||
bad: Mutex::new(HashSet::new()),
|
||||
more_to_verify: SMutex::new(()),
|
||||
empty: SMutex::new(()),
|
||||
|
||||
sizes: Sizes {
|
||||
unverified: AtomicUsize::new(0),
|
||||
verifying: AtomicUsize::new(0),
|
||||
verified: AtomicUsize::new(0),
|
||||
}
|
||||
});
|
||||
let more_to_verify = Arc::new(SCondvar::new());
|
||||
let deleting = Arc::new(AtomicBool::new(false));
|
||||
@@ -221,16 +247,18 @@ impl BlockQueue {
|
||||
}
|
||||
let mut verifying = verification.verifying.lock();
|
||||
let block = unverified.pop_front().unwrap();
|
||||
verification.sizes.unverified.fetch_sub(block.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
|
||||
block
|
||||
};
|
||||
|
||||
let block_hash = block.header.hash();
|
||||
match verify_block_unordered(block.header, block.bytes, &*engine) {
|
||||
let is_ready = match verify_block_unordered(block.header, block.bytes, &*engine) {
|
||||
Ok(verified) => {
|
||||
let mut verifying = verification.verifying.lock();
|
||||
for e in verifying.iter_mut() {
|
||||
if e.hash == block_hash {
|
||||
verification.sizes.verifying.fetch_add(verified.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
e.block = Some(verified);
|
||||
break;
|
||||
}
|
||||
@@ -239,8 +267,10 @@ impl BlockQueue {
|
||||
// we're next!
|
||||
let mut verified = verification.verified.lock();
|
||||
let mut bad = verification.bad.lock();
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||
ready.set();
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
@@ -250,23 +280,39 @@ impl BlockQueue {
|
||||
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
|
||||
bad.insert(block_hash.clone());
|
||||
verifying.retain(|e| e.hash != block_hash);
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||
ready.set();
|
||||
if !verifying.is_empty() && verifying.front().unwrap().hash == block_hash {
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
if is_ready {
|
||||
// Import the block immediately
|
||||
ready.set_sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
|
||||
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>, sizes: &Sizes) {
|
||||
let mut removed_size = 0;
|
||||
let mut inserted_size = 0;
|
||||
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
|
||||
let block = verifying.pop_front().unwrap().block.unwrap();
|
||||
if bad.contains(&block.header.parent_hash) {
|
||||
let size = block.heap_size_of_children();
|
||||
removed_size += size;
|
||||
|
||||
if bad.contains(&block.header.parent_hash()) {
|
||||
bad.insert(block.header.hash());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
inserted_size += size;
|
||||
verified.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
sizes.verifying.fetch_sub(removed_size, AtomicOrdering::SeqCst);
|
||||
sizes.verified.fetch_add(inserted_size, AtomicOrdering::SeqCst);
|
||||
}
|
||||
|
||||
/// Clear the queue and stop verification activity.
|
||||
@@ -277,6 +323,12 @@ impl BlockQueue {
|
||||
unverified.clear();
|
||||
verifying.clear();
|
||||
verified.clear();
|
||||
|
||||
let sizes = &self.verification.sizes;
|
||||
sizes.unverified.store(0, AtomicOrdering::Release);
|
||||
sizes.verifying.store(0, AtomicOrdering::Release);
|
||||
sizes.verified.store(0, AtomicOrdering::Release);
|
||||
|
||||
self.processing.write().clear();
|
||||
}
|
||||
|
||||
@@ -322,7 +374,9 @@ impl BlockQueue {
|
||||
match verify_block_basic(&header, &bytes, &*self.engine) {
|
||||
Ok(()) => {
|
||||
self.processing.write().insert(h.clone());
|
||||
self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes });
|
||||
let block = UnverifiedBlock { header: header, bytes: bytes };
|
||||
self.verification.sizes.unverified.fetch_add(block.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
self.verification.unverified.lock().push_back(block);
|
||||
self.more_to_verify.notify_all();
|
||||
Ok(h)
|
||||
},
|
||||
@@ -350,26 +404,32 @@ impl BlockQueue {
|
||||
}
|
||||
|
||||
let mut new_verified = VecDeque::new();
|
||||
let mut removed_size = 0;
|
||||
for block in verified.drain(..) {
|
||||
if bad.contains(&block.header.parent_hash) {
|
||||
removed_size += block.heap_size_of_children();
|
||||
bad.insert(block.header.hash());
|
||||
processing.remove(&block.header.hash());
|
||||
} else {
|
||||
new_verified.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
self.verification.sizes.verified.fetch_sub(removed_size, AtomicOrdering::SeqCst);
|
||||
*verified = new_verified;
|
||||
}
|
||||
|
||||
/// Mark given block as processed
|
||||
pub fn mark_as_good(&self, block_hashes: &[H256]) {
|
||||
if block_hashes.is_empty() {
|
||||
return;
|
||||
/// Mark given item as processed.
|
||||
/// Returns true if the queue becomes empty.
|
||||
pub fn mark_as_good(&self, hashes: &[H256]) -> bool {
|
||||
if hashes.is_empty() {
|
||||
return self.processing.read().is_empty();
|
||||
}
|
||||
let mut processing = self.processing.write();
|
||||
for hash in block_hashes {
|
||||
for hash in hashes {
|
||||
processing.remove(hash);
|
||||
}
|
||||
processing.is_empty()
|
||||
}
|
||||
|
||||
/// Removes up to `max` verified blocks from the queue
|
||||
@@ -379,28 +439,34 @@ impl BlockQueue {
|
||||
let mut result = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
let block = verified.pop_front().unwrap();
|
||||
self.verification.sizes.verified.fetch_sub(block.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
result.push(block);
|
||||
}
|
||||
self.ready_signal.reset();
|
||||
if !verified.is_empty() {
|
||||
self.ready_signal.set();
|
||||
self.ready_signal.set_async();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Get queue status.
|
||||
pub fn queue_info(&self) -> BlockQueueInfo {
|
||||
use std::mem::size_of;
|
||||
let (unverified_len, unverified_bytes) = {
|
||||
let v = self.verification.unverified.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
let len = self.verification.unverified.lock().len();
|
||||
let size = self.verification.sizes.unverified.load(AtomicOrdering::Acquire);
|
||||
|
||||
(len, size + len * size_of::<UnverifiedBlock>())
|
||||
};
|
||||
let (verifying_len, verifying_bytes) = {
|
||||
let v = self.verification.verifying.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
let len = self.verification.verifying.lock().len();
|
||||
let size = self.verification.sizes.verifying.load(AtomicOrdering::Acquire);
|
||||
(len, size + len * size_of::<VerifyingBlock>())
|
||||
};
|
||||
let (verified_len, verified_bytes) = {
|
||||
let v = self.verification.verified.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
let len = self.verification.verified.lock().len();
|
||||
let size = self.verification.sizes.verified.load(AtomicOrdering::Acquire);
|
||||
(len, size + len * size_of::<PreverifiedBlock>())
|
||||
};
|
||||
BlockQueueInfo {
|
||||
unverified_queue_size: unverified_len,
|
||||
@@ -408,12 +474,9 @@ impl BlockQueue {
|
||||
verified_queue_size: verified_len,
|
||||
max_queue_size: self.max_queue_size,
|
||||
max_mem_use: self.max_mem_use,
|
||||
mem_used:
|
||||
unverified_bytes
|
||||
+ verifying_bytes
|
||||
+ verified_bytes
|
||||
// TODO: https://github.com/servo/heapsize/pull/50
|
||||
//+ self.processing.read().heap_size_of_children(),
|
||||
mem_used: unverified_bytes
|
||||
+ verifying_bytes
|
||||
+ verified_bytes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1279,7 +1279,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
|
||||
let b1a = canon_chain
|
||||
@@ -1343,7 +1343,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let t2 = Transaction {
|
||||
nonce: 1.into(),
|
||||
@@ -1352,7 +1352,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let t3 = Transaction {
|
||||
nonce: 2.into(),
|
||||
@@ -1361,7 +1361,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let b1a = canon_chain
|
||||
.with_transaction(t1.clone())
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::*;
|
||||
use ipc::{IpcConfig, BinaryConvertError};
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use ipc::{IpcConfig};
|
||||
|
||||
/// Represents what has to be handled by actor listening to chain events
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
pub trait ChainNotify : Send + Sync {
|
||||
/// fires when chain has new blocks
|
||||
fn new_blocks(&self,
|
||||
|
||||
@@ -17,6 +17,7 @@ use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::path::{Path};
|
||||
use std::fmt;
|
||||
use std::cmp;
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::time::{Instant};
|
||||
use time::precise_time_ns;
|
||||
@@ -33,7 +34,7 @@ use io::*;
|
||||
use views::{BlockView, HeaderView, BodyView};
|
||||
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult};
|
||||
use header::BlockNumber;
|
||||
use state::State;
|
||||
use state::{State, CleanupMode};
|
||||
use spec::Spec;
|
||||
use basic_types::Seal;
|
||||
use engines::Engine;
|
||||
@@ -73,6 +74,7 @@ pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||
|
||||
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
||||
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
||||
const MIN_HISTORY_SIZE: u64 = 8;
|
||||
|
||||
impl fmt::Display for BlockChainInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@@ -139,10 +141,9 @@ pub struct Client {
|
||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||
queue_transactions: AtomicUsize,
|
||||
last_hashes: RwLock<VecDeque<H256>>,
|
||||
history: u64,
|
||||
}
|
||||
|
||||
const HISTORY: u64 = 1200;
|
||||
|
||||
// database columns
|
||||
/// Column for State
|
||||
pub const DB_COL_STATE: Option<u32> = Some(0);
|
||||
@@ -159,13 +160,6 @@ pub const DB_COL_ACCOUNT_BLOOM: Option<u32> = Some(5);
|
||||
/// Number of columns in DB
|
||||
pub const DB_NO_OF_COLUMNS: Option<u32> = Some(6);
|
||||
|
||||
/// Append a path element to the given path and return the string.
|
||||
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {
|
||||
let mut p = path.as_ref().to_path_buf();
|
||||
p.push(item);
|
||||
p.to_str().unwrap().to_owned()
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new client with given spec and DB path and custom verifier.
|
||||
pub fn new(
|
||||
@@ -182,7 +176,7 @@ impl Client {
|
||||
db_config.compaction = config.db_compaction.compaction_profile();
|
||||
db_config.wal = config.db_wal;
|
||||
|
||||
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
|
||||
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().expect("DB path could not be converted to string.")).map_err(ClientError::Database)));
|
||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
|
||||
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
|
||||
|
||||
@@ -194,6 +188,19 @@ impl Client {
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
}
|
||||
|
||||
trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());
|
||||
let history = cmp::max(MIN_HISTORY_SIZE, config.history);
|
||||
if let (Some(earliest), Some(latest)) = (state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()) {
|
||||
if latest > earliest && latest - earliest > history {
|
||||
for era in earliest..(latest - history + 1) {
|
||||
trace!("Removing era {}", era);
|
||||
let batch = DBTransaction::new(&db);
|
||||
try!(state_db.journal_db_mut().commit_old(&batch, era, &chain.block_hash(era).expect("Old block not found in the database")));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
||||
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
||||
}
|
||||
@@ -226,6 +233,7 @@ impl Client {
|
||||
notify: RwLock::new(Vec::new()),
|
||||
queue_transactions: AtomicUsize::new(0),
|
||||
last_hashes: RwLock::new(VecDeque::new()),
|
||||
history: history,
|
||||
};
|
||||
Ok(Arc::new(client))
|
||||
}
|
||||
@@ -251,6 +259,22 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// The env info as of the best block.
|
||||
fn latest_env_info(&self) -> EnvInfo {
|
||||
let header_data = self.best_block_header();
|
||||
let view = HeaderView::new(&header_data);
|
||||
|
||||
EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
timestamp: view.timestamp(),
|
||||
difficulty: view.difficulty(),
|
||||
last_hashes: self.build_last_hashes(view.hash()),
|
||||
gas_used: U256::default(),
|
||||
gas_limit: view.gas_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_last_hashes(&self, parent_hash: H256) -> Arc<LastHashes> {
|
||||
{
|
||||
let hashes = self.last_hashes.read();
|
||||
@@ -282,7 +306,7 @@ impl Client {
|
||||
|
||||
// Check the block isn't so old we won't be able to enact it.
|
||||
let best_block_number = self.chain.best_block_number();
|
||||
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
||||
if best_block_number >= self.history && header.number() <= best_block_number - self.history {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
||||
return Err(());
|
||||
}
|
||||
@@ -295,32 +319,28 @@ impl Client {
|
||||
};
|
||||
|
||||
// Check if Parent is in chain
|
||||
let chain_has_parent = self.chain.block_header(&header.parent_hash);
|
||||
if let None = chain_has_parent {
|
||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
|
||||
return Err(());
|
||||
};
|
||||
let chain_has_parent = self.chain.block_header(header.parent_hash());
|
||||
if let Some(parent) = chain_has_parent {
|
||||
// Enact Verified Block
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
||||
let db = self.state_db.lock().boxed_clone_canon(&header.parent_hash());
|
||||
|
||||
// Enact Verified Block
|
||||
let parent = chain_has_parent.unwrap();
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
||||
let is_canon = header.parent_hash == self.chain.best_block_hash();
|
||||
let db = if is_canon { self.state_db.lock().boxed_clone_canon() } else { self.state_db.lock().boxed_clone() };
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone());
|
||||
let locked_block = try!(enact_result.map_err(|e| {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
}));
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone());
|
||||
if let Err(e) = enact_result {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
};
|
||||
// Final Verification
|
||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Final Verification
|
||||
let locked_block = enact_result.unwrap();
|
||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
Ok(locked_block)
|
||||
} else {
|
||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
|
||||
Err(())
|
||||
}
|
||||
|
||||
Ok(locked_block)
|
||||
}
|
||||
|
||||
fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
|
||||
@@ -351,16 +371,19 @@ impl Client {
|
||||
|
||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||
pub fn import_verified_blocks(&self) -> usize {
|
||||
let max_blocks_to_import = 64;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, duration) = {
|
||||
let max_blocks_to_import = 4;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, duration, is_empty) = {
|
||||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||
let mut invalid_blocks = HashSet::new();
|
||||
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
||||
|
||||
let _import_lock = self.import_lock.lock();
|
||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||
if blocks.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let _timer = PerfTimer::new("import_verified_blocks");
|
||||
let start = precise_time_ns();
|
||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||
|
||||
for block in blocks {
|
||||
let header = &block.header;
|
||||
@@ -373,36 +396,35 @@ impl Client {
|
||||
invalid_blocks.insert(header.hash());
|
||||
continue;
|
||||
}
|
||||
let is_invalid = invalid_blocks.contains(header.parent_hash());
|
||||
if let (false, Ok(closed_block)) = (is_invalid, self.check_and_close_block(&block)) {
|
||||
imported_blocks.push(header.hash());
|
||||
|
||||
let closed_block = closed_block.unwrap();
|
||||
imported_blocks.push(header.hash());
|
||||
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
|
||||
import_results.push(route);
|
||||
|
||||
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
|
||||
import_results.push(route);
|
||||
|
||||
self.report.write().accrue_block(&block);
|
||||
self.report.write().accrue_block(&block);
|
||||
} else {
|
||||
invalid_blocks.insert(header.hash());
|
||||
}
|
||||
}
|
||||
|
||||
let imported = imported_blocks.len();
|
||||
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
||||
|
||||
{
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
if !imported_blocks.is_empty() {
|
||||
self.block_queue.mark_as_good(&imported_blocks);
|
||||
}
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
|
||||
let duration_ns = precise_time_ns() - start;
|
||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns)
|
||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns, is_empty)
|
||||
};
|
||||
|
||||
{
|
||||
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||
if !imported_blocks.is_empty() && is_empty {
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
|
||||
|
||||
if self.queue_info().is_empty() {
|
||||
if is_empty {
|
||||
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||
}
|
||||
|
||||
@@ -427,9 +449,9 @@ impl Client {
|
||||
let number = block.header().number();
|
||||
let parent = block.header().parent_hash().clone();
|
||||
// Are we committing an era?
|
||||
let ancient = if number >= HISTORY {
|
||||
let n = number - HISTORY;
|
||||
Some((n, self.chain.block_hash(n).unwrap()))
|
||||
let ancient = if number >= self.history {
|
||||
let n = number - self.history;
|
||||
Some((n, self.chain.block_hash(n).expect("only verified blocks can be commited; verified block has hash; qed")))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -442,7 +464,6 @@ impl Client {
|
||||
.collect();
|
||||
|
||||
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
|
||||
|
||||
let batch = DBTransaction::new(&self.db);
|
||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||
// already-imported block of the same number.
|
||||
@@ -458,6 +479,8 @@ impl Client {
|
||||
enacted: route.enacted.clone(),
|
||||
retracted: route.retracted.len()
|
||||
});
|
||||
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
|
||||
state.sync_cache(&route.enacted, &route.retracted, is_canon);
|
||||
// Final commit to the DB
|
||||
self.db.write_buffered(batch).expect("DB write failed.");
|
||||
self.chain.commit();
|
||||
@@ -505,7 +528,7 @@ impl Client {
|
||||
let db = self.state_db.lock().boxed_clone();
|
||||
|
||||
// early exit for pruned blocks
|
||||
if db.is_pruned() && self.chain.best_block_number() >= block_number + HISTORY {
|
||||
if db.is_pruned() && self.chain.best_block_number() >= block_number + self.history {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -532,9 +555,11 @@ impl Client {
|
||||
|
||||
/// Get a copy of the best block's state.
|
||||
pub fn state(&self) -> State {
|
||||
let header = self.best_block_header();
|
||||
let header = HeaderView::new(&header);
|
||||
State::from_existing(
|
||||
self.state_db.lock().boxed_clone(),
|
||||
HeaderView::new(&self.best_block_header()).state_root(),
|
||||
self.state_db.lock().boxed_clone_canon(&header.hash()),
|
||||
header.state_root(),
|
||||
self.engine.account_start_nonce(),
|
||||
self.trie_factory.clone())
|
||||
.expect("State root of best block header always valid.")
|
||||
@@ -608,7 +633,7 @@ impl Client {
|
||||
let best_block_number = self.chain_info().best_block_number;
|
||||
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
||||
|
||||
if best_block_number > HISTORY + block_number && db.is_pruned() {
|
||||
if best_block_number > self.history + block_number && db.is_pruned() {
|
||||
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
||||
}
|
||||
|
||||
@@ -620,8 +645,10 @@ impl Client {
|
||||
0
|
||||
};
|
||||
|
||||
self.block_hash(BlockID::Number(start_num))
|
||||
.expect("blocks within HISTORY are always stored.")
|
||||
match self.block_hash(BlockID::Number(start_num)) {
|
||||
Some(h) => h,
|
||||
None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
|
||||
}
|
||||
}
|
||||
_ => match self.block_hash(at) {
|
||||
Some(hash) => hash,
|
||||
@@ -681,7 +708,7 @@ impl BlockChainClient for Client {
|
||||
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
|
||||
let view = HeaderView::new(&header);
|
||||
let last_hashes = self.build_last_hashes(view.hash());
|
||||
let last_hashes = self.build_last_hashes(view.parent_hash());
|
||||
let env_info = EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
@@ -703,7 +730,7 @@ impl BlockChainClient for Client {
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
state.add_balance(&sender, &(needed_balance - balance));
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options));
|
||||
@@ -833,8 +860,9 @@ impl BlockChainClient for Client {
|
||||
let t = self.chain.block_body(&address.block_hash)
|
||||
.and_then(|block| BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index));
|
||||
|
||||
match (t, self.chain.transaction_receipt(&address)) {
|
||||
(Some(tx), Some(receipt)) => {
|
||||
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
|
||||
match (tx_and_sender, self.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();
|
||||
@@ -856,7 +884,7 @@ impl BlockChainClient for Client {
|
||||
gas_used: receipt.gas_used - prior_gas_used,
|
||||
contract_address: match tx.action {
|
||||
Action::Call(_) => None,
|
||||
Action::Create => Some(contract_address(&tx.sender().unwrap(), &tx.nonce))
|
||||
Action::Create => Some(contract_address(&sender, &tx.nonce))
|
||||
},
|
||||
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
@@ -979,17 +1007,18 @@ impl BlockChainClient for Client {
|
||||
let start = self.block_number(filter.range.start);
|
||||
let end = self.block_number(filter.range.end);
|
||||
|
||||
if start.is_some() && end.is_some() {
|
||||
let filter = trace::Filter {
|
||||
range: start.unwrap() as usize..end.unwrap() as usize,
|
||||
from_address: From::from(filter.from_address),
|
||||
to_address: From::from(filter.to_address),
|
||||
};
|
||||
match (start, end) {
|
||||
(Some(s), Some(e)) => {
|
||||
let filter = trace::Filter {
|
||||
range: s as usize..e as usize,
|
||||
from_address: From::from(filter.from_address),
|
||||
to_address: From::from(filter.to_address),
|
||||
};
|
||||
|
||||
let traces = self.tracedb.filter(&filter);
|
||||
Some(traces)
|
||||
} else {
|
||||
None
|
||||
let traces = self.tracedb.filter(&filter);
|
||||
Some(traces)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1036,7 +1065,11 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain.best_block_number())
|
||||
}
|
||||
|
||||
fn signing_network_id(&self) -> Option<u8> {
|
||||
self.engine.signing_network_id(&self.latest_env_info())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1050,7 +1083,7 @@ impl MiningBlockChainClient for Client {
|
||||
&self.vm_factory,
|
||||
self.trie_factory.clone(),
|
||||
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
||||
self.state_db.lock().boxed_clone(),
|
||||
self.state_db.lock().boxed_clone_canon(&h),
|
||||
&self.chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
|
||||
self.build_last_hashes(h.clone()),
|
||||
author,
|
||||
@@ -1061,11 +1094,15 @@ impl MiningBlockChainClient for Client {
|
||||
// Add uncles
|
||||
self.chain
|
||||
.find_uncle_headers(&h, engine.maximum_uncle_age())
|
||||
.unwrap()
|
||||
.unwrap_or_else(Vec::new)
|
||||
.into_iter()
|
||||
.take(engine.maximum_uncle_count())
|
||||
.foreach(|h| {
|
||||
open_block.push_uncle(h).unwrap();
|
||||
open_block.push_uncle(h).expect("pushing maximum_uncle_count;
|
||||
open_block was just created;
|
||||
push_uncle is not ok only if more than maximum_uncle_count is pushed;
|
||||
so all push_uncle are Ok;
|
||||
qed");
|
||||
});
|
||||
|
||||
open_block
|
||||
@@ -1085,9 +1122,9 @@ impl MiningBlockChainClient for Client {
|
||||
|
||||
let block_data = block.rlp_bytes();
|
||||
// Clear canonical state cache
|
||||
self.state_db.lock().clear_cache();
|
||||
let route = self.commit_block(block, &h, &block_data);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
||||
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
||||
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
||||
|
||||
@@ -107,6 +107,8 @@ pub struct ClientConfig {
|
||||
pub mode: Mode,
|
||||
/// Type of block verifier used by client.
|
||||
pub verifier_type: VerifierType,
|
||||
/// State pruning history size.
|
||||
pub history: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -30,13 +30,17 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use types::trace_filter::Filter as TraceFilter;
|
||||
pub use executive::{Executed, Executive, TransactOptions};
|
||||
pub use env_info::{LastHashes, EnvInfo};
|
||||
pub use self::chain_notify::{ChainNotify, ChainNotifyClient};
|
||||
pub use self::chain_notify::ChainNotify;
|
||||
#[cfg(feature="ipc")]
|
||||
pub use self::chain_notify::ChainNotifyClient;
|
||||
#[cfg(feature="ipc")]
|
||||
pub use self::traits::RemoteClient;
|
||||
|
||||
pub use types::call_analytics::CallAnalytics;
|
||||
pub use block_import_error::BlockImportError;
|
||||
pub use transaction_import::TransactionImportResult;
|
||||
pub use transaction_import::TransactionImportError;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
|
||||
|
||||
mod traits {
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
|
||||
@@ -53,6 +53,8 @@ pub struct TestBlockChainClient {
|
||||
pub genesis_hash: H256,
|
||||
/// Last block hash.
|
||||
pub last_hash: RwLock<H256>,
|
||||
/// Extra data do set for each block
|
||||
pub extra_data: Bytes,
|
||||
/// Difficulty.
|
||||
pub difficulty: RwLock<U256>,
|
||||
/// Balances.
|
||||
@@ -99,11 +101,17 @@ impl Default for TestBlockChainClient {
|
||||
impl TestBlockChainClient {
|
||||
/// Creates new test client.
|
||||
pub fn new() -> Self {
|
||||
Self::new_with_extra_data(Bytes::new())
|
||||
}
|
||||
|
||||
/// Creates new test client with specified extra data for each block
|
||||
pub fn new_with_extra_data(extra_data: Bytes) -> Self {
|
||||
let spec = Spec::new_test();
|
||||
let mut client = TestBlockChainClient {
|
||||
blocks: RwLock::new(HashMap::new()),
|
||||
numbers: RwLock::new(HashMap::new()),
|
||||
genesis_hash: H256::new(),
|
||||
extra_data: extra_data,
|
||||
last_hash: RwLock::new(H256::new()),
|
||||
difficulty: RwLock::new(From::from(0)),
|
||||
balances: RwLock::new(HashMap::new()),
|
||||
@@ -121,7 +129,7 @@ impl TestBlockChainClient {
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
client
|
||||
}
|
||||
|
||||
|
||||
/// Set the transaction receipt result
|
||||
pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) {
|
||||
self.receipts.write().insert(id, receipt);
|
||||
@@ -166,6 +174,7 @@ impl TestBlockChainClient {
|
||||
header.parent_hash = self.last_hash.read().clone();
|
||||
header.number = n as BlockNumber;
|
||||
header.gas_limit = U256::from(1_000_000);
|
||||
header.extra_data = self.extra_data.clone();
|
||||
let uncles = match with {
|
||||
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
||||
let mut uncles = RlpStream::new_list(1);
|
||||
@@ -193,7 +202,7 @@ impl TestBlockChainClient {
|
||||
gas_price: U256::one(),
|
||||
nonce: U256::zero()
|
||||
};
|
||||
let signed_tx = tx.sign(keypair.secret());
|
||||
let signed_tx = tx.sign(keypair.secret(), None);
|
||||
txs.append(&signed_tx);
|
||||
txs.out()
|
||||
},
|
||||
@@ -558,6 +567,8 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
|
||||
fn signing_network_id(&self) -> Option<u8> { None }
|
||||
}
|
||||
|
||||
@@ -37,13 +37,10 @@ use executive::Executed;
|
||||
use env_info::LastHashes;
|
||||
use types::call_analytics::CallAnalytics;
|
||||
use block_import_error::BlockImportError;
|
||||
use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
use ipc::{IpcConfig, BinaryConvertError};
|
||||
use ipc::{IpcConfig};
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::block_status::BlockStatus;
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc(client_ident="RemoteClient")]
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
pub trait BlockChainClient : Sync + Send {
|
||||
@@ -215,6 +212,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the preferred network ID to sign on
|
||||
fn signing_network_id(&self) -> Option<u8> { None }
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Engine for InstantSeal {
|
||||
}
|
||||
|
||||
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||
Schedule::new_homestead()
|
||||
Schedule::new_post_eip150(usize::max_value(), false, false, false)
|
||||
}
|
||||
|
||||
fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
|
||||
|
||||
@@ -98,6 +98,9 @@ pub trait Engine : Sync + Send {
|
||||
/// Verify a particular transaction is valid.
|
||||
fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||
|
||||
/// The network ID that transactions should be signed with.
|
||||
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u8> { None }
|
||||
|
||||
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
||||
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
||||
/// methods are needed for an Engine, this may be overridden.
|
||||
|
||||
@@ -62,6 +62,8 @@ pub enum TransactionError {
|
||||
},
|
||||
/// Transaction's gas limit (aka gas) is invalid.
|
||||
InvalidGasLimit(OutOfBounds<U256>),
|
||||
/// Invalid network ID given.
|
||||
InvalidNetworkId,
|
||||
}
|
||||
|
||||
impl fmt::Display for TransactionError {
|
||||
@@ -80,6 +82,7 @@ impl fmt::Display for TransactionError {
|
||||
GasLimitExceeded { limit, got } =>
|
||||
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
||||
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
||||
InvalidNetworkId => "Transaction of this network ID is not allowed on this chain.".into(),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Transaction error ({})", msg))
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use block::*;
|
||||
use state::CleanupMode;
|
||||
use spec::CommonParams;
|
||||
use engines::Engine;
|
||||
use evm::Schedule;
|
||||
@@ -38,13 +39,31 @@ pub struct EthashParams {
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
/// Homestead transition block number.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
pub homestead_transition: u64,
|
||||
/// DAO hard-fork transition block (X).
|
||||
pub dao_hardfork_transition: u64,
|
||||
/// DAO hard-fork refund contract address (C).
|
||||
pub dao_hardfork_beneficiary: Address,
|
||||
/// DAO hard-fork DAO accounts list (L)
|
||||
pub dao_hardfork_accounts: Vec<Address>,
|
||||
/// Transition block for a change of difficulty params (currently just bound_divisor).
|
||||
pub difficulty_hardfork_transition: u64,
|
||||
/// Difficulty param after the difficulty transition.
|
||||
pub difficulty_hardfork_bound_divisor: U256,
|
||||
/// Block on which there is no additional difficulty from the exponential bomb.
|
||||
pub bomb_defuse_transition: u64,
|
||||
/// Number of first block where EIP-150 rules begin.
|
||||
pub eip150_transition: u64,
|
||||
/// Number of first block where EIP-155 rules begin.
|
||||
pub eip155_transition: u64,
|
||||
/// Number of first block where EIP-160 rules begin.
|
||||
pub eip160_transition: u64,
|
||||
/// Number of first block where EIP-161.abc begin.
|
||||
pub eip161abc_transition: u64,
|
||||
/// Number of first block where EIP-161.d begins.
|
||||
pub eip161d_transition: u64,
|
||||
/// Maximum amount of code that can be deploying into a contract.
|
||||
pub max_code_size: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
@@ -56,10 +75,19 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map_or(0, Into::into),
|
||||
dao_hardfork_transition: p.dao_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
homestead_transition: p.homestead_transition.map_or(0, Into::into),
|
||||
dao_hardfork_transition: p.dao_hardfork_transition.map_or(u64::max_value(), Into::into),
|
||||
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
|
||||
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
|
||||
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into),
|
||||
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into),
|
||||
bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into),
|
||||
eip150_transition: p.eip150_transition.map_or(0, Into::into),
|
||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||
eip160_transition: p.eip160_transition.map_or(0, Into::into),
|
||||
eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into),
|
||||
eip161d_transition: p.eip161d_transition.map_or(u64::max_value(), Into::into),
|
||||
max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,12 +132,27 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
trace!(target: "client", "Creating schedule. fCML={}", self.ethash_params.frontier_compatibility_mode_limit);
|
||||
trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition);
|
||||
|
||||
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
|
||||
if env_info.number < self.ethash_params.homestead_transition {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
} else if env_info.number < self.ethash_params.eip150_transition {
|
||||
Schedule::new_homestead()
|
||||
} else {
|
||||
Schedule::new_post_eip150(
|
||||
self.ethash_params.max_code_size as usize,
|
||||
env_info.number >= self.ethash_params.eip160_transition,
|
||||
env_info.number >= self.ethash_params.eip161abc_transition,
|
||||
env_info.number >= self.ethash_params.eip161d_transition
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn signing_network_id(&self, env_info: &EnvInfo) -> Option<u8> {
|
||||
if env_info.number >= self.ethash_params.eip155_transition && self.params().network_id < 127 {
|
||||
Some(self.params().network_id as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +187,7 @@ impl Engine for Ethash {
|
||||
let mut state = block.fields_mut().state;
|
||||
for child in &self.ethash_params.dao_hardfork_accounts {
|
||||
let b = state.balance(child);
|
||||
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b);
|
||||
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b, CleanupMode::NoEmpty);
|
||||
}
|
||||
// }
|
||||
}
|
||||
@@ -157,12 +200,12 @@ impl Engine for Ethash {
|
||||
let fields = block.fields_mut();
|
||||
|
||||
// Bestow block reward
|
||||
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
|
||||
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())), CleanupMode::NoEmpty);
|
||||
|
||||
// Bestow uncle rewards
|
||||
let current_number = fields.header.number();
|
||||
for u in fields.uncles.iter() {
|
||||
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)));
|
||||
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)), CleanupMode::NoEmpty);
|
||||
}
|
||||
if let Err(e) = fields.state.commit() {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
@@ -246,9 +289,16 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
if header.number() >= self.ethash_params.frontier_compatibility_mode_limit {
|
||||
if header.number() >= self.ethash_params.homestead_transition {
|
||||
try!(t.check_low_s());
|
||||
}
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if header.number() < self.ethash_params.eip155_transition || n as usize != self.params().network_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into())
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -268,7 +318,7 @@ impl Ethash {
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
|
||||
let duration_limit = self.ethash_params.duration_limit;
|
||||
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
||||
let frontier_limit = self.ethash_params.homestead_transition;
|
||||
|
||||
let mut target = if header.number < frontier_limit {
|
||||
if header.timestamp >= parent.timestamp + duration_limit {
|
||||
|
||||
@@ -44,12 +44,21 @@ pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethere
|
||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||
pub fn new_homestead_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
|
||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
||||
pub fn new_eip150_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
||||
|
||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
||||
pub fn new_eip161_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/eip161_test.json")) }
|
||||
|
||||
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
||||
pub fn new_daohardfork_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/daohardfork_test.json")) }
|
||||
pub fn new_transition_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
||||
|
||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||
pub fn new_mainnet_like() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
|
||||
/// Create a new Ropsten chain spec.
|
||||
pub fn new_ropsten() -> Spec { Spec::load(include_bytes!("../../res/ethereum/ropsten.json")) }
|
||||
|
||||
/// Create a new Morden chain spec.
|
||||
pub fn new_morden() -> Spec { Spec::load(include_bytes!("../../res/ethereum/morden.json")) }
|
||||
|
||||
|
||||
@@ -52,6 +52,12 @@ pub trait Ext {
|
||||
/// Determine whether an account exists.
|
||||
fn exists(&self, address: &Address) -> bool;
|
||||
|
||||
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool;
|
||||
|
||||
/// Balance of the origin account.
|
||||
fn origin_balance(&self) -> U256;
|
||||
|
||||
/// Returns address balance.
|
||||
fn balance(&self, address: &Address) -> U256;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ use super::u256_to_address;
|
||||
use evm::{self, CostType};
|
||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
||||
use evm::interpreter::stack::Stack;
|
||||
use evm::schedule::Schedule;
|
||||
|
||||
macro_rules! overflowing {
|
||||
($x: expr) => {{
|
||||
@@ -31,7 +32,7 @@ macro_rules! overflowing {
|
||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
||||
enum InstructionCost<Cost: CostType> {
|
||||
Gas(Cost),
|
||||
GasMem(Cost, Cost),
|
||||
GasMem(Cost, Cost, Option<Cost>),
|
||||
GasMemCopy(Cost, Cost, Cost)
|
||||
}
|
||||
|
||||
@@ -56,7 +57,37 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
}
|
||||
}
|
||||
|
||||
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
|
||||
/// and that we `requested` some.
|
||||
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<evm::Result<Gas>>) -> evm::Result<Gas> {
|
||||
match schedule.sub_gas_cap_divisor {
|
||||
Some(cap_divisor) if self.current_gas >= needed => {
|
||||
let gas_remaining = self.current_gas - needed;
|
||||
let max_gas_provided = gas_remaining - gas_remaining / Gas::from(cap_divisor);
|
||||
if let Some(Ok(r)) = requested {
|
||||
Ok(min(r, max_gas_provided))
|
||||
} else {
|
||||
Ok(max_gas_provided)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if let Some(r) = requested {
|
||||
r
|
||||
} else if self.current_gas >= needed {
|
||||
Ok(self.current_gas - needed)
|
||||
} else {
|
||||
Ok(0.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||
/// Determine how much gas is used by the given instruction, given the machine's state.
|
||||
///
|
||||
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
|
||||
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
|
||||
/// it will be the amount of gas that the current context provides to the child context.
|
||||
pub fn get_gas_cost_mem(
|
||||
&mut self,
|
||||
ext: &evm::Ext,
|
||||
@@ -64,7 +95,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
info: &InstructionInfo,
|
||||
stack: &Stack<U256>,
|
||||
current_mem_size: usize,
|
||||
) -> evm::Result<(Gas, Gas, usize)> {
|
||||
) -> evm::Result<(Gas, Gas, usize, Option<Gas>)> {
|
||||
let schedule = ext.schedule();
|
||||
let tier = instructions::get_tier_idx(info.tier);
|
||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||
@@ -90,26 +121,47 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
instructions::SLOAD => {
|
||||
InstructionCost::Gas(Gas::from(schedule.sload_gas))
|
||||
},
|
||||
instructions::BALANCE => {
|
||||
InstructionCost::Gas(Gas::from(schedule.balance_gas))
|
||||
},
|
||||
instructions::EXTCODESIZE => {
|
||||
InstructionCost::Gas(Gas::from(schedule.extcodesize_gas))
|
||||
},
|
||||
instructions::SUICIDE => {
|
||||
let mut gas = Gas::from(schedule.suicide_gas);
|
||||
|
||||
let is_value_transfer = !ext.origin_balance().is_zero();
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
if (
|
||||
!schedule.no_empty && !ext.exists(&address)
|
||||
) || (
|
||||
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)
|
||||
) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
|
||||
}
|
||||
|
||||
InstructionCost::Gas(gas)
|
||||
},
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)), None)
|
||||
},
|
||||
instructions::MSTORE8 => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)), None)
|
||||
},
|
||||
instructions::RETURN => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None)
|
||||
},
|
||||
instructions::SHA3 => {
|
||||
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
|
||||
let words = w >> 5;
|
||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None)
|
||||
},
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
||||
},
|
||||
instructions::EXTCODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
InstructionCost::GasMemCopy(schedule.extcodecopy_base_gas.into(), try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
},
|
||||
instructions::LOG0...instructions::LOG4 => {
|
||||
let no_of_topics = instructions::get_log_topics(instruction);
|
||||
@@ -117,39 +169,68 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
|
||||
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None)
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(5), stack.peek(6))),
|
||||
try!(mem_needed(stack.peek(3), stack.peek(4)))
|
||||
);
|
||||
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
let is_value_transfer = !stack.peek(2).is_zero();
|
||||
|
||||
if instruction == instructions::CALL && !ext.exists(&address) {
|
||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
|
||||
if instruction == instructions::CALL {
|
||||
if (
|
||||
!schedule.no_empty && !ext.exists(&address)
|
||||
) || (
|
||||
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)
|
||||
) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
|
||||
}
|
||||
};
|
||||
|
||||
if !stack.peek(2).is_zero() {
|
||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
|
||||
if is_value_transfer {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
|
||||
};
|
||||
|
||||
InstructionCost::GasMem(gas,mem)
|
||||
// TODO: refactor to avoid duplicate calculation here and later on.
|
||||
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
|
||||
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
|
||||
let requested = Gas::from_u256(*stack.peek(0));
|
||||
let provided = try!(self.gas_provided(schedule, cost_so_far, Some(requested)));
|
||||
gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionCost::GasMem(gas, mem, Some(provided))
|
||||
},
|
||||
instructions::DELEGATECALL => {
|
||||
let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(4), stack.peek(5))),
|
||||
try!(mem_needed(stack.peek(2), stack.peek(3)))
|
||||
);
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
|
||||
// TODO: refactor to avoid duplicate calculation here and later on.
|
||||
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
|
||||
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
|
||||
let requested = Gas::from_u256(*stack.peek(0));
|
||||
let provided = try!(self.gas_provided(schedule, cost_so_far, Some(requested)));
|
||||
gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionCost::GasMem(gas, mem, Some(provided))
|
||||
},
|
||||
instructions::CREATE => {
|
||||
let gas = Gas::from(schedule.create_gas);
|
||||
let mut gas = Gas::from(schedule.create_gas);
|
||||
let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
|
||||
// TODO: refactor to avoid duplicate calculation here and later on.
|
||||
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
|
||||
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
|
||||
let provided = try!(self.gas_provided(schedule, cost_so_far, None));
|
||||
gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionCost::GasMem(gas, mem, Some(provided))
|
||||
},
|
||||
instructions::EXP => {
|
||||
let expon = stack.peek(1);
|
||||
@@ -157,17 +238,17 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||
InstructionCost::Gas(gas)
|
||||
},
|
||||
_ => InstructionCost::Gas(default_gas)
|
||||
_ => InstructionCost::Gas(default_gas),
|
||||
};
|
||||
|
||||
match cost {
|
||||
InstructionCost::Gas(gas) => {
|
||||
Ok((gas, self.current_mem_gas, 0))
|
||||
Ok((gas, self.current_mem_gas, 0, None))
|
||||
},
|
||||
InstructionCost::GasMem(gas, mem_size) => {
|
||||
InstructionCost::GasMem(gas, mem_size, provided) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
Ok((gas, new_mem_gas, new_mem_size))
|
||||
Ok((gas, new_mem_gas, new_mem_size, provided))
|
||||
},
|
||||
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
@@ -175,7 +256,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let copy_gas = Gas::from(schedule.copy_gas) * copy;
|
||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
Ok((gas, new_mem_gas, new_mem_size))
|
||||
Ok((gas, new_mem_gas, new_mem_size, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ impl<'a> CodeReader<'a> {
|
||||
|
||||
enum InstructionResult<Gas> {
|
||||
Ok,
|
||||
UseAllGas,
|
||||
GasLeft(Gas),
|
||||
UnusedGas(Gas),
|
||||
JumpToPosition(U256),
|
||||
// gas left, init_orf, init_size
|
||||
@@ -129,7 +127,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
try!(self.verify_instruction(ext, instruction, &info, &stack));
|
||||
|
||||
// Calculate gas cost
|
||||
let (gas_cost, mem_gas, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
|
||||
let (gas_cost, mem_gas, mem_size, provided) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
|
||||
// TODO: make compile-time removable if too much of a performance hit.
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
|
||||
|
||||
@@ -155,25 +153,19 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack, provided
|
||||
));
|
||||
|
||||
if let InstructionResult::UnusedGas(ref gas) = result {
|
||||
gasometer.current_gas = gasometer.current_gas + *gas;
|
||||
}
|
||||
|
||||
if trace_executed {
|
||||
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
|
||||
}
|
||||
|
||||
// Advance
|
||||
match result {
|
||||
InstructionResult::Ok => {},
|
||||
InstructionResult::UnusedGas(gas) => {
|
||||
gasometer.current_gas = gasometer.current_gas + gas;
|
||||
},
|
||||
InstructionResult::UseAllGas => {
|
||||
gasometer.current_gas = Cost::from(0);
|
||||
},
|
||||
InstructionResult::GasLeft(gas_left) => {
|
||||
gasometer.current_gas = gas_left;
|
||||
},
|
||||
InstructionResult::JumpToPosition(position) => {
|
||||
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
||||
reader.position = pos;
|
||||
@@ -182,6 +174,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
||||
},
|
||||
InstructionResult::StopExecution => break,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +257,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
ext: &mut evm::Ext,
|
||||
instruction: Instruction,
|
||||
code: &mut CodeReader,
|
||||
stack: &mut Stack<U256>
|
||||
stack: &mut Stack<U256>,
|
||||
provided: Option<Cost>
|
||||
) -> evm::Result<InstructionResult<Cost>> {
|
||||
match instruction {
|
||||
instructions::JUMP => {
|
||||
@@ -289,31 +283,32 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let endowment = stack.pop_back();
|
||||
let init_off = stack.pop_back();
|
||||
let init_size = stack.pop_back();
|
||||
let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed");
|
||||
|
||||
let contract_code = self.mem.read_slice(init_off, init_size);
|
||||
let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||
|
||||
if !can_create {
|
||||
stack.push(U256::zero());
|
||||
return Ok(InstructionResult::Ok);
|
||||
return Ok(InstructionResult::UnusedGas(create_gas));
|
||||
}
|
||||
|
||||
let create_result = ext.create(&gas.as_u256(), &endowment, contract_code);
|
||||
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code);
|
||||
return match create_result {
|
||||
ContractCreateResult::Created(address, gas_left) => {
|
||||
stack.push(address_to_u256(address));
|
||||
Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
},
|
||||
ContractCreateResult::Failed => {
|
||||
stack.push(U256::zero());
|
||||
// TODO [todr] Should we just StopExecution here?
|
||||
Ok(InstructionResult::UseAllGas)
|
||||
Ok(InstructionResult::Ok)
|
||||
}
|
||||
};
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
||||
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
||||
let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated.");
|
||||
stack.pop_back();
|
||||
let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed");
|
||||
let code_address = stack.pop_back();
|
||||
let code_address = u256_to_address(&code_address);
|
||||
|
||||
@@ -331,7 +326,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
// Add stipend (only CALL|CALLCODE when value > 0)
|
||||
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val.is_zero() {
|
||||
false => Cost::from(ext.schedule().call_stipend),
|
||||
true => Cost::from(0)
|
||||
true => Cost::from(0),
|
||||
});
|
||||
|
||||
// Get sender & receive addresses, check if we have balance
|
||||
|
||||
@@ -21,7 +21,7 @@ use util::sha3::*;
|
||||
use bit_set::BitSet;
|
||||
use super::super::instructions;
|
||||
|
||||
const CACHE_CODE_ITEMS: usize = 4096;
|
||||
const CACHE_CODE_ITEMS: usize = 65536;
|
||||
|
||||
/// GLobal cache for EVM interpreter
|
||||
pub struct SharedCache {
|
||||
|
||||
@@ -70,6 +70,8 @@ pub struct Schedule {
|
||||
pub quad_coeff_div: usize,
|
||||
/// Cost for contract length when executing `CREATE`
|
||||
pub create_data_gas: usize,
|
||||
/// Maximum code size when creating a contract.
|
||||
pub create_data_limit: usize,
|
||||
/// Transaction cost
|
||||
pub tx_gas: usize,
|
||||
/// `CREATE` transaction cost
|
||||
@@ -80,6 +82,23 @@ pub struct Schedule {
|
||||
pub tx_data_non_zero_gas: usize,
|
||||
/// Gas price for copying memory
|
||||
pub copy_gas: usize,
|
||||
/// Price of EXTCODESIZE
|
||||
pub extcodesize_gas: usize,
|
||||
/// Base price of EXTCODECOPY
|
||||
pub extcodecopy_base_gas: usize,
|
||||
/// Price of BALANCE
|
||||
pub balance_gas: usize,
|
||||
/// Price of SUICIDE
|
||||
pub suicide_gas: usize,
|
||||
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
|
||||
pub suicide_to_new_account_cost: usize,
|
||||
/// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit.
|
||||
/// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS
|
||||
pub sub_gas_cap_divisor: Option<usize>,
|
||||
/// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value.
|
||||
pub no_empty: bool,
|
||||
/// Kill empty accounts if touched.
|
||||
pub kill_empty: bool,
|
||||
}
|
||||
|
||||
impl Schedule {
|
||||
@@ -93,8 +112,54 @@ impl Schedule {
|
||||
Self::new(true, true, 53000)
|
||||
}
|
||||
|
||||
/// Schedule for the post-EIP-150-era of the Ethereum main net.
|
||||
pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule {
|
||||
Schedule {
|
||||
exceptional_failed_code_deposit: true,
|
||||
have_delegate_call: true,
|
||||
stack_limit: 1024,
|
||||
max_depth: 1024,
|
||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||
exp_gas: 10,
|
||||
exp_byte_gas: if fix_exp {50} else {10},
|
||||
sha3_gas: 30,
|
||||
sha3_word_gas: 6,
|
||||
sload_gas: 200,
|
||||
sstore_set_gas: 20000,
|
||||
sstore_reset_gas: 5000,
|
||||
sstore_refund_gas: 15000,
|
||||
jumpdest_gas: 1,
|
||||
log_gas: 375,
|
||||
log_data_gas: 8,
|
||||
log_topic_gas: 375,
|
||||
create_gas: 32000,
|
||||
call_gas: 700,
|
||||
call_stipend: 2300,
|
||||
call_value_transfer_gas: 9000,
|
||||
call_new_account_gas: 25000,
|
||||
suicide_refund_gas: 24000,
|
||||
memory_gas: 3,
|
||||
quad_coeff_div: 512,
|
||||
create_data_gas: 200,
|
||||
create_data_limit: max_code_size,
|
||||
tx_gas: 21000,
|
||||
tx_create_gas: 53000,
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
extcodesize_gas: 700,
|
||||
extcodecopy_base_gas: 700,
|
||||
balance_gas: 400,
|
||||
suicide_gas: 5000,
|
||||
suicide_to_new_account_cost: 25000,
|
||||
sub_gas_cap_divisor: Some(64),
|
||||
no_empty: no_empty,
|
||||
kill_empty: kill_empty,
|
||||
}
|
||||
}
|
||||
|
||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||
Schedule{
|
||||
Schedule {
|
||||
exceptional_failed_code_deposit: efcd,
|
||||
have_delegate_call: hdc,
|
||||
stack_limit: 1024,
|
||||
@@ -121,11 +186,20 @@ impl Schedule {
|
||||
memory_gas: 3,
|
||||
quad_coeff_div: 512,
|
||||
create_data_gas: 200,
|
||||
create_data_limit: usize::max_value(),
|
||||
tx_gas: 21000,
|
||||
tx_create_gas: tcg,
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
extcodesize_gas: 20,
|
||||
extcodecopy_base_gas: 20,
|
||||
balance_gas: 20,
|
||||
suicide_gas: 0,
|
||||
suicide_to_new_account_cost: 0,
|
||||
sub_gas_cap_divisor: None,
|
||||
no_empty: false,
|
||||
kill_empty: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,14 @@ impl Ext for FakeExt {
|
||||
self.balances.contains_key(address)
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
self.balances.get(address).map_or(false, |b| !b.is_zero())
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address) -> U256 {
|
||||
*self.balances.get(address).unwrap()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Transaction Execution environment.
|
||||
use common::*;
|
||||
use state::*;
|
||||
use state::{State, CleanupMode};
|
||||
use engines::Engine;
|
||||
use types::executed::CallType;
|
||||
use evm::{self, Ext, Factory, Finalize};
|
||||
@@ -26,10 +26,10 @@ use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, E
|
||||
use crossbeam;
|
||||
pub use types::executed::{Executed, ExecutionResult};
|
||||
|
||||
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
|
||||
/// Roughly estimate what stack size each level of evm depth will use
|
||||
/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132)
|
||||
/// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp`
|
||||
const MAX_VM_DEPTH_FOR_THREAD: usize = 64;
|
||||
const STACK_SIZE_PER_DEPTH: usize = 24*1024;
|
||||
|
||||
/// Returns new address created from address and given nonce.
|
||||
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||
@@ -148,12 +148,13 @@ impl<'a> Executive<'a> {
|
||||
|
||||
// TODO: we might need bigints here, or at least check overflows.
|
||||
let balance = self.state.balance(&sender);
|
||||
let gas_cost = U512::from(t.gas) * U512::from(t.gas_price);
|
||||
let gas_cost = t.gas.full_mul(t.gas_price);
|
||||
let total_cost = U512::from(t.value) + gas_cost;
|
||||
|
||||
// avoid unaffordable transactions
|
||||
if U512::from(balance) < total_cost {
|
||||
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: U512::from(balance) }));
|
||||
let balance512 = U512::from(balance);
|
||||
if balance512 < total_cost {
|
||||
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 }));
|
||||
}
|
||||
|
||||
// NOTE: there can be no invalid transactions from this point.
|
||||
@@ -212,8 +213,11 @@ impl<'a> Executive<'a> {
|
||||
tracer: &mut T,
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
|
||||
|
||||
let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);
|
||||
|
||||
// Ordinary execution - keep VM in same thread
|
||||
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
|
||||
if (self.depth + 1) % depth_threshold != 0 {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
||||
@@ -248,9 +252,11 @@ impl<'a> Executive<'a> {
|
||||
// backup used in case of running out of gas
|
||||
self.state.snapshot();
|
||||
|
||||
let schedule = self.engine.schedule(self.info);
|
||||
|
||||
// at first, transfer value to destination
|
||||
if let ActionValue::Transfer(val) = params.value {
|
||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val);
|
||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule));
|
||||
}
|
||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||
|
||||
@@ -265,7 +271,7 @@ impl<'a> Executive<'a> {
|
||||
let cost = self.engine.cost_of_builtin(¶ms.code_address, data);
|
||||
if cost <= params.gas {
|
||||
self.engine.execute_builtin(¶ms.code_address, data, &mut output);
|
||||
self.state.clear_snapshot();
|
||||
self.state.discard_snapshot();
|
||||
|
||||
// trace only top level calls to builtins to avoid DDoS attacks
|
||||
if self.depth == 0 {
|
||||
@@ -285,7 +291,7 @@ impl<'a> Executive<'a> {
|
||||
Ok(params.gas - cost)
|
||||
} else {
|
||||
// just drain the whole gas
|
||||
self.state.revert_snapshot();
|
||||
self.state.revert_to_snapshot();
|
||||
|
||||
tracer.trace_failed_call(trace_info, vec![]);
|
||||
|
||||
@@ -331,7 +337,7 @@ impl<'a> Executive<'a> {
|
||||
res
|
||||
} else {
|
||||
// otherwise it's just a basic transaction, only do tracing, if necessary.
|
||||
self.state.clear_snapshot();
|
||||
self.state.discard_snapshot();
|
||||
|
||||
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
|
||||
Ok(params.gas)
|
||||
@@ -356,12 +362,14 @@ impl<'a> Executive<'a> {
|
||||
let mut unconfirmed_substate = Substate::new();
|
||||
|
||||
// create contract and transfer value to it if necessary
|
||||
let schedule = self.engine.schedule(self.info);
|
||||
let nonce_offset = if schedule.no_empty {1} else {0}.into();
|
||||
let prev_bal = self.state.balance(¶ms.address);
|
||||
if let ActionValue::Transfer(val) = params.value {
|
||||
self.state.sub_balance(¶ms.sender, &val);
|
||||
self.state.new_contract(¶ms.address, val + prev_bal);
|
||||
self.state.new_contract(¶ms.address, val + prev_bal, nonce_offset);
|
||||
} else {
|
||||
self.state.new_contract(¶ms.address, prev_bal);
|
||||
self.state.new_contract(¶ms.address, prev_bal, nonce_offset);
|
||||
}
|
||||
|
||||
let trace_info = tracer.prepare_trace_create(¶ms);
|
||||
@@ -397,7 +405,7 @@ impl<'a> Executive<'a> {
|
||||
fn finalize(
|
||||
&mut self,
|
||||
t: &SignedTransaction,
|
||||
substate: Substate,
|
||||
mut substate: Substate,
|
||||
result: evm::Result<U256>,
|
||||
output: Bytes,
|
||||
trace: Vec<FlatTrace>,
|
||||
@@ -413,7 +421,7 @@ impl<'a> Executive<'a> {
|
||||
|
||||
// real ammount to refund
|
||||
let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() };
|
||||
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2));
|
||||
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1);
|
||||
let gas_left = gas_left_prerefund + refunded;
|
||||
|
||||
let gas_used = t.gas - gas_left;
|
||||
@@ -423,16 +431,32 @@ impl<'a> Executive<'a> {
|
||||
trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n",
|
||||
t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
|
||||
|
||||
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, t.sender().unwrap());
|
||||
self.state.add_balance(&t.sender().unwrap(), &refund_value);
|
||||
let sender = match t.sender() {
|
||||
Ok(sender) => sender,
|
||||
Err(e) => {
|
||||
debug!(target: "executive", "attempted to finalize transaction without sender: {}", e);
|
||||
return Err(ExecutionError::Internal);
|
||||
}
|
||||
};
|
||||
|
||||
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender);
|
||||
// Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction
|
||||
self.state.add_balance(&sender, &refund_value, CleanupMode::NoEmpty);
|
||||
trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author);
|
||||
self.state.add_balance(&self.info.author, &fees_value);
|
||||
self.state.add_balance(&self.info.author, &fees_value, substate.to_cleanup_mode(&schedule));
|
||||
|
||||
// perform suicides
|
||||
for address in &substate.suicides {
|
||||
self.state.kill_account(address);
|
||||
}
|
||||
|
||||
// perform garbage-collection
|
||||
for address in &substate.garbage {
|
||||
if self.state.exists(address) && !self.state.exists_and_not_null(address) {
|
||||
self.state.kill_account(address);
|
||||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
|
||||
Err(_) => {
|
||||
@@ -473,10 +497,10 @@ impl<'a> Executive<'a> {
|
||||
| Err(evm::Error::BadInstruction {.. })
|
||||
| Err(evm::Error::StackUnderflow {..})
|
||||
| Err(evm::Error::OutOfStack {..}) => {
|
||||
self.state.revert_snapshot();
|
||||
self.state.revert_to_snapshot();
|
||||
},
|
||||
Ok(_) | Err(evm::Error::Internal) => {
|
||||
self.state.clear_snapshot();
|
||||
self.state.discard_snapshot();
|
||||
substate.accrue(un_substate);
|
||||
}
|
||||
}
|
||||
@@ -495,6 +519,7 @@ mod tests {
|
||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
||||
use types::executed::CallType;
|
||||
use state::{CleanupMode};
|
||||
|
||||
#[test]
|
||||
fn test_contract_address() {
|
||||
@@ -516,7 +541,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(0x7));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(0x100u64));
|
||||
state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
@@ -575,7 +600,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(100));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
@@ -631,7 +656,7 @@ mod tests {
|
||||
params.call_type = CallType::Call;
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
@@ -689,7 +714,7 @@ mod tests {
|
||||
VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 99979.into(), executed: Some(VMExecutedOperation { gas_used: 64755.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) }
|
||||
],
|
||||
@@ -740,7 +765,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(100.into());
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
@@ -828,7 +853,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(100));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
@@ -880,7 +905,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(100));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(1024);
|
||||
let mut substate = Substate::new();
|
||||
@@ -940,7 +965,7 @@ mod tests {
|
||||
let mut state = state_result.reference_mut();
|
||||
state.init_code(&address_a, code_a.clone());
|
||||
state.init_code(&address_b, code_b.clone());
|
||||
state.add_balance(&sender, &U256::from(100_000));
|
||||
state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty);
|
||||
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1013,13 +1038,13 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
let contract = contract_address(&sender, &U256::zero());
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(18));
|
||||
state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = U256::from(100_000);
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1080,12 +1105,12 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::one()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(17));
|
||||
state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = U256::from(100_000);
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1113,12 +1138,12 @@ mod tests {
|
||||
gas: U256::from(80_001),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(17));
|
||||
state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_used = U256::from(20_000);
|
||||
info.gas_limit = U256::from(100_000);
|
||||
@@ -1148,12 +1173,12 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::one(),
|
||||
nonce: U256::zero()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100_017));
|
||||
state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = U256::from(100_000);
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1188,7 +1213,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap());
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap());
|
||||
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
|
||||
@@ -113,6 +113,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
||||
self.state.exists(address)
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
self.state.exists_and_not_null(address)
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 { self.balance(&self.origin_info.address) }
|
||||
|
||||
fn balance(&self, address: &Address) -> U256 {
|
||||
self.state.balance(address)
|
||||
}
|
||||
@@ -235,7 +241,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
||||
},
|
||||
OutputPolicy::InitContract(ref mut copy) => {
|
||||
let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas);
|
||||
if return_cost > *gas {
|
||||
if return_cost > *gas || data.len() > self.schedule.create_data_limit {
|
||||
return match self.schedule.exceptional_failed_code_deposit {
|
||||
true => Err(evm::Error::OutOfGas),
|
||||
false => Ok(*gas)
|
||||
@@ -266,11 +272,11 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
||||
let address = self.origin_info.address.clone();
|
||||
let balance = self.balance(&address);
|
||||
if &address == refund_address {
|
||||
// TODO [todr] To be consisted with CPP client we set balance to 0 in that case.
|
||||
// TODO [todr] To be consistent with CPP client we set balance to 0 in that case.
|
||||
self.state.sub_balance(&address, &balance);
|
||||
} else {
|
||||
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
||||
self.state.transfer_balance(&address, refund_address, &balance);
|
||||
trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
||||
self.state.transfer_balance(&address, refund_address, &balance, self.substate.to_cleanup_mode(&self.schedule));
|
||||
}
|
||||
|
||||
self.tracer.trace_suicide(address, balance, refund_address.clone());
|
||||
|
||||
@@ -48,7 +48,9 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
let mut spec = match era {
|
||||
ChainEra::Frontier => ethereum::new_frontier_test(),
|
||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||
ChainEra::DaoHardfork => ethereum::new_daohardfork_test(),
|
||||
ChainEra::Eip150 => ethereum::new_eip150_test(),
|
||||
ChainEra::Eip161 => ethereum::new_eip161_test(),
|
||||
ChainEra::TransitionTest => ethereum::new_transition_test(),
|
||||
};
|
||||
spec.set_genesis_state(state);
|
||||
spec.overwrite_genesis_params(genesis);
|
||||
@@ -114,14 +116,38 @@ mod frontier_era_tests {
|
||||
declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"}
|
||||
}
|
||||
|
||||
mod daohardfork_tests {
|
||||
mod transition_tests {
|
||||
use tests::helpers::*;
|
||||
use super::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::DaoHardfork)
|
||||
json_chain_test(json_data, ChainEra::TransitionTest)
|
||||
}
|
||||
|
||||
declare_test!{BlockchainTests_TestNetwork_bcSimpleTransitionTest, "BlockchainTests/TestNetwork/bcSimpleTransitionTest"}
|
||||
declare_test!{BlockchainTests_TestNetwork_bcTheDaoTest, "BlockchainTests/TestNetwork/bcTheDaoTest"}
|
||||
declare_test!{BlockchainTests_TestNetwork_bcEIP150Test, "BlockchainTests/TestNetwork/bcEIP150Test"}
|
||||
}
|
||||
|
||||
mod eip150_blockchain_tests {
|
||||
use tests::helpers::*;
|
||||
use super::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip150)
|
||||
}
|
||||
|
||||
declare_test!{BlockchainTests_EIP150_bcBlockGasLimitTest, "BlockchainTests/EIP150/bcBlockGasLimitTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcForkStressTest, "BlockchainTests/EIP150/bcForkStressTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcGasPricerTest, "BlockchainTests/EIP150/bcGasPricerTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcInvalidHeaderTest, "BlockchainTests/EIP150/bcInvalidHeaderTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcInvalidRLPTest, "BlockchainTests/EIP150/bcInvalidRLPTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcMultiChainTest, "BlockchainTests/EIP150/bcMultiChainTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcRPC_API_Test, "BlockchainTests/EIP150/bcRPC_API_Test"}
|
||||
declare_test!{BlockchainTests_EIP150_bcStateTest, "BlockchainTests/EIP150/bcStateTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcTotalDifficultyTest, "BlockchainTests/EIP150/bcTotalDifficultyTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcUncleHeaderValiditiy, "BlockchainTests/EIP150/bcUncleHeaderValiditiy"}
|
||||
declare_test!{BlockchainTests_EIP150_bcUncleTest, "BlockchainTests/EIP150/bcUncleTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcValidBlockTest, "BlockchainTests/EIP150/bcValidBlockTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcWalletTest, "BlockchainTests/EIP150/bcWalletTest"}
|
||||
}
|
||||
|
||||
43
ethcore/src/json_tests/eip150_state.rs
Normal file
43
ethcore/src/json_tests/eip150_state.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use super::state::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip150)
|
||||
}
|
||||
|
||||
declare_test!{StateTests_EIP150_stEIPSpecificTest, "StateTests/EIP150/stEIPSpecificTest"}
|
||||
declare_test!{StateTests_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP150/stEIPsingleCodeGasPrices"}
|
||||
declare_test!{StateTests_EIP150_stMemExpandingEIPCalls, "StateTests/EIP150/stMemExpandingEIPCalls"}
|
||||
|
||||
declare_test!{StateTests_EIP150_stCallCodes, "StateTests/EIP150/Homestead/stCallCodes"}
|
||||
declare_test!{StateTests_EIP150_stCallCreateCallCodeTest, "StateTests/EIP150/Homestead/stCallCreateCallCodeTest"}
|
||||
declare_test!{StateTests_EIP150_stDelegatecallTest, "StateTests/EIP150/Homestead/stDelegatecallTest"}
|
||||
declare_test!{StateTests_EIP150_stInitCodeTest, "StateTests/EIP150/Homestead/stInitCodeTest"}
|
||||
declare_test!{StateTests_EIP150_stLogTests, "StateTests/EIP150/Homestead/stLogTests"}
|
||||
declare_test!{heavy => StateTests_EIP150_stMemoryStressTest, "StateTests/EIP150/Homestead/stMemoryStressTest"}
|
||||
declare_test!{heavy => StateTests_EIP150_stMemoryTest, "StateTests/EIP150/Homestead/stMemoryTest"}
|
||||
declare_test!{StateTests_EIP150_stPreCompiledContracts, "StateTests/EIP150/Homestead/stPreCompiledContracts"}
|
||||
declare_test!{heavy => StateTests_EIP150_stQuadraticComplexityTest, "StateTests/EIP150/Homestead/stQuadraticComplexityTest"}
|
||||
declare_test!{StateTests_EIP150_stRecursiveCreate, "StateTests/EIP150/Homestead/stRecursiveCreate"}
|
||||
declare_test!{StateTests_EIP150_stRefundTest, "StateTests/EIP150/Homestead/stRefundTest"}
|
||||
declare_test!{StateTests_EIP150_stSpecialTest, "StateTests/EIP150/Homestead/stSpecialTest"}
|
||||
declare_test!{StateTests_EIP150_stSystemOperationsTest, "StateTests/EIP150/Homestead/stSystemOperationsTest"}
|
||||
declare_test!{StateTests_EIP150_stTransactionTest, "StateTests/EIP150/Homestead/stTransactionTest"}
|
||||
declare_test!{StateTests_EIP150_stWalletTest, "StateTests/EIP150/Homestead/stWalletTest"}
|
||||
51
ethcore/src/json_tests/eip161_state.rs
Normal file
51
ethcore/src/json_tests/eip161_state.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use super::state::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip161)
|
||||
}
|
||||
|
||||
declare_test!{StateTests_EIP158_stEIP158SpecificTest, "StateTests/EIP158/stEIP158SpecificTest"}
|
||||
declare_test!{StateTests_EIP158_stNonZeroCallsTest, "StateTests/EIP158/stNonZeroCallsTest"}
|
||||
declare_test!{StateTests_EIP158_stZeroCallsTest, "StateTests/EIP158/stZeroCallsTest"}
|
||||
|
||||
declare_test!{StateTests_EIP158_EIP150_stMemExpandingEIPCalls, "StateTests/EIP158/EIP150/stMemExpandingEIPCalls"}
|
||||
declare_test!{StateTests_EIP158_EIP150_stEIPSpecificTest, "StateTests/EIP158/EIP150/stEIPSpecificTest"}
|
||||
declare_test!{StateTests_EIP158_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP158/EIP150/stEIPsingleCodeGasPrices"}
|
||||
declare_test!{StateTests_EIP158_EIP150_stChangedTests, "StateTests/EIP158/EIP150/stChangedTests"}
|
||||
|
||||
declare_test!{StateTests_EIP158_Homestead_stBoundsTest, "StateTests/EIP158/Homestead/stBoundsTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallCodes, "StateTests/EIP158/Homestead/stCallCodes"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallCreateCallCodeTest, "StateTests/EIP158/Homestead/stCallCreateCallCodeTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodes, "StateTests/EIP158/Homestead/stCallDelegateCodes"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodesCallCode, "StateTests/EIP158/Homestead/stCallDelegateCodesCallCode"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stDelegatecallTest, "StateTests/EIP158/Homestead/stDelegatecallTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stHomeSteadSpecific, "StateTests/EIP158/Homestead/stHomeSteadSpecific"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stInitCodeTest, "StateTests/EIP158/Homestead/stInitCodeTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stLogTests, "StateTests/EIP158/Homestead/stLogTests"}
|
||||
declare_test!{heavy => StateTests_EIP158_Homestead_stMemoryTest, "StateTests/EIP158/Homestead/stMemoryTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stPreCompiledContracts, "StateTests/EIP158/Homestead/stPreCompiledContracts"}
|
||||
declare_test!{heavy => StateTests_EIP158_Homestead_stQuadraticComplexityTest, "StateTests/EIP158/Homestead/stQuadraticComplexityTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stRecursiveCreate, "StateTests/EIP158/Homestead/stRecursiveCreate"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stRefundTest, "StateTests/EIP158/Homestead/stRefundTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stSpecialTest, "StateTests/EIP158/Homestead/stSpecialTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stSystemOperationsTest, "StateTests/EIP158/Homestead/stSystemOperationsTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stTransactionTest, "StateTests/EIP158/Homestead/stTransactionTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stWalletTest, "StateTests/EIP158/Homestead/stWalletTest"}
|
||||
@@ -91,10 +91,18 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
|
||||
self.ext.exists(address)
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
self.ext.exists_and_not_null(address)
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address) -> U256 {
|
||||
self.ext.balance(address)
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 {
|
||||
self.ext.origin_balance()
|
||||
}
|
||||
|
||||
fn blockhash(&self, number: &U256) -> H256 {
|
||||
self.ext.blockhash(number)
|
||||
}
|
||||
|
||||
@@ -36,3 +36,6 @@ declare_test!{BlockchainTests_Homestead_bcUncleHeaderValiditiy, "BlockchainTests
|
||||
declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/bcUncleTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
|
||||
// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
|
||||
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}
|
||||
|
||||
@@ -23,4 +23,6 @@ mod state;
|
||||
mod chain;
|
||||
mod homestead_state;
|
||||
mod homestead_chain;
|
||||
mod eip150_state;
|
||||
mod eip161_state;
|
||||
mod trie;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,17 +33,24 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
Some(x) if x < 1_150_000 => &old_schedule,
|
||||
Some(_) => &new_schedule
|
||||
};
|
||||
let allow_network_id_of_one = number.map_or(false, |n| n > 2600000);
|
||||
|
||||
let rlp: Vec<u8> = test.rlp.into();
|
||||
let res = UntrustedRlp::new(&rlp)
|
||||
.as_val()
|
||||
.map_err(From::from)
|
||||
.and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call));
|
||||
.and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one));
|
||||
|
||||
fail_unless(test.transaction.is_none() == res.is_err());
|
||||
if let (Some(tx), Some(sender)) = (test.transaction, test.sender) {
|
||||
let t = res.unwrap();
|
||||
fail_unless(t.sender().unwrap() == sender.into());
|
||||
let is_acceptable_network_id = match t.network_id() {
|
||||
None => true,
|
||||
Some(1) if allow_network_id_of_one => true,
|
||||
_ => false,
|
||||
};
|
||||
fail_unless(is_acceptable_network_id);
|
||||
let data: Vec<u8> = tx.data.into();
|
||||
fail_unless(t.data == data);
|
||||
fail_unless(t.gas_price == tx.gas_price.into());
|
||||
|
||||
@@ -21,16 +21,18 @@ use util::*;
|
||||
use util::using_queue::{UsingQueue, GetAction};
|
||||
use account_provider::AccountProvider;
|
||||
use views::{BlockView, HeaderView};
|
||||
use state::State;
|
||||
use header::Header;
|
||||
use state::{State, CleanupMode};
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||
use executive::contract_address;
|
||||
use block::{ClosedBlock, IsBlock, Block};
|
||||
use header::BlockNumber;
|
||||
use error::*;
|
||||
use transaction::{Action, SignedTransaction};
|
||||
use receipt::{Receipt, RichReceipt};
|
||||
use spec::Spec;
|
||||
use engines::Engine;
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||
use miner::work_notify::WorkPoster;
|
||||
use client::TransactionImportResult;
|
||||
use miner::price_info::PriceInfo;
|
||||
@@ -47,6 +49,17 @@ pub enum PendingSet {
|
||||
SealingOrElseQueue,
|
||||
}
|
||||
|
||||
/// Type of the gas limit to apply to the transaction queue.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum GasLimit {
|
||||
/// Depends on the block gas limit and is updated with every block.
|
||||
Auto,
|
||||
/// No limit.
|
||||
None,
|
||||
/// Set to a fixed gas value.
|
||||
Fixed(U256),
|
||||
}
|
||||
|
||||
/// Configures the behaviour of the miner.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct MinerOptions {
|
||||
@@ -64,12 +77,16 @@ pub struct MinerOptions {
|
||||
pub tx_gas_limit: U256,
|
||||
/// Maximum size of the transaction queue.
|
||||
pub tx_queue_size: usize,
|
||||
/// Strategy to use for prioritizing transactions in the queue.
|
||||
pub tx_queue_strategy: PrioritizationStrategy,
|
||||
/// Whether we should fallback to providing all the queue's transactions or just pending.
|
||||
pub pending_set: PendingSet,
|
||||
/// How many historical work packages can we store before running out?
|
||||
pub work_queue_size: usize,
|
||||
/// Can we submit two different solutions for the same block and expect both to result in an import?
|
||||
pub enable_resubmission: bool,
|
||||
/// Global gas limit for all transaction in the queue except for local and retracted.
|
||||
pub tx_queue_gas_limit: GasLimit,
|
||||
}
|
||||
|
||||
impl Default for MinerOptions {
|
||||
@@ -80,11 +97,13 @@ impl Default for MinerOptions {
|
||||
reseal_on_external_tx: false,
|
||||
reseal_on_own_tx: true,
|
||||
tx_gas_limit: !U256::zero(),
|
||||
tx_queue_size: 1024,
|
||||
tx_queue_size: 2048,
|
||||
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
|
||||
pending_set: PendingSet::AlwaysQueue,
|
||||
reseal_min_period: Duration::from_secs(2),
|
||||
work_queue_size: 20,
|
||||
work_queue_size: 5,
|
||||
enable_resubmission: true,
|
||||
tx_queue_gas_limit: GasLimit::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,7 +207,7 @@ impl Miner {
|
||||
/// Creates new instance of miner without accounts, but with given spec.
|
||||
pub fn with_spec(spec: &Spec) -> Miner {
|
||||
Miner {
|
||||
transaction_queue: Arc::new(Mutex::new(TransactionQueue::new())),
|
||||
transaction_queue: Arc::new(Mutex::new(TransactionQueue::default())),
|
||||
options: Default::default(),
|
||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||
sealing_block_last_request: Mutex::new(0),
|
||||
@@ -206,7 +225,13 @@ impl Miner {
|
||||
/// Creates new instance of miner
|
||||
pub fn new(options: MinerOptions, gas_pricer: GasPricer, spec: &Spec, accounts: Option<Arc<AccountProvider>>) -> Arc<Miner> {
|
||||
let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None };
|
||||
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)));
|
||||
let gas_limit = match options.tx_queue_gas_limit {
|
||||
GasLimit::Fixed(ref limit) => *limit,
|
||||
_ => !U256::zero(),
|
||||
};
|
||||
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(
|
||||
options.tx_queue_strategy, options.tx_queue_size, gas_limit, options.tx_gas_limit
|
||||
)));
|
||||
Arc::new(Miner {
|
||||
transaction_queue: txq,
|
||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||
@@ -243,6 +268,7 @@ impl Miner {
|
||||
fn prepare_sealing(&self, chain: &MiningBlockChainClient) {
|
||||
trace!(target: "miner", "prepare_sealing: entering");
|
||||
|
||||
let _timer = PerfTimer::new("prepare_sealing");
|
||||
{
|
||||
trace!(target: "miner", "recalibrating...");
|
||||
let txq = self.transaction_queue.clone();
|
||||
@@ -253,6 +279,7 @@ impl Miner {
|
||||
trace!(target: "miner", "done recalibration.");
|
||||
}
|
||||
|
||||
let _timer = PerfTimer::new("prepare_block");
|
||||
let (transactions, mut open_block, original_work_hash) = {
|
||||
let transactions = {self.transaction_queue.lock().top_transactions()};
|
||||
let mut sealing_work = self.sealing_work.lock();
|
||||
@@ -395,6 +422,10 @@ impl Miner {
|
||||
let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit();
|
||||
let mut queue = self.transaction_queue.lock();
|
||||
queue.set_gas_limit(gas_limit);
|
||||
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
|
||||
// Set total tx queue gas limit to be 20x the block gas limit.
|
||||
queue.set_total_gas_limit(gas_limit * 20.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if we had to prepare new pending block
|
||||
@@ -437,13 +468,39 @@ impl Miner {
|
||||
balance: chain.latest_balance(a),
|
||||
};
|
||||
|
||||
let best_block_header: Header = ::util::rlp::decode(&chain.best_block_header());
|
||||
transactions.into_iter()
|
||||
.map(|tx| transaction_queue.add(tx, &fetch_account, origin))
|
||||
.map(|tx| {
|
||||
match self.engine.verify_transaction_basic(&tx, &best_block_header) {
|
||||
Err(e) => {
|
||||
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
|
||||
Err(e)
|
||||
},
|
||||
Ok(()) => {
|
||||
transaction_queue.add(tx, &fetch_account, origin)
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Are we allowed to do a non-mandatory reseal?
|
||||
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() }
|
||||
|
||||
fn from_pending_block<H, F, G>(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H
|
||||
where F: Fn() -> H, G: Fn(&ClosedBlock) -> H {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
|| from_chain(),
|
||||
|b| {
|
||||
if b.block().header().number() > latest_block_number {
|
||||
map_block(b)
|
||||
} else {
|
||||
from_chain()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
|
||||
@@ -499,7 +556,7 @@ impl MinerService for Miner {
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
state.add_balance(&sender, &(needed_balance - balance));
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options));
|
||||
@@ -516,29 +573,35 @@ impl MinerService for Miner {
|
||||
}
|
||||
|
||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_balance(address),
|
||||
|b| b.block().fields().state.balance(address)
|
||||
)
|
||||
}
|
||||
|
||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_storage_at(address, position),
|
||||
|b| b.block().fields().state.storage_at(address, position)
|
||||
)
|
||||
}
|
||||
|
||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_nonce(address),
|
||||
|b| b.block().fields().state.nonce(address)
|
||||
)
|
||||
}
|
||||
|
||||
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address).map(|c| (*c).clone()))
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_code(address),
|
||||
|b| b.block().fields().state.code(address).map(|c| (*c).clone())
|
||||
)
|
||||
}
|
||||
|
||||
fn set_author(&self, author: Address) {
|
||||
@@ -683,50 +746,74 @@ impl MinerService for Miner {
|
||||
queue.top_transactions()
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
fn pending_transactions(&self, best_block: BlockNumber) -> Vec<SignedTransaction> {
|
||||
let queue = self.transaction_queue.lock();
|
||||
let sw = self.sealing_work.lock();
|
||||
// TODO: should only use the sealing_work when it's current (it could be an old block)
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
match (&self.options.pending_set, sealing_set) {
|
||||
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.top_transactions(),
|
||||
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().to_owned()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.top_transactions(),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.top_transactions(),
|
||||
|sealing| sealing.transactions().to_owned()
|
||||
)
|
||||
},
|
||||
PendingSet::AlwaysSealing => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| vec![],
|
||||
|sealing| sealing.transactions().to_owned()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256> {
|
||||
fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256> {
|
||||
let queue = self.transaction_queue.lock();
|
||||
let sw = self.sealing_work.lock();
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
match (&self.options.pending_set, sealing_set) {
|
||||
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.pending_hashes(),
|
||||
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().iter().map(|t| t.hash()).collect()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.pending_hashes(),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.pending_hashes(),
|
||||
|sealing| sealing.transactions().iter().map(|t| t.hash()).collect()
|
||||
)
|
||||
},
|
||||
PendingSet::AlwaysSealing => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| vec![],
|
||||
|sealing| sealing.transactions().iter().map(|t| t.hash()).collect()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||
fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction> {
|
||||
let queue = self.transaction_queue.lock();
|
||||
let sw = self.sealing_work.lock();
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
match (&self.options.pending_set, sealing_set) {
|
||||
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.find(hash),
|
||||
(_, sealing) => sealing.and_then(|s| s.transactions().iter().find(|t| &t.hash() == hash).cloned()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.find(hash),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.find(hash),
|
||||
|sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned()
|
||||
)
|
||||
},
|
||||
PendingSet::AlwaysSealing => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| None,
|
||||
|sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||
(true, Some(pending)) => {
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| None,
|
||||
|pending| {
|
||||
let txs = pending.transactions();
|
||||
txs.iter()
|
||||
.map(|t| t.hash())
|
||||
@@ -747,15 +834,15 @@ impl MinerService for Miner {
|
||||
logs: receipt.logs.clone(),
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||
(true, Some(pending)) => {
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt> {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| BTreeMap::new(),
|
||||
|pending| {
|
||||
let hashes = pending.transactions()
|
||||
.iter()
|
||||
.map(|t| t.hash());
|
||||
@@ -763,9 +850,8 @@ impl MinerService for Miner {
|
||||
let receipts = pending.receipts().iter().cloned();
|
||||
|
||||
hashes.zip(receipts).collect()
|
||||
},
|
||||
_ => BTreeMap::new()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256> {
|
||||
@@ -918,7 +1004,7 @@ impl MinerService for Miner {
|
||||
mod tests {
|
||||
|
||||
use std::time::Duration;
|
||||
use super::super::MinerService;
|
||||
use super::super::{MinerService, PrioritizationStrategy};
|
||||
use super::*;
|
||||
use util::*;
|
||||
use client::{TestBlockChainClient, EachBlockWith};
|
||||
@@ -969,6 +1055,8 @@ mod tests {
|
||||
reseal_min_period: Duration::from_secs(5),
|
||||
tx_gas_limit: !U256::zero(),
|
||||
tx_queue_size: 1024,
|
||||
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
|
||||
tx_queue_gas_limit: GasLimit::None,
|
||||
pending_set: PendingSet::AlwaysSealing,
|
||||
work_queue_size: 5,
|
||||
enable_resubmission: true,
|
||||
@@ -993,22 +1081,50 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
}.sign(keypair.secret(), None)
|
||||
};
|
||||
|
||||
let best_block = 0;
|
||||
// when
|
||||
let res = miner.import_own_transaction(&client, transaction);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes().len(), 1);
|
||||
assert_eq!(miner.pending_receipts().len(), 1);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1);
|
||||
assert_eq!(miner.pending_receipts(best_block).len(), 1);
|
||||
// This method will let us know if pending block was created (before calling that method)
|
||||
assert_eq!(miner.enable_and_prepare_sealing(&client), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_use_pending_block_if_best_block_is_higher() {
|
||||
// given
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
let transaction = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::zero(),
|
||||
data: "3331600055".from_hex().unwrap(),
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret(), None)
|
||||
};
|
||||
let best_block = 10;
|
||||
// when
|
||||
let res = miner.import_own_transaction(&client, transaction);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_import_external_transaction() {
|
||||
// given
|
||||
@@ -1023,18 +1139,18 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
}.sign(keypair.secret(), None)
|
||||
};
|
||||
|
||||
let best_block = 0;
|
||||
// when
|
||||
let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes().len(), 0);
|
||||
assert_eq!(miner.pending_transactions().len(), 0);
|
||||
assert_eq!(miner.pending_receipts().len(), 0);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
||||
// This method will let us know if pending block was created (before calling that method)
|
||||
assert_eq!(miner.enable_and_prepare_sealing(&client), true);
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ mod transaction_queue;
|
||||
mod work_notify;
|
||||
mod price_info;
|
||||
|
||||
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions};
|
||||
pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||
pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
||||
pub use self::external::{ExternalMiner, ExternalMinerService};
|
||||
pub use client::TransactionImportResult;
|
||||
|
||||
@@ -56,6 +56,7 @@ use std::collections::BTreeMap;
|
||||
use util::{H256, U256, Address, Bytes};
|
||||
use client::{MiningBlockChainClient, Executed, CallAnalytics};
|
||||
use block::ClosedBlock;
|
||||
use header::BlockNumber;
|
||||
use receipt::{RichReceipt, Receipt};
|
||||
use error::{Error, CallError};
|
||||
use transaction::SignedTransaction;
|
||||
@@ -115,7 +116,7 @@ pub trait MinerService : Send + Sync {
|
||||
Result<TransactionImportResult, Error>;
|
||||
|
||||
/// Returns hashes of transactions currently in pending
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256>;
|
||||
fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256>;
|
||||
|
||||
/// Removes all transactions from the queue and restart mining operation.
|
||||
fn clear_and_reset(&self, chain: &MiningBlockChainClient);
|
||||
@@ -135,19 +136,19 @@ pub trait MinerService : Send + Sync {
|
||||
where F: FnOnce(&ClosedBlock) -> T, Self: Sized;
|
||||
|
||||
/// Query pending transactions for hash.
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction>;
|
||||
fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction>;
|
||||
|
||||
/// Get a list of all transactions.
|
||||
fn all_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get a list of all pending transactions.
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
fn pending_transactions(&self, best_block: BlockNumber) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get a list of all pending receipts.
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt>;
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt>;
|
||||
|
||||
/// Get a particular reciept.
|
||||
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt>;
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
|
||||
|
||||
/// Returns highest transaction nonce for given address.
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
|
||||
@@ -21,55 +21,6 @@
|
||||
//! transaction's nonce and next nonce expected from this sender). If nonces are equal transaction's gas price is used
|
||||
//! for comparison (higher gas price = higher priority).
|
||||
//!
|
||||
//! # Usage Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate ethcore_util as util;
|
||||
//! extern crate ethcore;
|
||||
//! extern crate rustc_serialize;
|
||||
//!
|
||||
//! use util::crypto::KeyPair;
|
||||
//! use util::hash::Address;
|
||||
//! use util::numbers::{Uint, U256};
|
||||
//! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::transaction::*;
|
||||
//! use rustc_serialize::hex::FromHex;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let key = KeyPair::create().unwrap();
|
||||
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) };
|
||||
//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) };
|
||||
//!
|
||||
//! let st1 = t1.sign(&key.secret());
|
||||
//! let st2 = t2.sign(&key.secret());
|
||||
//! let default_nonce = |_a: &Address| AccountDetails {
|
||||
//! nonce: U256::from(10),
|
||||
//! balance: U256::from(1_000_000),
|
||||
//! };
|
||||
//!
|
||||
//! let mut txq = TransactionQueue::new();
|
||||
//! txq.add(st2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//! txq.add(st1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//!
|
||||
//! // Check status
|
||||
//! assert_eq!(txq.status().pending, 2);
|
||||
//! // Check top transactions
|
||||
//! let top = txq.top_transactions();
|
||||
//! assert_eq!(top.len(), 2);
|
||||
//! assert_eq!(top[0], st1);
|
||||
//! assert_eq!(top[1], st2);
|
||||
//!
|
||||
//! // And when transaction is removed (but nonce haven't changed)
|
||||
//! // it will move subsequent transactions to future
|
||||
//! txq.remove_invalid(&st1.hash(), &default_nonce);
|
||||
//! assert_eq!(txq.status().pending, 0);
|
||||
//! assert_eq!(txq.status().future, 1);
|
||||
//! assert_eq!(txq.top_transactions().len(), 0);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Maintaing valid state
|
||||
//!
|
||||
//! 1. Whenever transaction is imported to queue (to queue) all other transactions from this sender are revalidated in current. It means that they are moved to future and back again (height recalculation & gap filling).
|
||||
@@ -134,9 +85,17 @@ struct TransactionOrder {
|
||||
/// Gas Price of the transaction.
|
||||
/// Low gas price = Low priority (processed later)
|
||||
gas_price: U256,
|
||||
/// Gas (limit) of the transaction.
|
||||
/// Gas usage priority factor. Usage depends on strategy.
|
||||
/// Represents the linear increment in required gas price for heavy transactions.
|
||||
///
|
||||
/// High gas limit + Low gas price = Low priority
|
||||
/// High gas limit + High gas price = High priority
|
||||
gas_factor: U256,
|
||||
/// Gas (limit) of the transaction. Usage depends on strategy.
|
||||
/// Low gas limit = High priority (processed earlier)
|
||||
gas: U256,
|
||||
/// Transaction ordering strategy
|
||||
strategy: PrioritizationStrategy,
|
||||
/// Hash to identify associated transaction
|
||||
hash: H256,
|
||||
/// Origin of the transaction
|
||||
@@ -147,11 +106,15 @@ struct TransactionOrder {
|
||||
|
||||
|
||||
impl TransactionOrder {
|
||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
||||
|
||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256, min_gas_price: U256, strategy: PrioritizationStrategy) -> Self {
|
||||
let factor = (tx.transaction.gas >> 15) * min_gas_price;
|
||||
TransactionOrder {
|
||||
nonce_height: tx.nonce() - base_nonce,
|
||||
gas_price: tx.transaction.gas_price,
|
||||
gas: tx.transaction.gas,
|
||||
gas_factor: factor,
|
||||
strategy: strategy,
|
||||
hash: tx.hash(),
|
||||
origin: tx.origin,
|
||||
penalties: 0,
|
||||
@@ -199,18 +162,28 @@ impl Ord for TransactionOrder {
|
||||
return self.origin.cmp(&b.origin);
|
||||
}
|
||||
|
||||
// Then compare gas usage
|
||||
let a_gas = self.gas;
|
||||
let b_gas = b.gas;
|
||||
if a_gas != b_gas {
|
||||
return a_gas.cmp(&b_gas);
|
||||
match self.strategy {
|
||||
PrioritizationStrategy::GasAndGasPrice => {
|
||||
if self.gas != b.gas {
|
||||
return self.gas.cmp(&b.gas);
|
||||
}
|
||||
},
|
||||
PrioritizationStrategy::GasFactorAndGasPrice => {
|
||||
// avoiding overflows
|
||||
// (gp1 - g1) > (gp2 - g2) <=>
|
||||
// (gp1 + g2) > (gp2 + g1)
|
||||
let f_a = self.gas_price + b.gas_factor;
|
||||
let f_b = b.gas_price + self.gas_factor;
|
||||
if f_a != f_b {
|
||||
return f_b.cmp(&f_a);
|
||||
}
|
||||
},
|
||||
PrioritizationStrategy::GasPriceOnly => {},
|
||||
}
|
||||
|
||||
// Then compare gas_prices
|
||||
let a_gas_price = self.gas_price;
|
||||
let b_gas_price = b.gas_price;
|
||||
if a_gas_price != b_gas_price {
|
||||
return b_gas_price.cmp(&a_gas_price);
|
||||
if self.gas_price != b.gas_price {
|
||||
return b.gas_price.cmp(&self.gas_price);
|
||||
}
|
||||
|
||||
// Compare hashes
|
||||
@@ -257,6 +230,7 @@ struct TransactionSet {
|
||||
by_priority: BTreeSet<TransactionOrder>,
|
||||
by_address: Table<Address, U256, TransactionOrder>,
|
||||
limit: usize,
|
||||
gas_limit: U256,
|
||||
}
|
||||
|
||||
impl TransactionSet {
|
||||
@@ -277,14 +251,20 @@ impl TransactionSet {
|
||||
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||
/// Returns addresses and lowest nonces of transactions removed because of limit.
|
||||
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashMap<Address, U256>> {
|
||||
let len = self.by_priority.len();
|
||||
if len <= self.limit {
|
||||
return None;
|
||||
}
|
||||
let mut count = 0;
|
||||
let mut gas: U256 = 0.into();
|
||||
let to_drop : Vec<(Address, U256)> = {
|
||||
self.by_priority
|
||||
.iter()
|
||||
.skip(self.limit)
|
||||
.filter(|order| {
|
||||
count = count + 1;
|
||||
let r = gas.overflowing_add(order.gas);
|
||||
if r.1 { return false }
|
||||
gas = r.0;
|
||||
// Own and retracted transactions are allowed to go above all limits.
|
||||
order.origin != TransactionOrigin::Local && order.origin != TransactionOrigin::RetractedBlock &&
|
||||
(gas > self.gas_limit || count > self.limit)
|
||||
})
|
||||
.map(|order| by_hash.get(&order.hash)
|
||||
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
||||
.map(|tx| (tx.sender(), tx.nonce()))
|
||||
@@ -295,6 +275,7 @@ impl TransactionSet {
|
||||
.fold(HashMap::new(), |mut removed, (sender, nonce)| {
|
||||
let order = self.drop(&sender, &nonce)
|
||||
.expect("Transaction has just been found in `by_priority`; so it is in `by_address` also.");
|
||||
trace!(target: "txqueue", "Dropped out of limit transaction: {:?}", order.hash);
|
||||
|
||||
by_hash.remove(&order.hash)
|
||||
.expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`");
|
||||
@@ -350,8 +331,32 @@ pub struct AccountDetails {
|
||||
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
||||
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
|
||||
|
||||
/// Describes the strategy used to prioritize transactions in the queue.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PrioritizationStrategy {
|
||||
/// Use only gas price. Disregards the actual computation cost of the transaction.
|
||||
/// i.e. Higher gas price = Higher priority
|
||||
GasPriceOnly,
|
||||
/// Use gas limit and then gas price.
|
||||
/// i.e. Higher gas limit = Lower priority
|
||||
GasAndGasPrice,
|
||||
/// Calculate and use priority based on gas and gas price.
|
||||
/// PRIORITY = GAS_PRICE - GAS/2^15 * MIN_GAS_PRICE
|
||||
///
|
||||
/// Rationale:
|
||||
/// Heavy transactions are paying linear cost (GAS * GAS_PRICE)
|
||||
/// while the computation might be more expensive.
|
||||
///
|
||||
/// i.e.
|
||||
/// 1M gas tx with `gas_price=30*min` has the same priority
|
||||
/// as 32k gas tx with `gas_price=min`
|
||||
GasFactorAndGasPrice,
|
||||
}
|
||||
|
||||
/// `TransactionQueue` implementation
|
||||
pub struct TransactionQueue {
|
||||
/// Prioritization strategy for this queue
|
||||
strategy: PrioritizationStrategy,
|
||||
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
|
||||
minimal_gas_price: U256,
|
||||
/// The maximum amount of gas any individual transaction may use.
|
||||
@@ -370,31 +375,34 @@ pub struct TransactionQueue {
|
||||
|
||||
impl Default for TransactionQueue {
|
||||
fn default() -> Self {
|
||||
TransactionQueue::new()
|
||||
TransactionQueue::new(PrioritizationStrategy::GasPriceOnly)
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionQueue {
|
||||
/// Creates new instance of this Queue
|
||||
pub fn new() -> Self {
|
||||
Self::with_limits(1024, !U256::zero())
|
||||
pub fn new(strategy: PrioritizationStrategy) -> Self {
|
||||
Self::with_limits(strategy, 1024, !U256::zero(), !U256::zero())
|
||||
}
|
||||
|
||||
/// Create new instance of this Queue with specified limits
|
||||
pub fn with_limits(limit: usize, tx_gas_limit: U256) -> Self {
|
||||
pub fn with_limits(strategy: PrioritizationStrategy, limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self {
|
||||
let current = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
limit: limit,
|
||||
gas_limit: gas_limit,
|
||||
};
|
||||
|
||||
let future = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
limit: limit,
|
||||
gas_limit: gas_limit,
|
||||
};
|
||||
|
||||
TransactionQueue {
|
||||
strategy: strategy,
|
||||
minimal_gas_price: U256::zero(),
|
||||
tx_gas_limit: tx_gas_limit,
|
||||
gas_limit: !U256::zero(),
|
||||
@@ -441,6 +449,13 @@ impl TransactionQueue {
|
||||
};
|
||||
}
|
||||
|
||||
/// Sets new total gas limit.
|
||||
pub fn set_total_gas_limit(&mut self, gas_limit: U256) {
|
||||
self.future.gas_limit = gas_limit;
|
||||
self.current.gas_limit = gas_limit;
|
||||
self.future.enforce_limit(&mut self.by_hash);
|
||||
}
|
||||
|
||||
/// Set the new limit for the amount of gas any individual transaction may have.
|
||||
/// Any transaction already imported to the queue is not affected.
|
||||
pub fn set_tx_gas_limit(&mut self, limit: U256) {
|
||||
@@ -560,7 +575,7 @@ impl TransactionQueue {
|
||||
};
|
||||
for k in nonces_from_sender {
|
||||
let order = self.future.drop(&sender, &k).unwrap();
|
||||
self.current.insert(sender, k, order.penalize());
|
||||
self.future.insert(sender, k, order.penalize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,6 +599,8 @@ impl TransactionQueue {
|
||||
let nonce = transaction.nonce();
|
||||
let current_nonce = fetch_account(&sender).nonce;
|
||||
|
||||
trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash());
|
||||
|
||||
// Remove from future
|
||||
let order = self.future.drop(&sender, &nonce);
|
||||
if order.is_some() {
|
||||
@@ -658,6 +675,15 @@ impl TransactionQueue {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn future_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.future.by_priority
|
||||
.iter()
|
||||
.map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`"))
|
||||
.map(|t| t.transaction.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns hashes of all transactions from current, ordered by priority.
|
||||
pub fn pending_hashes(&self) -> Vec<H256> {
|
||||
self.current.by_priority
|
||||
@@ -736,10 +762,21 @@ impl TransactionQueue {
|
||||
return Err(TransactionError::AlreadyImported);
|
||||
}
|
||||
|
||||
let min_gas_price = (self.minimal_gas_price, self.strategy);
|
||||
let address = tx.sender();
|
||||
let nonce = tx.nonce();
|
||||
let hash = tx.hash();
|
||||
|
||||
{
|
||||
// Rough size sanity check
|
||||
let gas = &tx.transaction.gas;
|
||||
if U256::from(tx.transaction.data.len()) > *gas {
|
||||
// Droping transaction
|
||||
trace!(target: "txqueue", "Dropping oversized transaction: {:?} (gas: {} < size {})", hash, gas, tx.transaction.data.len());
|
||||
return Err(TransactionError::LimitReached);
|
||||
}
|
||||
}
|
||||
|
||||
// The transaction might be old, let's check that.
|
||||
// This has to be the first test, otherwise calculating
|
||||
// nonce height would result in overflow.
|
||||
@@ -763,7 +800,7 @@ impl TransactionQueue {
|
||||
if nonce > next_nonce {
|
||||
// We have a gap - put to future.
|
||||
// Insert transaction (or replace old one with lower gas price)
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.future, &mut self.by_hash)));
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.future, &mut self.by_hash)));
|
||||
// Enforce limit in Future
|
||||
let removed = self.future.enforce_limit(&mut self.by_hash);
|
||||
// Return an error if this transaction was not imported because of limit.
|
||||
@@ -779,7 +816,7 @@ impl TransactionQueue {
|
||||
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
|
||||
|
||||
// Replace transaction if any
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash)));
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.current, &mut self.by_hash)));
|
||||
// Keep track of highest nonce stored in current
|
||||
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
||||
self.last_nonces.insert(address, new_max);
|
||||
@@ -815,8 +852,8 @@ impl TransactionQueue {
|
||||
///
|
||||
/// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher
|
||||
/// gas_price)
|
||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, min_gas_price: (U256, PrioritizationStrategy), set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||
let order = TransactionOrder::for_transaction(&tx, base_nonce, min_gas_price.0, min_gas_price.1);
|
||||
let hash = tx.hash();
|
||||
let address = tx.sender();
|
||||
let nonce = tx.nonce();
|
||||
@@ -837,12 +874,14 @@ impl TransactionQueue {
|
||||
let old_fee = old.gas_price;
|
||||
let new_fee = order.gas_price;
|
||||
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
||||
trace!(target: "txqueue", "Didn't insert transaction because gas price was too low: {:?} ({:?} stays in the queue)", order.hash, old.hash);
|
||||
// Put back old transaction since it has greater priority (higher gas_price)
|
||||
set.insert(address, nonce, old);
|
||||
// and remove new one
|
||||
by_hash.remove(&order.hash).expect("The hash has been just inserted and no other line is altering `by_hash`.");
|
||||
false
|
||||
} else {
|
||||
trace!(target: "txqueue", "Replaced transaction: {:?} with transaction with higher gas price: {:?}", old.hash, order.hash);
|
||||
// Make sure we remove old transaction entirely
|
||||
by_hash.remove(&old.hash).expect("The hash is coming from `future` so it has to be in `by_hash`.");
|
||||
true
|
||||
@@ -918,7 +957,7 @@ mod test {
|
||||
|
||||
fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
new_unsigned_tx_with_gas(default_nonce_val(), gas, gas_price).sign(keypair.secret())
|
||||
new_unsigned_tx_with_gas(default_nonce_val(), gas, gas_price).sign(keypair.secret(), None)
|
||||
}
|
||||
|
||||
fn new_tx() -> SignedTransaction {
|
||||
@@ -941,7 +980,7 @@ mod test {
|
||||
let mut tx2 = new_unsigned_tx(nonce);
|
||||
tx2.gas_price = 2.into();
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None))
|
||||
}
|
||||
|
||||
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
@@ -956,8 +995,7 @@ mod test {
|
||||
tx.gas_price = tx.gas_price + gas_price;
|
||||
let mut tx2 = new_unsigned_tx(nonce + 1.into());
|
||||
tx2.gas_price = tx2.gas_price + gas_price;
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None))
|
||||
}
|
||||
|
||||
fn new_txs_with_gas_price_diff(second_nonce: U256, gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
@@ -968,7 +1006,7 @@ mod test {
|
||||
let mut tx2 = new_unsigned_tx(nonce + second_nonce);
|
||||
tx2.gas_price = tx2.gas_price + gas_price;
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None))
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -982,13 +1020,18 @@ mod test {
|
||||
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
|
||||
}
|
||||
|
||||
fn transaction_order(tx: &VerifiedTransaction, nonce: U256) -> TransactionOrder {
|
||||
TransactionOrder::for_transaction(tx, nonce, 0.into(), PrioritizationStrategy::GasPriceOnly)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_transaction_set() {
|
||||
// given
|
||||
let mut set = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
limit: 1
|
||||
limit: 1,
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
||||
@@ -1002,9 +1045,9 @@ mod test {
|
||||
x
|
||||
};
|
||||
// Insert both transactions
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||
let order1 = transaction_order(&tx1, U256::zero());
|
||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
|
||||
let order2 = transaction_order(&tx2, U256::zero());
|
||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||
assert_eq!(set.by_priority.len(), 2);
|
||||
assert_eq!(set.by_address.len(), 2);
|
||||
@@ -1027,7 +1070,8 @@ mod test {
|
||||
let mut set = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
limit: 1
|
||||
limit: 1,
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
// Create two transactions with same nonce
|
||||
// (same hash)
|
||||
@@ -1043,12 +1087,12 @@ mod test {
|
||||
x
|
||||
};
|
||||
// Insert both transactions
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||
let order1 = transaction_order(&tx1, U256::zero());
|
||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
// Two different orders (imagine nonce changed in the meantime)
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::one());
|
||||
let order2 = transaction_order(&tx2, U256::one());
|
||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
@@ -1063,7 +1107,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_similar_txs();
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
@@ -1088,7 +1132,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_move_all_transactions_from_future() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs_with_gas_price_diff(1.into(), 1.into());
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
@@ -1114,7 +1158,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_import_tx() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
|
||||
// when
|
||||
@@ -1129,7 +1173,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_order_by_gas() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasAndGasPrice);
|
||||
let tx1 = new_tx_with_gas(50000.into(), 40.into());
|
||||
let tx2 = new_tx_with_gas(40000.into(), 30.into());
|
||||
let tx3 = new_tx_with_gas(30000.into(), 10.into());
|
||||
@@ -1159,10 +1203,45 @@ mod test {
|
||||
assert_eq!(txq.top_transactions()[2].gas_price, 20.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_order_by_gas_factor() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasFactorAndGasPrice);
|
||||
|
||||
let tx1 = new_tx_with_gas(150_000.into(), 40.into());
|
||||
let tx2 = new_tx_with_gas(40_000.into(), 16.into());
|
||||
let tx3 = new_tx_with_gas(30_000.into(), 15.into());
|
||||
let tx4 = new_tx_with_gas(150_000.into(), 62.into());
|
||||
txq.set_minimal_gas_price(15.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx1, &default_nonce, TransactionOrigin::External);
|
||||
let res2 = txq.add(tx2, &default_nonce, TransactionOrigin::External);
|
||||
let res3 = txq.add(tx3, &default_nonce, TransactionOrigin::External);
|
||||
let res4 = txq.add(tx4, &default_nonce, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(res2.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(res3.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
|
||||
let stats = txq.status();
|
||||
assert_eq!(stats.pending, 4);
|
||||
println!("{:?}", txq.top_transactions());
|
||||
assert_eq!(txq.top_transactions()[0].gas, 30_000.into());
|
||||
assert_eq!(txq.top_transactions()[1].gas, 150_000.into());
|
||||
assert_eq!(txq.top_transactions()[2].gas, 40_000.into());
|
||||
assert_eq!(txq.top_transactions()[3].gas, 150_000.into());
|
||||
assert_eq!(txq.top_transactions()[0].gas_price, 15.into());
|
||||
assert_eq!(txq.top_transactions()[1].gas_price, 62.into());
|
||||
assert_eq!(txq.top_transactions()[2].gas_price, 16.into());
|
||||
assert_eq!(txq.top_transactions()[3].gas_price, 40.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gas_limit_should_never_overflow() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
txq.set_gas_limit(U256::zero());
|
||||
assert_eq!(txq.gas_limit, U256::zero());
|
||||
|
||||
@@ -1176,7 +1255,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_import_transaction_above_gas_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let gas = tx.gas;
|
||||
let limit = gas / U256::from(2);
|
||||
@@ -1198,7 +1277,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_drop_transactions_from_senders_without_balance() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let account = |a: &Address| AccountDetails {
|
||||
nonce: default_nonce(a).nonce,
|
||||
@@ -1221,7 +1300,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
@@ -1241,7 +1320,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
@@ -1258,7 +1337,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_reject_incorrectly_signed_transaction() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_unsigned_tx(U256::from(123));
|
||||
let stx = {
|
||||
let mut s = RlpStream::new_list(9);
|
||||
@@ -1283,7 +1362,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_import_txs_from_same_sender() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
|
||||
@@ -1301,7 +1380,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_prioritize_local_transactions_within_same_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
// the second one has same nonce but higher `gas_price`
|
||||
let (_, tx2) = new_similar_txs();
|
||||
@@ -1322,7 +1401,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_prioritize_reimported_transactions_within_same_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
// the second one has same nonce but higher `gas_price`
|
||||
let (_, tx2) = new_similar_txs();
|
||||
@@ -1343,7 +1422,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
|
||||
// when
|
||||
@@ -1357,10 +1436,40 @@ mod test {
|
||||
assert_eq!(top.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_penalize_transactions_from_sender_in_future() {
|
||||
// given
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::default();
|
||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||
let (txa, txb) = new_txs(U256::from(1));
|
||||
let (tx1, tx2) = new_txs_with_higher_gas_price(U256::from(3));
|
||||
|
||||
// insert everything
|
||||
txq.add(txa.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(txb.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx1.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
|
||||
assert_eq!(txq.status().future, 4);
|
||||
|
||||
// when
|
||||
txq.penalize(&tx1.hash());
|
||||
|
||||
// then
|
||||
let top = txq.future_transactions();
|
||||
assert_eq!(top[0], txa);
|
||||
assert_eq!(top[1], txb);
|
||||
assert_eq!(top[2], tx1);
|
||||
assert_eq!(top[3], tx2);
|
||||
assert_eq!(top.len(), 4);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_penalize_transactions_from_sender() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||
let (txa, txb) = new_txs(U256::from(1));
|
||||
let (tx1, tx2) = new_txs_with_higher_gas_price(U256::from(3));
|
||||
@@ -1393,7 +1502,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_pending_hashes() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
|
||||
@@ -1411,7 +1520,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_put_transaction_to_futures_if_gap_detected() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(2));
|
||||
|
||||
@@ -1437,7 +1546,7 @@ mod test {
|
||||
!U256::zero() };
|
||||
let next2_nonce = default_nonce_val() + U256::from(3);
|
||||
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1456,12 +1565,12 @@ mod test {
|
||||
#[test]
|
||||
fn should_move_transactions_if_gap_filled() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let kp = KeyPair::create().unwrap();
|
||||
let secret = kp.secret();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(secret);
|
||||
let tx1 = new_unsigned_tx(U256::from(124)).sign(secret);
|
||||
let tx2 = new_unsigned_tx(U256::from(125)).sign(secret);
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(secret, None);
|
||||
let tx1 = new_unsigned_tx(U256::from(124)).sign(secret, None);
|
||||
let tx2 = new_unsigned_tx(U256::from(125)).sign(secret, None);
|
||||
|
||||
txq.add(tx, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
@@ -1480,7 +1589,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_remove_transaction() {
|
||||
// given
|
||||
let mut txq2 = TransactionQueue::new();
|
||||
let mut txq2 = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(3));
|
||||
txq2.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq2.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1501,7 +1610,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_move_transactions_to_future_if_gap_introduced() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let tx3 = new_tx();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1522,7 +1631,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_clear_queue() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::one());
|
||||
|
||||
// add
|
||||
@@ -1542,7 +1651,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||
let (tx, tx2) = new_txs(U256::one());
|
||||
let sender = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
@@ -1564,7 +1673,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(2, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 2, !U256::zero(), !U256::zero());
|
||||
let tx = new_tx();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let sender = tx1.sender().unwrap();
|
||||
@@ -1585,7 +1694,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn should_limit_future_transactions() {
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||
txq.current.set_limit(10);
|
||||
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(4), U256::from(1));
|
||||
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(4), U256::from(2));
|
||||
@@ -1602,9 +1711,37 @@ mod test {
|
||||
assert_eq!(txq.status().future, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_limit_by_gas() {
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
||||
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(1), U256::from(1));
|
||||
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(1), U256::from(2));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).ok();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).ok();
|
||||
txq.add(tx3.clone(), &default_nonce, TransactionOrigin::External).ok();
|
||||
txq.add(tx4.clone(), &default_nonce, TransactionOrigin::External).ok();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_keep_own_transactions_above_gas_limit() {
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
||||
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(1), U256::from(1));
|
||||
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(1), U256::from(2));
|
||||
let (tx5, tx6) = new_txs_with_gas_price_diff(U256::from(1), U256::from(2));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx5.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
// Not accepted because of limit
|
||||
txq.add(tx6.clone(), &default_nonce, TransactionOrigin::External).unwrap_err();
|
||||
txq.add(tx3.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx4.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
assert_eq!(txq.status().pending, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_drop_transactions_with_old_nonces() {
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let last_nonce = tx.nonce + U256::one();
|
||||
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
|
||||
@@ -1624,7 +1761,7 @@ mod test {
|
||||
// given
|
||||
let nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce + U256::one(),
|
||||
balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (_tx1, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
@@ -1643,7 +1780,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_accept_same_transaction_twice_if_removed() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1664,7 +1801,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_move_to_future_if_state_nonce_is_higher() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let tx3 = new_tx();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1687,13 +1824,13 @@ mod test {
|
||||
fn should_replace_same_transaction_when_has_higher_fee() {
|
||||
init_log();
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(keypair.secret(), None);
|
||||
let tx2 = {
|
||||
let mut tx2 = (*tx).clone();
|
||||
tx2.gas_price = U256::from(200);
|
||||
tx2.sign(keypair.secret())
|
||||
tx2.sign(keypair.secret(), None)
|
||||
};
|
||||
|
||||
// when
|
||||
@@ -1710,18 +1847,18 @@ mod test {
|
||||
#[test]
|
||||
fn should_replace_same_transaction_when_importing_to_futures() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx0 = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let tx0 = new_unsigned_tx(U256::from(123)).sign(keypair.secret(), None);
|
||||
let tx1 = {
|
||||
let mut tx1 = (*tx0).clone();
|
||||
tx1.nonce = U256::from(124);
|
||||
tx1.sign(keypair.secret())
|
||||
tx1.sign(keypair.secret(), None)
|
||||
};
|
||||
let tx2 = {
|
||||
let mut tx2 = (*tx1).clone();
|
||||
tx2.gas_price = U256::from(200);
|
||||
tx2.sign(keypair.secret())
|
||||
tx2.sign(keypair.secret(), None)
|
||||
};
|
||||
|
||||
// when
|
||||
@@ -1744,7 +1881,7 @@ mod test {
|
||||
!U256::zero() };
|
||||
let next_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1762,7 +1899,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_none_when_transaction_from_given_address_does_not_exist() {
|
||||
// given
|
||||
let txq = TransactionQueue::new();
|
||||
let txq = TransactionQueue::default();
|
||||
|
||||
// then
|
||||
assert_eq!(txq.last_nonce(&Address::default()), None);
|
||||
@@ -1771,7 +1908,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let from = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
@@ -1787,7 +1924,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
||||
@@ -1805,7 +1942,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_valid_last_nonce_after_remove_all() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::from(4));
|
||||
let sender = tx1.sender().unwrap();
|
||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||
@@ -1829,7 +1966,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_true_if_there_is_local_transaction_pending() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||
|
||||
@@ -1845,7 +1982,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_keep_right_order_in_future() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
default_nonce(a).balance };
|
||||
@@ -1862,7 +1999,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_last_nonce() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2, tx2_2, tx3) = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
@@ -1874,7 +2011,7 @@ mod test {
|
||||
let tx3 = new_unsigned_tx(nonce + 2.into());
|
||||
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None))
|
||||
};
|
||||
let sender = tx1.sender().unwrap();
|
||||
txq.add(tx1, &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
|
||||
@@ -27,7 +27,7 @@ use ids::BlockID;
|
||||
use views::BlockView;
|
||||
use super::state_db::StateDB;
|
||||
|
||||
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut, BytesConvertable};
|
||||
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut, BytesConvertable, U256, Uint};
|
||||
use util::Mutex;
|
||||
use util::hash::{FixedHash, H256};
|
||||
use util::journaldb::{self, Algorithm, JournalDB};
|
||||
@@ -39,6 +39,8 @@ use self::account::Account;
|
||||
use self::block::AbridgedBlock;
|
||||
use self::io::SnapshotWriter;
|
||||
|
||||
use super::account::Account as StateAccount;
|
||||
|
||||
use crossbeam::{scope, ScopedJoinHandle};
|
||||
use rand::{Rng, OsRng};
|
||||
|
||||
@@ -417,6 +419,7 @@ impl StateRebuilder {
|
||||
/// Feed an uncompressed state chunk into the rebuilder.
|
||||
pub fn feed(&mut self, chunk: &[u8]) -> Result<(), ::error::Error> {
|
||||
let rlp = UntrustedRlp::new(chunk);
|
||||
let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp();
|
||||
let account_fat_rlps: Vec<_> = rlp.iter().map(|r| r.as_raw()).collect();
|
||||
let mut pairs = Vec::with_capacity(rlp.item_count());
|
||||
let backing = self.db.backing().clone();
|
||||
@@ -464,7 +467,9 @@ impl StateRebuilder {
|
||||
};
|
||||
|
||||
for (hash, thin_rlp) in pairs {
|
||||
bloom.set(hash.as_slice());
|
||||
if &thin_rlp[..] != &empty_rlp[..] {
|
||||
bloom.set(hash.as_slice());
|
||||
}
|
||||
try!(account_trie.insert(&hash, &thin_rlp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ pub struct CommonParams {
|
||||
/// Maximum size of extra data.
|
||||
pub maximum_extra_data_size: usize,
|
||||
/// Network id.
|
||||
pub network_id: U256,
|
||||
pub network_id: usize,
|
||||
/// Main subprotocol name.
|
||||
pub subprotocol_name: String,
|
||||
/// Minimum gas limit.
|
||||
pub min_gas_limit: U256,
|
||||
/// Fork block to check.
|
||||
@@ -50,6 +52,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(),
|
||||
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 },
|
||||
}
|
||||
@@ -155,7 +158,7 @@ impl Spec {
|
||||
pub fn nodes(&self) -> &[String] { &self.nodes }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn network_id(&self) -> U256 { self.params.network_id }
|
||||
pub fn network_id(&self) -> usize { self.params.network_id }
|
||||
|
||||
/// Get the configured network fork block.
|
||||
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { self.params.fork_block }
|
||||
@@ -240,7 +243,7 @@ impl Spec {
|
||||
}
|
||||
}
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
db.note_account_bloom(address);
|
||||
db.note_non_null_account(address);
|
||||
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address));
|
||||
}
|
||||
assert!(db.as_hashdb().contains(&self.state_root()));
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::{RefCell, RefMut};
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use common::*;
|
||||
use engines::Engine;
|
||||
use executive::{Executive, TransactOptions};
|
||||
@@ -38,42 +38,93 @@ pub struct ApplyOutcome {
|
||||
/// Result type for the execution ("application") of a transaction.
|
||||
pub type ApplyResult = Result<ApplyOutcome, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AccountEntry {
|
||||
/// Contains account data.
|
||||
Cached(Account),
|
||||
/// Account has been deleted.
|
||||
Killed,
|
||||
/// Account does not exist.
|
||||
Missing,
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||
/// Account modification state. Used to check if the account was
|
||||
/// Modified in between commits and overall.
|
||||
enum AccountState {
|
||||
/// Account was loaded from disk and never modified in this state object.
|
||||
CleanFresh,
|
||||
/// Account was loaded from the global cache and never modified.
|
||||
CleanCached,
|
||||
/// Account has been modified and is not committed to the trie yet.
|
||||
/// This is set if any of the account data is changed, including
|
||||
/// storage and code.
|
||||
Dirty,
|
||||
/// Account was modified and committed to the trie.
|
||||
Committed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// In-memory copy of the account data. Holds the optional account
|
||||
/// and the modification status.
|
||||
/// Account entry can contain existing (`Some`) or non-existing
|
||||
/// account (`None`)
|
||||
struct AccountEntry {
|
||||
account: Option<Account>,
|
||||
state: AccountState,
|
||||
}
|
||||
|
||||
// Account cache item. Contains account data and
|
||||
// modification state
|
||||
impl AccountEntry {
|
||||
fn is_dirty(&self) -> bool {
|
||||
match *self {
|
||||
AccountEntry::Cached(ref a) => a.is_dirty(),
|
||||
AccountEntry::Killed => true,
|
||||
AccountEntry::Missing => false,
|
||||
}
|
||||
self.state == AccountState::Dirty
|
||||
}
|
||||
|
||||
/// Clone dirty data into new `AccountEntry`.
|
||||
/// Clone dirty data into new `AccountEntry`. This includes
|
||||
/// basic account data and modified storage keys.
|
||||
/// Returns None if clean.
|
||||
fn clone_dirty(&self) -> Option<AccountEntry> {
|
||||
match *self {
|
||||
AccountEntry::Cached(ref acc) if acc.is_dirty() => Some(AccountEntry::Cached(acc.clone_dirty())),
|
||||
AccountEntry::Killed => Some(AccountEntry::Killed),
|
||||
_ => None,
|
||||
fn clone_if_dirty(&self) -> Option<AccountEntry> {
|
||||
match self.is_dirty() {
|
||||
true => Some(self.clone_dirty()),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone account entry data that needs to be saved in the snapshot.
|
||||
/// This includes basic account information and all locally cached storage keys
|
||||
fn clone_for_snapshot(&self) -> AccountEntry {
|
||||
match *self {
|
||||
AccountEntry::Cached(ref acc) => AccountEntry::Cached(acc.clone_all()),
|
||||
AccountEntry::Killed => AccountEntry::Killed,
|
||||
AccountEntry::Missing => AccountEntry::Missing,
|
||||
/// Clone dirty data into new `AccountEntry`. This includes
|
||||
/// basic account data and modified storage keys.
|
||||
fn clone_dirty(&self) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: self.account.as_ref().map(Account::clone_dirty),
|
||||
state: self.state,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as dirty.
|
||||
fn new_dirty(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::Dirty,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as clean.
|
||||
fn new_clean(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::CleanFresh,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as clean and cached.
|
||||
fn new_clean_cached(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::CleanCached,
|
||||
}
|
||||
}
|
||||
|
||||
// Replace data with another entry but preserve storage cache.
|
||||
fn overwrite_with(&mut self, other: AccountEntry) {
|
||||
self.state = other.state;
|
||||
match other.account {
|
||||
Some(acc) => match self.account {
|
||||
Some(ref mut ours) => {
|
||||
ours.overwrite_with(acc);
|
||||
},
|
||||
None => {},
|
||||
},
|
||||
None => self.account = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +137,9 @@ impl AccountEntry {
|
||||
/// locally from previous commits. Global cache reflects the database
|
||||
/// state and never contains any changes.
|
||||
///
|
||||
/// Cache items contains account data, or the flag that account does not exist
|
||||
/// and modification state (see `AccountState`)
|
||||
///
|
||||
/// Account data can be in the following cache states:
|
||||
/// * In global but not local - something that was queried from the database,
|
||||
/// but never modified
|
||||
@@ -99,12 +153,32 @@ impl AccountEntry {
|
||||
/// then global state cache. If data is not found in any of the caches
|
||||
/// it is loaded from the DB to the local cache.
|
||||
///
|
||||
/// **** IMPORTANT *************************************************************
|
||||
/// All the modifications to the account data must set the `Dirty` state in the
|
||||
/// `AccountEntry`. This is done in `require` and `require_or_from`. So just
|
||||
/// use that.
|
||||
/// ****************************************************************************
|
||||
///
|
||||
/// Upon destruction all the local cache data merged into the global cache.
|
||||
/// The merge might be rejected if current state is non-canonical.
|
||||
///
|
||||
/// State snapshotting.
|
||||
///
|
||||
/// A new snapshot can be created with `snapshot()`. Snapshots can be
|
||||
/// created in a hierarchy.
|
||||
/// When a snapshot is active all changes are applied directly into
|
||||
/// `cache` and the original value is copied into an active snapshot.
|
||||
/// Reverting a snapshot with `revert_to_snapshot` involves copying
|
||||
/// original values from the latest snapshot back into `cache`. The code
|
||||
/// takes care not to overwrite cached storage while doing that.
|
||||
/// Snapshot can be discateded with `discard_snapshot`. All of the orignal
|
||||
/// backed-up values are moved into a parent snapshot (if any).
|
||||
///
|
||||
pub struct State {
|
||||
db: StateDB,
|
||||
root: H256,
|
||||
cache: RefCell<HashMap<Address, AccountEntry>>,
|
||||
// The original account is preserved in
|
||||
snapshots: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
|
||||
account_start_nonce: U256,
|
||||
trie_factory: TrieFactory,
|
||||
@@ -117,6 +191,13 @@ enum RequireCache {
|
||||
Code,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum CleanupMode<'a> {
|
||||
ForceCreate,
|
||||
NoEmpty,
|
||||
KillEmpty(&'a mut HashSet<Address>),
|
||||
}
|
||||
|
||||
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
|
||||
Therefore creating a SecTrieDB with this state's root will not fail.";
|
||||
|
||||
@@ -158,35 +239,48 @@ impl State {
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Create a recoverable snaphot of this state
|
||||
/// Create a recoverable snaphot of this state.
|
||||
pub fn snapshot(&mut self) {
|
||||
self.snapshots.borrow_mut().push(HashMap::new());
|
||||
self.snapshots.get_mut().push(HashMap::new());
|
||||
}
|
||||
|
||||
/// Merge last snapshot with previous
|
||||
pub fn clear_snapshot(&mut self) {
|
||||
/// Merge last snapshot with previous.
|
||||
pub fn discard_snapshot(&mut self) {
|
||||
// merge with previous snapshot
|
||||
let last = self.snapshots.borrow_mut().pop();
|
||||
let last = self.snapshots.get_mut().pop();
|
||||
if let Some(mut snapshot) = last {
|
||||
if let Some(ref mut prev) = self.snapshots.borrow_mut().last_mut() {
|
||||
for (k, v) in snapshot.drain() {
|
||||
prev.entry(k).or_insert(v);
|
||||
if let Some(ref mut prev) = self.snapshots.get_mut().last_mut() {
|
||||
if prev.is_empty() {
|
||||
**prev = snapshot;
|
||||
} else {
|
||||
for (k, v) in snapshot.drain() {
|
||||
prev.entry(k).or_insert(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Revert to snapshot
|
||||
pub fn revert_snapshot(&mut self) {
|
||||
if let Some(mut snapshot) = self.snapshots.borrow_mut().pop() {
|
||||
/// Revert to the last snapshot and discard it.
|
||||
pub fn revert_to_snapshot(&mut self) {
|
||||
if let Some(mut snapshot) = self.snapshots.get_mut().pop() {
|
||||
for (k, v) in snapshot.drain() {
|
||||
match v {
|
||||
Some(v) => {
|
||||
self.cache.borrow_mut().insert(k, v);
|
||||
match self.cache.get_mut().entry(k) {
|
||||
Entry::Occupied(mut e) => {
|
||||
// Merge snapshotted changes back into the main account
|
||||
// storage preserving the cache.
|
||||
e.get_mut().overwrite_with(v);
|
||||
},
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(v);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
match self.cache.borrow_mut().entry(k) {
|
||||
::std::collections::hash_map::Entry::Occupied(e) => {
|
||||
match self.cache.get_mut().entry(k) {
|
||||
Entry::Occupied(e) => {
|
||||
if e.get().is_dirty() {
|
||||
e.remove();
|
||||
}
|
||||
@@ -200,10 +294,17 @@ impl State {
|
||||
}
|
||||
|
||||
fn insert_cache(&self, address: &Address, account: AccountEntry) {
|
||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||
if !snapshot.contains_key(address) {
|
||||
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
|
||||
return;
|
||||
// Dirty account which is not in the cache means this is a new account.
|
||||
// It goes directly into the snapshot as there's nothing to rever to.
|
||||
//
|
||||
// In all other cases account is read as clean first, and after that made
|
||||
// dirty in and added to the snapshot with `note_cache`.
|
||||
if account.is_dirty() {
|
||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||
if !snapshot.contains_key(address) {
|
||||
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cache.borrow_mut().insert(address.clone(), account);
|
||||
@@ -212,14 +313,14 @@ impl State {
|
||||
fn note_cache(&self, address: &Address) {
|
||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||
if !snapshot.contains_key(address) {
|
||||
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_for_snapshot));
|
||||
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_dirty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy the current object and return root and database.
|
||||
pub fn drop(mut self) -> (H256, StateDB) {
|
||||
self.commit_cache();
|
||||
self.update_shared_cache();
|
||||
(self.root, self.db)
|
||||
}
|
||||
|
||||
@@ -230,29 +331,36 @@ impl State {
|
||||
|
||||
/// Create a new contract at address `contract`. If there is already an account at the address
|
||||
/// it will have its code reset, ready for `init_code()`.
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
|
||||
self.insert_cache(contract, AccountEntry::Cached(Account::new_contract(balance, self.account_start_nonce)));
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) {
|
||||
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce + nonce_offset))));
|
||||
}
|
||||
|
||||
/// Remove an existing account.
|
||||
pub fn kill_account(&mut self, account: &Address) {
|
||||
self.insert_cache(account, AccountEntry::Killed);
|
||||
self.insert_cache(account, AccountEntry::new_dirty(None));
|
||||
}
|
||||
|
||||
/// Determine whether an account exists.
|
||||
pub fn exists(&self, a: &Address) -> bool {
|
||||
self.ensure_cached(a, RequireCache::None, |a| a.is_some())
|
||||
// Bloom filter does not contain empty accounts, so it is important here to
|
||||
// check if account exists in the database directly before EIP-161 is in effect.
|
||||
self.ensure_cached(a, RequireCache::None, false, |a| a.is_some())
|
||||
}
|
||||
|
||||
/// Determine whether an account exists and if not empty.
|
||||
pub fn exists_and_not_null(&self, a: &Address) -> bool {
|
||||
self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null()))
|
||||
}
|
||||
|
||||
/// Get the balance of account `a`.
|
||||
pub fn balance(&self, a: &Address) -> U256 {
|
||||
self.ensure_cached(a, RequireCache::None,
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
|
||||
}
|
||||
|
||||
/// Get the nonce of account `a`.
|
||||
pub fn nonce(&self, a: &Address) -> U256 {
|
||||
self.ensure_cached(a, RequireCache::None,
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
|
||||
}
|
||||
|
||||
@@ -271,8 +379,8 @@ impl State {
|
||||
let local_cache = self.cache.borrow_mut();
|
||||
let mut local_account = None;
|
||||
if let Some(maybe_acc) = local_cache.get(address) {
|
||||
match *maybe_acc {
|
||||
AccountEntry::Cached(ref account) => {
|
||||
match maybe_acc.account {
|
||||
Some(ref account) => {
|
||||
if let Some(value) = account.cached_storage_at(key) {
|
||||
return value;
|
||||
} else {
|
||||
@@ -288,61 +396,72 @@ impl State {
|
||||
return result;
|
||||
}
|
||||
if let Some(ref mut acc) = local_account {
|
||||
if let AccountEntry::Cached(ref account) = **acc {
|
||||
if let Some(ref account) = acc.account {
|
||||
return account.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), account.address_hash(address)), key)
|
||||
} else {
|
||||
return H256::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
// account is not found in the global cache, get from the DB and insert into local
|
||||
if !self.db.check_account_bloom(address) { return H256::zero() }
|
||||
|
||||
// check bloom before any requests to trie
|
||||
if !self.db.check_non_null_bloom(address) { return H256::zero() }
|
||||
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(address) {
|
||||
Ok(acc) => acc.map(Account::from_rlp),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
let r = maybe_acc.as_ref().map_or(H256::new(), |a| a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key));
|
||||
match maybe_acc {
|
||||
Some(account) => self.insert_cache(address, AccountEntry::Cached(account)),
|
||||
None => self.insert_cache(address, AccountEntry::Missing),
|
||||
}
|
||||
self.insert_cache(address, AccountEntry::new_clean(maybe_acc));
|
||||
r
|
||||
}
|
||||
|
||||
/// Get accounts' code.
|
||||
pub fn code(&self, a: &Address) -> Option<Arc<Bytes>> {
|
||||
self.ensure_cached(a, RequireCache::Code,
|
||||
self.ensure_cached(a, RequireCache::Code, true,
|
||||
|a| a.as_ref().map_or(None, |a| a.code().clone()))
|
||||
}
|
||||
|
||||
pub fn code_hash(&self, a: &Address) -> H256 {
|
||||
self.ensure_cached(a, RequireCache::None,
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(SHA3_EMPTY, |a| a.code_hash()))
|
||||
}
|
||||
|
||||
/// Get accounts' code size.
|
||||
pub fn code_size(&self, a: &Address) -> Option<u64> {
|
||||
self.ensure_cached(a, RequireCache::CodeSize,
|
||||
self.ensure_cached(a, RequireCache::CodeSize, true,
|
||||
|a| a.as_ref().and_then(|a| a.code_size()))
|
||||
}
|
||||
|
||||
/// Add `incr` to the balance of account `a`.
|
||||
pub fn add_balance(&mut self, a: &Address, incr: &U256) {
|
||||
pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) {
|
||||
trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a));
|
||||
self.require(a, false).add_balance(incr);
|
||||
let is_value_transfer = !incr.is_zero();
|
||||
if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)) {
|
||||
self.require(a, false).add_balance(incr);
|
||||
} else {
|
||||
match cleanup_mode {
|
||||
CleanupMode::KillEmpty(set) => if !is_value_transfer && self.exists(a) && !self.exists_and_not_null(a) {
|
||||
set.insert(a.clone());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract `decr` from the balance of account `a`.
|
||||
pub fn sub_balance(&mut self, a: &Address, decr: &U256) {
|
||||
trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a));
|
||||
self.require(a, false).sub_balance(decr);
|
||||
if !decr.is_zero() || !self.exists(a) {
|
||||
self.require(a, false).sub_balance(decr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtracts `by` from the balance of `from` and adds it to that of `to`.
|
||||
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) {
|
||||
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, cleanup_mode: CleanupMode) {
|
||||
self.sub_balance(from, by);
|
||||
self.add_balance(to, by);
|
||||
self.add_balance(to, by, cleanup_mode);
|
||||
}
|
||||
|
||||
/// Increment the nonce of account `a` by 1.
|
||||
@@ -352,7 +471,9 @@ impl State {
|
||||
|
||||
/// Mutate storage of account `a` so that it is `value` for `key`.
|
||||
pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) {
|
||||
self.require(a, false).set_storage(key, value)
|
||||
if self.storage_at(a, &key) != value {
|
||||
self.require(a, false).set_storage(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise the code of account `a` so that it is `code`.
|
||||
@@ -392,32 +513,31 @@ impl State {
|
||||
accounts: &mut HashMap<Address, AccountEntry>
|
||||
) -> Result<(), Error> {
|
||||
// first, commit the sub trees.
|
||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match a {
|
||||
&mut&mut AccountEntry::Cached(ref mut account) if account.is_dirty() => {
|
||||
db.note_account_bloom(&address);
|
||||
let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), account.address_hash(address));
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
if let Some(ref mut account) = a.account {
|
||||
let addr_hash = account.address_hash(address);
|
||||
{
|
||||
let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), addr_hash);
|
||||
account.commit_storage(trie_factory, &mut account_db);
|
||||
account.commit_code(&mut account_db);
|
||||
}
|
||||
_ => {}
|
||||
if !account.is_empty() {
|
||||
db.note_non_null_account(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut trie = trie_factory.from_existing(db.as_hashdb_mut(), root).unwrap();
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match **a {
|
||||
AccountEntry::Cached(ref mut account) if account.is_dirty() => {
|
||||
account.set_clean();
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
a.state = AccountState::Committed;
|
||||
match a.account {
|
||||
Some(ref mut account) => {
|
||||
try!(trie.insert(address, &account.rlp()));
|
||||
},
|
||||
AccountEntry::Killed => {
|
||||
None => {
|
||||
try!(trie.remove(address));
|
||||
**a = AccountEntry::Missing;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,20 +545,12 @@ impl State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit_cache(&mut self) {
|
||||
/// Merge local cache into shared canonical state cache.
|
||||
fn update_shared_cache(&mut self) {
|
||||
let mut addresses = self.cache.borrow_mut();
|
||||
for (address, a) in addresses.drain() {
|
||||
match a {
|
||||
AccountEntry::Cached(account) => {
|
||||
if !account.is_dirty() {
|
||||
self.db.cache_account(address, Some(account));
|
||||
}
|
||||
},
|
||||
AccountEntry::Missing => {
|
||||
self.db.cache_account(address, None);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
trace!("Committing cache {:?} entries", addresses.len());
|
||||
for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) {
|
||||
self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,8 +571,7 @@ impl State {
|
||||
pub fn populate_from(&mut self, accounts: PodState) {
|
||||
assert!(self.snapshots.borrow().is_empty());
|
||||
for (add, acc) in accounts.drain().into_iter() {
|
||||
self.db.note_account_bloom(&add);
|
||||
self.cache.borrow_mut().insert(add, AccountEntry::Cached(Account::from_pod(acc)));
|
||||
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +581,7 @@ impl State {
|
||||
// TODO: handle database rather than just the cache.
|
||||
// will need fat db.
|
||||
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
||||
if let AccountEntry::Cached(ref acc) = *opt {
|
||||
if let Some(ref acc) = opt.account {
|
||||
m.insert(add.clone(), PodAccount::from_account(acc));
|
||||
}
|
||||
m
|
||||
@@ -478,14 +589,14 @@ impl State {
|
||||
}
|
||||
|
||||
fn query_pod(&mut self, query: &PodState) {
|
||||
for (ref address, ref pod_account) in query.get() {
|
||||
self.ensure_cached(address, RequireCache::Code, |a| {
|
||||
if a.is_some() {
|
||||
for key in pod_account.storage.keys() {
|
||||
self.storage_at(address, key);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (address, pod_account) in query.get().into_iter()
|
||||
.filter(|&(ref a, _)| self.ensure_cached(a, RequireCache::Code, true, |a| a.is_some()))
|
||||
{
|
||||
// needs to be split into two parts for the refcell code here
|
||||
// to work.
|
||||
for key in pod_account.storage.keys() {
|
||||
self.storage_at(address, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,11 +626,11 @@ impl State {
|
||||
/// Check caches for required data
|
||||
/// First searches for account in the local, then the shared cache.
|
||||
/// Populates local cache if nothing found.
|
||||
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, f: F) -> U
|
||||
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_bloom: bool, f: F) -> U
|
||||
where F: Fn(Option<&Account>) -> U {
|
||||
// check local cache first
|
||||
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
||||
if let AccountEntry::Cached(ref mut account) = **maybe_acc {
|
||||
if let Some(ref mut account) = maybe_acc.account {
|
||||
Self::update_account_cache(require, account, a, self.db.as_hashdb());
|
||||
return f(Some(account));
|
||||
}
|
||||
@@ -535,8 +646,10 @@ impl State {
|
||||
match result {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
// first check bloom if it is not in database for sure
|
||||
if check_bloom && !self.db.check_non_null_bloom(a) { return f(None); }
|
||||
|
||||
// not found in the global cache, get from the DB and insert into local
|
||||
if !self.db.check_account_bloom(a) { return f(None); }
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let mut maybe_acc = match db.get(a) {
|
||||
Ok(acc) => acc.map(Account::from_rlp),
|
||||
@@ -546,10 +659,7 @@ impl State {
|
||||
Self::update_account_cache(require, account, a, self.db.as_hashdb());
|
||||
}
|
||||
let r = f(maybe_acc.as_ref());
|
||||
match maybe_acc {
|
||||
Some(account) => self.insert_cache(a, AccountEntry::Cached(account)),
|
||||
None => self.insert_cache(a, AccountEntry::Missing),
|
||||
}
|
||||
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
|
||||
r
|
||||
}
|
||||
}
|
||||
@@ -568,36 +678,37 @@ impl State {
|
||||
let contains_key = self.cache.borrow().contains_key(a);
|
||||
if !contains_key {
|
||||
match self.db.get_cached_account(a) {
|
||||
Some(Some(acc)) => self.insert_cache(a, AccountEntry::Cached(acc)),
|
||||
Some(None) => self.insert_cache(a, AccountEntry::Missing),
|
||||
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
|
||||
None => {
|
||||
let maybe_acc = if self.db.check_account_bloom(a) {
|
||||
let maybe_acc = if self.db.check_non_null_bloom(a) {
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(a) {
|
||||
Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)),
|
||||
Ok(None) => AccountEntry::Missing,
|
||||
match db.get(a) {
|
||||
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
|
||||
Ok(None) => AccountEntry::new_clean(None),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
maybe_acc
|
||||
}
|
||||
}
|
||||
else {
|
||||
AccountEntry::Missing
|
||||
AccountEntry::new_clean(None)
|
||||
};
|
||||
self.insert_cache(a, maybe_acc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.note_cache(a);
|
||||
}
|
||||
|
||||
match self.cache.borrow_mut().get_mut(a).unwrap() {
|
||||
&mut AccountEntry::Cached(ref mut acc) => not_default(acc),
|
||||
slot => *slot = AccountEntry::Cached(default()),
|
||||
}
|
||||
self.note_cache(a);
|
||||
|
||||
match &mut self.cache.borrow_mut().get_mut(a).unwrap().account {
|
||||
&mut Some(ref mut acc) => not_default(acc),
|
||||
slot => *slot = Some(default()),
|
||||
}
|
||||
|
||||
// at this point the account is guaranteed to be in the cache.
|
||||
RefMut::map(self.cache.borrow_mut(), |c| {
|
||||
match c.get_mut(a).unwrap() {
|
||||
&mut AccountEntry::Cached(ref mut account) => {
|
||||
let mut entry = c.get_mut(a).unwrap();
|
||||
// set the dirty flag after changing account data.
|
||||
entry.state = AccountState::Dirty;
|
||||
match entry.account {
|
||||
Some(ref mut account) => {
|
||||
if require_code {
|
||||
let addr_hash = account.address_hash(a);
|
||||
account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash));
|
||||
@@ -621,7 +732,7 @@ impl Clone for State {
|
||||
let cache = {
|
||||
let mut cache: HashMap<Address, AccountEntry> = HashMap::new();
|
||||
for (key, val) in self.cache.borrow().iter() {
|
||||
if let Some(entry) = val.clone_dirty() {
|
||||
if let Some(entry) = val.clone_if_dirty() {
|
||||
cache.insert(key.clone(), entry);
|
||||
}
|
||||
}
|
||||
@@ -673,9 +784,9 @@ fn should_apply_create_transaction() {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -734,9 +845,9 @@ fn should_trace_failed_create_transaction() {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: FromHex::from_hex("5b600056").unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -772,10 +883,10 @@ fn should_trace_call_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -816,9 +927,9 @@ fn should_trace_basic_call_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -859,7 +970,7 @@ fn should_trace_call_transaction_to_builtin() {
|
||||
action: Action::Call(0x1.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||
@@ -902,7 +1013,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
@@ -946,7 +1057,7 @@ fn should_not_trace_callcode() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
@@ -1009,7 +1120,7 @@ fn should_not_trace_delegatecall() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
@@ -1069,10 +1180,10 @@ fn should_trace_failed_call_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1110,11 +1221,11 @@ fn should_trace_call_with_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
|
||||
@@ -1171,10 +1282,10 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1227,10 +1338,10 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1271,11 +1382,11 @@ fn should_trace_failed_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],//600480600b6000396000f35b600056
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1328,12 +1439,12 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1404,12 +1515,12 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],//600480600b6000396000f35b600056
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
|
||||
@@ -1478,11 +1589,11 @@ fn should_trace_suicide() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
|
||||
state.add_balance(&0xa.into(), &50.into());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &100.into());
|
||||
state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty);
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &100.into(), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1554,7 +1665,7 @@ fn get_from_database() {
|
||||
let (root, db) = {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
state.inc_nonce(&a);
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.drop()
|
||||
@@ -1571,14 +1682,49 @@ fn remove() {
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
assert_eq!(state.exists(&a), false);
|
||||
assert_eq!(state.exists_and_not_null(&a), false);
|
||||
state.inc_nonce(&a);
|
||||
assert_eq!(state.exists(&a), true);
|
||||
assert_eq!(state.exists_and_not_null(&a), true);
|
||||
assert_eq!(state.nonce(&a), U256::from(1u64));
|
||||
state.kill_account(&a);
|
||||
assert_eq!(state.exists(&a), false);
|
||||
assert_eq!(state.exists_and_not_null(&a), false);
|
||||
assert_eq!(state.nonce(&a), U256::from(0u64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_account_is_not_created() {
|
||||
let a = Address::zero();
|
||||
let path = RandomTempPath::new();
|
||||
let db = get_temp_state_db_in(path.as_path());
|
||||
let (root, db) = {
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty); // create an empty account
|
||||
state.commit().unwrap();
|
||||
state.drop()
|
||||
};
|
||||
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
|
||||
assert!(!state.exists(&a));
|
||||
assert!(!state.exists_and_not_null(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_account_exists_when_creation_forced() {
|
||||
let a = Address::zero();
|
||||
let path = RandomTempPath::new();
|
||||
let db = get_temp_state_db_in(path.as_path());
|
||||
let (root, db) = {
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate); // create an empty account
|
||||
state.commit().unwrap();
|
||||
state.drop()
|
||||
};
|
||||
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
|
||||
assert!(state.exists(&a));
|
||||
assert!(!state.exists_and_not_null(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_from_database() {
|
||||
let a = Address::zero();
|
||||
@@ -1614,7 +1760,7 @@ fn alter_balance() {
|
||||
let mut state = state_result.reference_mut();
|
||||
let a = Address::zero();
|
||||
let b = address_from_u64(1u64);
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
@@ -1622,7 +1768,7 @@ fn alter_balance() {
|
||||
assert_eq!(state.balance(&a), U256::from(27u64));
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.balance(&a), U256::from(27u64));
|
||||
state.transfer_balance(&a, &b, &U256::from(18u64));
|
||||
state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(9u64));
|
||||
assert_eq!(state.balance(&b), U256::from(18u64));
|
||||
state.commit().unwrap();
|
||||
@@ -1675,14 +1821,14 @@ fn snapshot_basic() {
|
||||
let mut state = state_result.reference_mut();
|
||||
let a = Address::zero();
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.clear_snapshot();
|
||||
state.discard_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(1u64));
|
||||
state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(70u64));
|
||||
state.revert_snapshot();
|
||||
state.revert_to_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
}
|
||||
|
||||
@@ -1693,11 +1839,11 @@ fn snapshot_nested() {
|
||||
let a = Address::zero();
|
||||
state.snapshot();
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.clear_snapshot();
|
||||
state.discard_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.revert_snapshot();
|
||||
state.revert_to_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(0));
|
||||
}
|
||||
|
||||
@@ -1709,4 +1855,20 @@ fn create_empty() {
|
||||
assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_panic_on_state_diff_with_storage() {
|
||||
let state = get_temp_state();
|
||||
let mut state = state.reference().clone();
|
||||
|
||||
let a: Address = 0xa.into();
|
||||
state.init_code(&a, b"abcdefg".to_vec());
|
||||
state.add_balance(&a, &256.into(), CleanupMode::NoEmpty);
|
||||
state.set_storage(&a, 0xb.into(), 0xc.into());
|
||||
|
||||
let mut new_state = state.clone();
|
||||
new_state.set_storage(&a, 0xb.into(), 0xd.into());
|
||||
|
||||
new_state.diff_from(state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,22 +14,53 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{VecDeque, HashSet};
|
||||
use lru_cache::LruCache;
|
||||
use util::journaldb::JournalDB;
|
||||
use util::hash::{H256};
|
||||
use util::hashdb::HashDB;
|
||||
use util::{Arc, Address, DBTransaction, UtilError, Mutex, Hashable, BytesConvertable};
|
||||
use account::Account;
|
||||
use header::BlockNumber;
|
||||
use util::{Arc, Address, Database, DBTransaction, UtilError, Mutex, Hashable, BytesConvertable};
|
||||
use bloomfilter::{Bloom, BloomJournal};
|
||||
use util::Database;
|
||||
use client::DB_COL_ACCOUNT_BLOOM;
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
const STATE_CACHE_ITEMS: usize = 65536;
|
||||
const STATE_CACHE_ITEMS: usize = 256000;
|
||||
const STATE_CACHE_BLOCKS: usize = 12;
|
||||
|
||||
/// Shared canonical state cache.
|
||||
struct AccountCache {
|
||||
/// DB Account cache. `None` indicates that account is known to be missing.
|
||||
accounts: LruCache<Address, Option<Account>>,
|
||||
/// Accounts changed in recently committed blocks. Ordered by block number.
|
||||
modifications: VecDeque<BlockChanges>,
|
||||
}
|
||||
|
||||
/// Buffered account cache item.
|
||||
struct CacheQueueItem {
|
||||
/// Account address.
|
||||
address: Address,
|
||||
/// Acccount data or `None` if account does not exist.
|
||||
account: Option<Account>,
|
||||
/// Indicates that the account was modified before being
|
||||
/// added to the cache.
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Accumulates a list of accounts changed in a block.
|
||||
struct BlockChanges {
|
||||
/// Block number.
|
||||
number: BlockNumber,
|
||||
/// Block hash.
|
||||
hash: H256,
|
||||
/// Parent block hash.
|
||||
parent: H256,
|
||||
/// A set of modified account addresses.
|
||||
accounts: HashSet<Address>,
|
||||
/// Block is part of the canonical chain.
|
||||
is_canon: bool,
|
||||
}
|
||||
|
||||
/// State database abstraction.
|
||||
@@ -39,11 +70,21 @@ struct AccountCache {
|
||||
/// on commit.
|
||||
/// For non-canonical clones cache is cleared on commit.
|
||||
pub struct StateDB {
|
||||
/// Backing database.
|
||||
db: Box<JournalDB>,
|
||||
/// Shared canonical state cache.
|
||||
account_cache: Arc<Mutex<AccountCache>>,
|
||||
cache_overlay: Vec<(Address, Option<Account>)>,
|
||||
is_canon: bool,
|
||||
/// Local cache buffer.
|
||||
cache_buffer: Vec<CacheQueueItem>,
|
||||
/// Shared account bloom. Does not handle chain reorganizations.
|
||||
account_bloom: Arc<Mutex<Bloom>>,
|
||||
/// Hash of the block on top of which this instance was created or
|
||||
/// `None` if cache is disabled
|
||||
parent_hash: Option<H256>,
|
||||
/// Hash of the committing block or `None` if not committed yet.
|
||||
commit_hash: Option<H256>,
|
||||
/// Number of the committing block or `None` if not committed yet.
|
||||
commit_number: Option<BlockNumber>,
|
||||
}
|
||||
|
||||
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;
|
||||
@@ -52,6 +93,8 @@ pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;
|
||||
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
|
||||
|
||||
impl StateDB {
|
||||
/// Loads accounts bloom from the database
|
||||
/// This bloom is used to handle request for the non-existant account fast
|
||||
pub fn load_bloom(db: &Database) -> Bloom {
|
||||
let hash_count_entry = db.get(DB_COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
|
||||
.expect("Low-level database error");
|
||||
@@ -82,20 +125,25 @@ impl StateDB {
|
||||
let bloom = Self::load_bloom(db.backing());
|
||||
StateDB {
|
||||
db: db,
|
||||
account_cache: Arc::new(Mutex::new(AccountCache { accounts: LruCache::new(STATE_CACHE_ITEMS) })),
|
||||
cache_overlay: Vec::new(),
|
||||
is_canon: false,
|
||||
account_cache: Arc::new(Mutex::new(AccountCache {
|
||||
accounts: LruCache::new(STATE_CACHE_ITEMS),
|
||||
modifications: VecDeque::new(),
|
||||
})),
|
||||
cache_buffer: Vec::new(),
|
||||
account_bloom: Arc::new(Mutex::new(bloom)),
|
||||
parent_hash: None,
|
||||
commit_hash: None,
|
||||
commit_number: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_account_bloom(&self, address: &Address) -> bool {
|
||||
pub fn check_non_null_bloom(&self, address: &Address) -> bool {
|
||||
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
|
||||
let bloom = self.account_bloom.lock();
|
||||
bloom.check(address.sha3().as_slice())
|
||||
}
|
||||
|
||||
pub fn note_account_bloom(&self, address: &Address) {
|
||||
pub fn note_non_null_account(&self, address: &Address) {
|
||||
trace!(target: "account_bloom", "Note account bloom: {:?}", address);
|
||||
let mut bloom = self.account_bloom.lock();
|
||||
bloom.set(address.sha3().as_slice());
|
||||
@@ -124,14 +172,106 @@ impl StateDB {
|
||||
}
|
||||
|
||||
let records = try!(self.db.commit(batch, now, id, end));
|
||||
if self.is_canon {
|
||||
self.commit_cache();
|
||||
} else {
|
||||
self.clear_cache();
|
||||
}
|
||||
self.commit_hash = Some(id.clone());
|
||||
self.commit_number = Some(now);
|
||||
Ok(records)
|
||||
}
|
||||
|
||||
/// Apply buffered cache changes and synchronize canonical
|
||||
/// state cache with the best block state.
|
||||
/// This function updates the cache by removing entries that are
|
||||
/// invalidated by chain reorganization. `update_cache` should be
|
||||
/// called after the block has been commited and the blockchain
|
||||
/// route has ben calculated.
|
||||
pub fn sync_cache(&mut self, enacted: &[H256], retracted: &[H256], is_best: bool) {
|
||||
trace!("sync_cache id = (#{:?}, {:?}), parent={:?}, best={}", self.commit_number, self.commit_hash, self.parent_hash, is_best);
|
||||
let mut cache = self.account_cache.lock();
|
||||
let mut cache = &mut *cache;
|
||||
|
||||
// Clean changes from re-enacted and retracted blocks
|
||||
let mut clear = false;
|
||||
for block in enacted.iter().filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) {
|
||||
clear = clear || {
|
||||
if let Some(ref mut m) = cache.modifications.iter_mut().find(|ref m| &m.hash == block) {
|
||||
trace!("Reverting enacted block {:?}", block);
|
||||
m.is_canon = true;
|
||||
for a in &m.accounts {
|
||||
trace!("Reverting enacted address {:?}", a);
|
||||
cache.accounts.remove(a);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for block in retracted {
|
||||
clear = clear || {
|
||||
if let Some(ref mut m) = cache.modifications.iter_mut().find(|ref m| &m.hash == block) {
|
||||
trace!("Retracting block {:?}", block);
|
||||
m.is_canon = false;
|
||||
for a in &m.accounts {
|
||||
trace!("Retracted address {:?}", a);
|
||||
cache.accounts.remove(a);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
if clear {
|
||||
// We don't know anything about the block; clear everything
|
||||
trace!("Wiping cache");
|
||||
cache.accounts.clear();
|
||||
cache.modifications.clear();
|
||||
}
|
||||
|
||||
// Apply cache changes only if committing on top of the latest canonical state
|
||||
// blocks are ordered by number and only one block with a given number is marked as canonical
|
||||
// (contributed to canonical state cache)
|
||||
if let (Some(ref number), Some(ref hash), Some(ref parent)) = (self.commit_number, self.commit_hash, self.parent_hash) {
|
||||
if cache.modifications.len() == STATE_CACHE_BLOCKS {
|
||||
cache.modifications.pop_back();
|
||||
}
|
||||
let mut modifications = HashSet::new();
|
||||
trace!("committing {} cache entries", self.cache_buffer.len());
|
||||
for account in self.cache_buffer.drain(..) {
|
||||
if account.modified {
|
||||
modifications.insert(account.address.clone());
|
||||
}
|
||||
if is_best {
|
||||
if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&account.address) {
|
||||
if let Some(new) = account.account {
|
||||
if account.modified {
|
||||
existing.overwrite_with(new);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cache.accounts.insert(account.address, account.account);
|
||||
}
|
||||
}
|
||||
|
||||
// Save modified accounts. These are ordered by the block number.
|
||||
let block_changes = BlockChanges {
|
||||
accounts: modifications,
|
||||
number: *number,
|
||||
hash: hash.clone(),
|
||||
is_canon: is_best,
|
||||
parent: parent.clone(),
|
||||
};
|
||||
let insert_at = cache.modifications.iter().enumerate().find(|&(_, ref m)| m.number < *number).map(|(i, _)| i);
|
||||
trace!("inserting modifications at {:?}", insert_at);
|
||||
if let Some(insert_at) = insert_at {
|
||||
cache.modifications.insert(insert_at, block_changes);
|
||||
} else {
|
||||
cache.modifications.push_back(block_changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an interface to HashDB.
|
||||
pub fn as_hashdb(&self) -> &HashDB {
|
||||
self.db.as_hashdb()
|
||||
@@ -147,20 +287,24 @@ impl StateDB {
|
||||
StateDB {
|
||||
db: self.db.boxed_clone(),
|
||||
account_cache: self.account_cache.clone(),
|
||||
cache_overlay: Vec::new(),
|
||||
is_canon: false,
|
||||
cache_buffer: Vec::new(),
|
||||
account_bloom: self.account_bloom.clone(),
|
||||
parent_hash: None,
|
||||
commit_hash: None,
|
||||
commit_number: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the database for a canonical state.
|
||||
pub fn boxed_clone_canon(&self) -> StateDB {
|
||||
pub fn boxed_clone_canon(&self, parent: &H256) -> StateDB {
|
||||
StateDB {
|
||||
db: self.db.boxed_clone(),
|
||||
account_cache: self.account_cache.clone(),
|
||||
cache_overlay: Vec::new(),
|
||||
is_canon: true,
|
||||
cache_buffer: Vec::new(),
|
||||
account_bloom: self.account_bloom.clone(),
|
||||
parent_hash: Some(parent.clone()),
|
||||
commit_hash: None,
|
||||
commit_number: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,53 +323,152 @@ impl StateDB {
|
||||
&*self.db
|
||||
}
|
||||
|
||||
/// Enqueue cache change.
|
||||
pub fn cache_account(&mut self, addr: Address, data: Option<Account>) {
|
||||
self.cache_overlay.push((addr, data));
|
||||
/// Returns underlying `JournalDB`.
|
||||
pub fn journal_db_mut(&mut self) -> &mut JournalDB {
|
||||
&mut *self.db
|
||||
}
|
||||
|
||||
/// Apply pending cache changes.
|
||||
fn commit_cache(&mut self) {
|
||||
let mut cache = self.account_cache.lock();
|
||||
for (address, account) in self.cache_overlay.drain(..) {
|
||||
if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&address) {
|
||||
if let Some(new) = account {
|
||||
existing.merge_with(new);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cache.accounts.insert(address, account);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the cache.
|
||||
pub fn clear_cache(&mut self) {
|
||||
self.cache_overlay.clear();
|
||||
let mut cache = self.account_cache.lock();
|
||||
cache.accounts.clear();
|
||||
/// Add pending cache change.
|
||||
/// The change is queued to be applied in `commit`.
|
||||
pub fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool) {
|
||||
self.cache_buffer.push(CacheQueueItem {
|
||||
address: addr,
|
||||
account: data,
|
||||
modified: modified,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get basic copy of the cached account. Does not include storage.
|
||||
/// Returns 'None' if the state is non-canonical and cache is disabled
|
||||
/// or if the account is not cached.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
pub fn get_cached_account(&self, addr: &Address) -> Option<Option<Account>> {
|
||||
if !self.is_canon {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(addr, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
let mut cache = self.account_cache.lock();
|
||||
cache.accounts.get_mut(&addr).map(|a| a.as_ref().map(|a| a.clone_basic()))
|
||||
}
|
||||
|
||||
/// Get value from a cached account.
|
||||
/// Returns 'None' if the state is non-canonical and cache is disabled
|
||||
/// or if the account is not cached.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
pub fn get_cached<F, U>(&self, a: &Address, f: F) -> Option<U>
|
||||
where F: FnOnce(Option<&mut Account>) -> U {
|
||||
if !self.is_canon {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(a, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
let mut cache = self.account_cache.lock();
|
||||
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
||||
}
|
||||
|
||||
/// Check if the account can be returned from cache by matching current block parent hash against canonical
|
||||
/// state and filtering out account modified in later blocks.
|
||||
fn is_allowed(addr: &Address, parent_hash: &Option<H256>, modifications: &VecDeque<BlockChanges>) -> bool {
|
||||
let mut parent = match *parent_hash {
|
||||
None => {
|
||||
trace!("Cache lookup skipped for {:?}: no parent hash", addr);
|
||||
return false;
|
||||
}
|
||||
Some(ref parent) => parent,
|
||||
};
|
||||
if modifications.is_empty() {
|
||||
return true;
|
||||
}
|
||||
// Ignore all accounts modified in later blocks
|
||||
// Modifications contains block ordered by the number
|
||||
// We search for our parent in that list first and then for
|
||||
// all its parent until we hit the canonical block,
|
||||
// checking against all the intermediate modifications.
|
||||
let mut iter = modifications.iter();
|
||||
while let Some(ref m) = iter.next() {
|
||||
if &m.hash == parent {
|
||||
if m.is_canon {
|
||||
return true;
|
||||
}
|
||||
parent = &m.parent;
|
||||
}
|
||||
if m.accounts.contains(addr) {
|
||||
trace!("Cache lookup skipped for {:?}: modified in a later block", addr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
trace!("Cache lookup skipped for {:?}: parent hash is unknown", addr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use util::{U256, H256, FixedHash, Address, DBTransaction};
|
||||
use tests::helpers::*;
|
||||
use account::Account;
|
||||
use util::log::init_log;
|
||||
|
||||
#[test]
|
||||
fn state_db_smoke() {
|
||||
init_log();
|
||||
|
||||
let mut state_db_result = get_temp_state_db();
|
||||
let state_db = state_db_result.take();
|
||||
let root_parent = H256::random();
|
||||
let address = Address::random();
|
||||
let h0 = H256::random();
|
||||
let h1a = H256::random();
|
||||
let h1b = H256::random();
|
||||
let h2a = H256::random();
|
||||
let h2b = H256::random();
|
||||
let h3a = H256::random();
|
||||
let h3b = H256::random();
|
||||
let mut batch = DBTransaction::new(state_db.journal_db().backing());
|
||||
|
||||
// blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ]
|
||||
// balance [ 5 5 4 3 2 2 ]
|
||||
let mut s = state_db.boxed_clone_canon(&root_parent);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false);
|
||||
s.commit(&mut batch, 0, &h0, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h0);
|
||||
s.commit(&mut batch, 1, &h1a, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h0);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(3.into(), 0.into())), true);
|
||||
s.commit(&mut batch, 1, &h1b, None).unwrap();
|
||||
s.sync_cache(&[], &[], false);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h1b);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(4.into(), 0.into())), true);
|
||||
s.commit(&mut batch, 2, &h2b, None).unwrap();
|
||||
s.sync_cache(&[], &[], false);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h1a);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(5.into(), 0.into())), true);
|
||||
s.commit(&mut batch, 2, &h2a, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h2a);
|
||||
s.commit(&mut batch, 3, &h3a, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h3a);
|
||||
assert_eq!(s.get_cached_account(&address).unwrap().unwrap().balance(), &U256::from(5));
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h1a);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h2b);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h1b);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
|
||||
// reorg to 3b
|
||||
// blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ]
|
||||
let mut s = state_db.boxed_clone_canon(&h2b);
|
||||
s.commit(&mut batch, 3, &h3b, None).unwrap();
|
||||
s.sync_cache(&[h1b.clone(), h2b.clone(), h3b.clone()], &[h1a.clone(), h2a.clone(), h3a.clone()], true);
|
||||
let s = state_db.boxed_clone_canon(&h3a);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
use std::collections::HashSet;
|
||||
use util::{Address, U256};
|
||||
use log_entry::LogEntry;
|
||||
use evm::Schedule;
|
||||
use state::CleanupMode;
|
||||
|
||||
/// State changes which should be applied in finalize,
|
||||
/// after transaction is fully executed.
|
||||
@@ -26,6 +28,9 @@ pub struct Substate {
|
||||
/// Any accounts that have suicided.
|
||||
pub suicides: HashSet<Address>,
|
||||
|
||||
/// Any accounts that are tagged for garbage collection.
|
||||
pub garbage: HashSet<Address>,
|
||||
|
||||
/// Any logs.
|
||||
pub logs: Vec<LogEntry>,
|
||||
|
||||
@@ -45,10 +50,20 @@ impl Substate {
|
||||
/// Merge secondary substate `s` into self, accruing each element correspondingly.
|
||||
pub fn accrue(&mut self, s: Substate) {
|
||||
self.suicides.extend(s.suicides.into_iter());
|
||||
self.garbage.extend(s.garbage.into_iter());
|
||||
self.logs.extend(s.logs.into_iter());
|
||||
self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count;
|
||||
self.contracts_created.extend(s.contracts_created.into_iter());
|
||||
}
|
||||
|
||||
/// Get the cleanup mode object from this.
|
||||
pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode {
|
||||
match (schedule.no_empty, schedule.kill_empty) {
|
||||
(false, _) => CleanupMode::ForceCreate,
|
||||
(true, false) => CleanupMode::NoEmpty,
|
||||
(true, true) => CleanupMode::KillEmpty(&mut self.garbage),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
use io::IoChannel;
|
||||
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID};
|
||||
use state::CleanupMode;
|
||||
use ethereum;
|
||||
use block::IsBlock;
|
||||
use tests::helpers::*;
|
||||
use common::*;
|
||||
use devtools::*;
|
||||
use miner::Miner;
|
||||
use spec::Spec;
|
||||
|
||||
#[test]
|
||||
fn imports_from_empty() {
|
||||
@@ -151,7 +153,7 @@ fn can_handle_long_fork() {
|
||||
push_blocks_to_client(client, 49, 1201, 800);
|
||||
push_blocks_to_client(client, 53, 1201, 600);
|
||||
|
||||
for _ in 0..40 {
|
||||
for _ in 0..400 {
|
||||
client.import_verified_blocks();
|
||||
}
|
||||
assert_eq!(2000, client.chain_info().best_block_number);
|
||||
@@ -167,3 +169,26 @@ fn can_mine() {
|
||||
|
||||
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_history_size() {
|
||||
let dir = RandomTempPath::new();
|
||||
let test_spec = Spec::new_null();
|
||||
let mut config = ClientConfig::default();
|
||||
config.history = 2;
|
||||
let address = Address::random();
|
||||
{
|
||||
let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected()).unwrap();
|
||||
for _ in 0..20 {
|
||||
let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]);
|
||||
b.block_mut().fields_mut().state.add_balance(&address, &5.into(), CleanupMode::NoEmpty);
|
||||
b.block_mut().fields_mut().state.commit().unwrap();
|
||||
let b = b.close_and_lock().seal(&*test_spec.engine, vec![]).unwrap();
|
||||
client.import_sealed_block(b).unwrap(); // account change is in the journal overlay
|
||||
}
|
||||
}
|
||||
let mut config = ClientConfig::default();
|
||||
config.history = 10;
|
||||
let client = Client::new(config, &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected()).unwrap();
|
||||
assert_eq!(client.state().balance(&address), 100.into());
|
||||
}
|
||||
|
||||
@@ -32,19 +32,21 @@ use miner::Miner;
|
||||
pub enum ChainEra {
|
||||
Frontier,
|
||||
Homestead,
|
||||
DaoHardfork,
|
||||
Eip150,
|
||||
Eip161,
|
||||
TransitionTest,
|
||||
}
|
||||
|
||||
pub struct TestEngine {
|
||||
engine: Arc<Engine>,
|
||||
max_depth: usize
|
||||
max_depth: usize,
|
||||
}
|
||||
|
||||
impl TestEngine {
|
||||
pub fn new(max_depth: usize) -> TestEngine {
|
||||
TestEngine {
|
||||
engine: ethereum::new_frontier_test().engine,
|
||||
max_depth: max_depth
|
||||
max_depth: max_depth,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +182,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
||||
action: Action::Create,
|
||||
data: vec![],
|
||||
value: U256::zero(),
|
||||
}.sign(kp.secret()), None).unwrap();
|
||||
}.sign(kp.secret(), None), None).unwrap();
|
||||
n += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,4 +16,6 @@
|
||||
|
||||
pub mod helpers;
|
||||
mod client;
|
||||
|
||||
#[cfg(feature="ipc")]
|
||||
mod rpc;
|
||||
|
||||
@@ -268,16 +268,6 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
return;
|
||||
}
|
||||
|
||||
// at first, let's insert new block traces
|
||||
{
|
||||
let mut traces = self.traces.write();
|
||||
// it's important to use overwrite here,
|
||||
// cause this value might be queried by hash later
|
||||
batch.write_with_cache(DB_COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
||||
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
|
||||
self.note_used(CacheID::Trace(request.block_hash.clone()));
|
||||
}
|
||||
|
||||
// now let's rebuild the blooms
|
||||
{
|
||||
let range_start = request.block_number as Number + 1 - request.enacted.len();
|
||||
@@ -288,12 +278,25 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||
// all traces are expected to be found here. That's why `expect` has been used
|
||||
// instead of `filter_map`. If some traces haven't been found, it meens that
|
||||
// traces database is corrupted or incomplete.
|
||||
.map(|block_hash| self.traces(block_hash).expect("Traces database is incomplete."))
|
||||
.map(|block_traces| block_traces.bloom())
|
||||
.map(|block_hash| if block_hash == &request.block_hash {
|
||||
request.traces.bloom()
|
||||
} else {
|
||||
self.traces(block_hash).expect("Traces database is incomplete.").bloom()
|
||||
})
|
||||
.map(blooms::Bloom::from)
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
// insert new block traces into the cache and the database
|
||||
{
|
||||
let mut traces = self.traces.write();
|
||||
// it's important to use overwrite here,
|
||||
// cause this value might be queried by hash later
|
||||
batch.write_with_cache(DB_COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
||||
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
|
||||
self.note_used(CacheID::Trace(request.block_hash.clone()));
|
||||
}
|
||||
|
||||
let chain = BloomGroupChain::new(self.bloom_config, self);
|
||||
let trace_blooms = chain.replace(&replaced_range, enacted_blooms);
|
||||
let blooms_to_insert = trace_blooms.into_iter()
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Blockchain Filter.
|
||||
#[derive(Binary)]
|
||||
#[derive(Binary, Debug, PartialEq)]
|
||||
pub struct Filter {
|
||||
/// Blockchain will be searched from this block.
|
||||
pub from_block: BlockID,
|
||||
|
||||
@@ -20,7 +20,7 @@ use util::numbers::*;
|
||||
use std::ops::Deref;
|
||||
use util::rlp::*;
|
||||
use util::sha3::*;
|
||||
use util::{UtilError, CryptoError, Bytes, Signature, Secret, ec};
|
||||
use util::{UtilError, CryptoError, Bytes, Signature, Secret, Public, ec};
|
||||
use std::cell::*;
|
||||
use error::*;
|
||||
use evm::Schedule;
|
||||
@@ -75,8 +75,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) {
|
||||
s.begin_list(6);
|
||||
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 });
|
||||
s.append(&self.nonce);
|
||||
s.append(&self.gas_price);
|
||||
s.append(&self.gas);
|
||||
@@ -86,6 +86,11 @@ impl Transaction {
|
||||
};
|
||||
s.append(&self.value);
|
||||
s.append(&self.data);
|
||||
if let Some(n) = network_id {
|
||||
s.append(&n);
|
||||
s.append(&0u8);
|
||||
s.append(&0u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +107,7 @@ impl From<ethjson::state::Transaction> for SignedTransaction {
|
||||
},
|
||||
value: t.value.into(),
|
||||
data: t.data.into(),
|
||||
}.sign(&t.secret.into())
|
||||
}.sign(&t.secret.into(), None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,26 +137,27 @@ impl From<ethjson::transaction::Transaction> for SignedTransaction {
|
||||
|
||||
impl Transaction {
|
||||
/// The message hash of the transaction.
|
||||
pub fn hash(&self) -> H256 {
|
||||
pub fn hash(&self, network_id: Option<u8>) -> H256 {
|
||||
let mut stream = RlpStream::new();
|
||||
self.rlp_append_unsigned_transaction(&mut stream);
|
||||
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) -> SignedTransaction {
|
||||
let sig = ec::sign(secret, &self.hash()).unwrap();
|
||||
self.with_signature(sig)
|
||||
pub fn sign(self, secret: &Secret, network_id: Option<u8>) -> SignedTransaction {
|
||||
let sig = ec::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: H520) -> SignedTransaction {
|
||||
pub fn with_signature(self, sig: Signature, network_id: Option<u8>) -> SignedTransaction {
|
||||
let (r, s, v) = sig.to_rsv();
|
||||
SignedTransaction {
|
||||
unsigned: self,
|
||||
r: r,
|
||||
s: s,
|
||||
v: v + 27,
|
||||
v: v + if let Some(n) = network_id { 35 + n * 2 } else { 27 },
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
}
|
||||
@@ -201,7 +207,8 @@ impl Transaction {
|
||||
pub struct SignedTransaction {
|
||||
/// Plain Transaction.
|
||||
unsigned: Transaction,
|
||||
/// The V field of the signature, either 27 or 28; helps describe the point on the curve.
|
||||
/// The V field of the signature; the LS bit described which half of the curve our point falls
|
||||
/// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks.
|
||||
v: u8,
|
||||
/// The R field of the signature; helps describe the point on the curve.
|
||||
r: U256,
|
||||
@@ -257,7 +264,7 @@ impl Encodable for SignedTransaction {
|
||||
|
||||
impl SignedTransaction {
|
||||
/// Append object with a signature into RLP stream
|
||||
pub fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
|
||||
fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
|
||||
s.begin_list(9);
|
||||
s.append(&self.nonce);
|
||||
s.append(&self.gas_price);
|
||||
@@ -286,8 +293,16 @@ impl SignedTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// 0 is `v` is 27, 1 if 28, and 4 otherwise.
|
||||
pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } }
|
||||
/// 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 } }
|
||||
|
||||
/// The network ID, or `None` if this is a global transaction.
|
||||
pub fn network_id(&self) -> Option<u8> {
|
||||
match self.v {
|
||||
v if v > 36 => Some((v - 35) / 2),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a signature object from the sig.
|
||||
pub fn signature(&self) -> Signature { Signature::from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) }
|
||||
@@ -303,28 +318,38 @@ impl SignedTransaction {
|
||||
|
||||
/// Returns transaction sender.
|
||||
pub fn sender(&self) -> Result<Address, Error> {
|
||||
let sender = self.sender.get();
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = Address::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3());
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
let sender = self.sender.get();
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = Address::from(try!(self.public_key()).sha3());
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the public key of the sender.
|
||||
pub fn public_key(&self) -> Result<Public, Error> {
|
||||
Ok(try!(ec::recover(&self.signature(), &self.unsigned.hash(self.network_id()))))
|
||||
}
|
||||
|
||||
/// Do basic validation, checking for valid signature and minimum gas,
|
||||
// TODO: consider use in block validation.
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "json-tests")]
|
||||
pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<SignedTransaction, Error> {
|
||||
pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool) -> Result<SignedTransaction, Error> {
|
||||
if require_low && !ec::is_low_s(&self.s) {
|
||||
return Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)));
|
||||
}
|
||||
match self.network_id() {
|
||||
None => {},
|
||||
Some(1) if allow_network_id_of_one => {},
|
||||
_ => return Err(TransactionError::InvalidNetworkId.into()),
|
||||
}
|
||||
try!(self.sender());
|
||||
if self.gas < U256::from(self.gas_required(&schedule)) {
|
||||
Err(From::from(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas})))
|
||||
Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
@@ -364,6 +389,7 @@ fn sender_test() {
|
||||
} else { panic!(); }
|
||||
assert_eq!(t.value, U256::from(0x0au64));
|
||||
assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e"));
|
||||
assert_eq!(t.network_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -376,8 +402,9 @@ fn signing() {
|
||||
gas: U256::from(50_000),
|
||||
value: U256::from(1),
|
||||
data: b"Hello!".to_vec()
|
||||
}.sign(&key.secret());
|
||||
}.sign(&key.secret(), None);
|
||||
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -391,7 +418,47 @@ fn fake_signing() {
|
||||
data: b"Hello!".to_vec()
|
||||
}.fake_sign(Address::from(0x69));
|
||||
assert_eq!(Address::from(0x69), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), None);
|
||||
|
||||
let t = t.clone();
|
||||
assert_eq!(Address::from(0x69), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_recover_from_network_specific_signing() {
|
||||
let key = ::util::crypto::KeyPair::create().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
nonce: U256::from(42),
|
||||
gas_price: U256::from(3000),
|
||||
gas: U256::from(50_000),
|
||||
value: U256::from(1),
|
||||
data: b"Hello!".to_vec()
|
||||
}.sign(&key.secret(), Some(69));
|
||||
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), Some(69));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_agree_with_vitalik() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
let test_vector = |tx_data: &str, address: &'static str| {
|
||||
let signed: SignedTransaction = decode(&FromHex::from_hex(tx_data).unwrap());
|
||||
signed.check_low_s().unwrap();
|
||||
assert_eq!(signed.sender().unwrap(), address.into());
|
||||
flushln!("networkid: {:?}", signed.network_id());
|
||||
};
|
||||
|
||||
test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce")
|
||||
test_vector("f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112")
|
||||
test_vector("f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be")
|
||||
test_vector("f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0")
|
||||
test_vector("f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554")
|
||||
test_vector("f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4")
|
||||
test_vector("f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35")
|
||||
test_vector("f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332")
|
||||
test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029")
|
||||
test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f")
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ mod tests {
|
||||
gas: U256::from(30_000),
|
||||
gas_price: U256::from(40_000),
|
||||
nonce: U256::one()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
|
||||
let tr2 = Transaction {
|
||||
action: Action::Create,
|
||||
@@ -369,7 +369,7 @@ mod tests {
|
||||
gas: U256::from(30_000),
|
||||
gas_price: U256::from(40_000),
|
||||
nonce: U256::from(2)
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
|
||||
let good_transactions = [ tr1.clone(), tr2.clone() ];
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use keccak::Keccak256;
|
||||
use super::{KeyPair, Error, Generator, Secret};
|
||||
use super::{KeyPair, Error, Generator};
|
||||
|
||||
/// Simple brainwallet.
|
||||
pub struct Brain(String);
|
||||
@@ -38,9 +38,9 @@ impl Generator for Brain {
|
||||
match i > 16384 {
|
||||
false => i += 1,
|
||||
true => {
|
||||
let result = KeyPair::from_secret(Secret::from(secret.clone()));
|
||||
if result.is_ok() {
|
||||
return result
|
||||
let result = KeyPair::from_secret(secret.clone().into());
|
||||
if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use account::{Version, Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Crypto {
|
||||
pub cipher: Cipher,
|
||||
pub ciphertext: [u8; 32],
|
||||
pub ciphertext: Vec<u8>,
|
||||
pub kdf: Kdf,
|
||||
pub mac: [u8; 32],
|
||||
}
|
||||
@@ -95,7 +95,7 @@ impl Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: iv,
|
||||
}),
|
||||
ciphertext: ciphertext,
|
||||
ciphertext: ciphertext.to_vec(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
dklen: crypto::KEY_LENGTH as u32,
|
||||
salt: salt,
|
||||
@@ -107,6 +107,10 @@ impl Crypto {
|
||||
}
|
||||
|
||||
pub fn secret(&self, password: &str) -> Result<Secret, Error> {
|
||||
if self.ciphertext.len() > 32 {
|
||||
return Err(Error::InvalidSecret);
|
||||
}
|
||||
|
||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c),
|
||||
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r),
|
||||
@@ -122,7 +126,8 @@ impl Crypto {
|
||||
|
||||
match self.cipher {
|
||||
Cipher::Aes128Ctr(ref params) => {
|
||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut *secret)
|
||||
let from = 32 - self.ciphertext.len();
|
||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut (&mut *secret)[from..])
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -76,15 +76,14 @@ impl DiskDirectory {
|
||||
.map(|entry| entry.path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
let files: Result<Vec<_>, _> = paths.iter()
|
||||
.map(fs::File::open)
|
||||
.collect();
|
||||
|
||||
let files = try!(files);
|
||||
|
||||
files.into_iter()
|
||||
.map(json::KeyFile::load)
|
||||
.zip(paths.into_iter())
|
||||
paths
|
||||
.iter()
|
||||
.map(|p| (
|
||||
fs::File::open(p)
|
||||
.map_err(Error::from)
|
||||
.and_then(|r| json::KeyFile::load(r).map_err(|e| Error::Custom(format!("{:?}", e)))),
|
||||
p
|
||||
))
|
||||
.map(|(file, path)| match file {
|
||||
Ok(file) => Ok((path.clone(), SafeAccount::from_file(
|
||||
file, path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned()
|
||||
@@ -163,7 +162,8 @@ mod test {
|
||||
#[test]
|
||||
fn should_create_new_account() {
|
||||
// given
|
||||
let dir = env::temp_dir();
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_create_new_account");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world";
|
||||
let directory = DiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
58
ethstore/src/json/bytes.rs
Normal file
58
ethstore/src/json/bytes.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::{ops, str};
|
||||
use serde::{Deserialize, Deserializer, Error, Serialize, Serializer};
|
||||
use rustc_serialize::hex::{ToHex, FromHex, FromHexError};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Bytes(Vec<u8>);
|
||||
|
||||
impl ops::Deref for Bytes {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Bytes {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||
where D: Deserializer
|
||||
{
|
||||
let s = try!(String::deserialize(deserializer));
|
||||
let data = try!(s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e))));
|
||||
Ok(Bytes(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Bytes {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
serializer.serialize_str(&self.0.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Bytes {
|
||||
type Err = FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.from_hex().map(Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Bytes {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
Bytes(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bytes> for Vec<u8> {
|
||||
fn from(b: Bytes) -> Self {
|
||||
b.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, Error};
|
||||
use serde::de::{Visitor, MapVisitor};
|
||||
use serde::ser;
|
||||
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256};
|
||||
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes};
|
||||
|
||||
pub type CipherText = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Crypto {
|
||||
pub cipher: Cipher,
|
||||
pub ciphertext: H256,
|
||||
pub ciphertext: CipherText,
|
||||
pub kdf: Kdf,
|
||||
pub mac: H256,
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
use std::str::FromStr;
|
||||
use std::{ops, fmt, str};
|
||||
use rustc_serialize::hex::{FromHex, ToHex};
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError};
|
||||
use serde::de::Visitor;
|
||||
@@ -65,7 +63,7 @@ macro_rules! impl_hash {
|
||||
type Value = $name;
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
FromStr::from_str(value).map_err(SerdeError::custom)
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
@@ -77,7 +75,7 @@ macro_rules! impl_hash {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for $name {
|
||||
impl str::FromStr for $name {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
@@ -92,6 +90,12 @@ macro_rules! impl_hash {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for $name {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; $size]> for $name {
|
||||
fn from(bytes: [u8; $size]) -> Self {
|
||||
$name(bytes)
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Universaly unique identifier.
|
||||
use std::str::FromStr;
|
||||
use std::fmt;
|
||||
use std::{fmt, str};
|
||||
use rustc_serialize::hex::{ToHex, FromHex};
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer, Error as SerdeError};
|
||||
use serde::de::Visitor;
|
||||
@@ -73,7 +72,7 @@ fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl FromStr for UUID {
|
||||
impl str::FromStr for UUID {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
@@ -95,6 +94,12 @@ impl FromStr for UUID {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for UUID {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UUID {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
@@ -116,7 +121,7 @@ impl Visitor for UUIDVisitor {
|
||||
type Value = UUID;
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
UUID::from_str(value).map_err(SerdeError::custom)
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
@@ -126,19 +131,18 @@ impl Visitor for UUIDVisitor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use super::UUID;
|
||||
|
||||
#[test]
|
||||
fn uuid_from_str() {
|
||||
let uuid = UUID::from_str("3198bc9c-6672-5ab3-d995-4942343ae5b6").unwrap();
|
||||
let uuid: UUID = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into();
|
||||
assert_eq!(uuid, UUID::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uuid_from_and_to_str() {
|
||||
let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6";
|
||||
let uuid = UUID::from_str(from).unwrap();
|
||||
let uuid: UUID = from.into();
|
||||
let to: String = uuid.into();
|
||||
assert_eq!(from, &to);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ impl Visitor for KeyFileVisitor {
|
||||
Some(KeyFileField::Version) => { version = Some(try!(visitor.visit_value())); }
|
||||
Some(KeyFileField::Crypto) => { crypto = Some(try!(visitor.visit_value())); }
|
||||
Some(KeyFileField::Address) => { address = Some(try!(visitor.visit_value())); }
|
||||
Some(KeyFileField::Name) => { name = visitor.visit_value().ok(); } // ignore anyhing that is not a string to be permissive.
|
||||
Some(KeyFileField::Name) => { name = visitor.visit_value().ok(); } // ignore anyhing that is not a string to be permissive.
|
||||
Some(KeyFileField::Meta) => { meta = visitor.visit_value().ok(); } // ignore anyhing that is not a string to be permissive.
|
||||
None => { break; }
|
||||
}
|
||||
@@ -153,7 +153,7 @@ impl KeyFile {
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use json::{KeyFile, UUID, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt, H128, H160, H256};
|
||||
use json::{KeyFile, UUID, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt};
|
||||
|
||||
#[test]
|
||||
fn basic_keyfile() {
|
||||
@@ -185,20 +185,20 @@ mod tests {
|
||||
let expected = KeyFile {
|
||||
id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
version: Version::V3,
|
||||
address: H160::from_str("6edddfc6349aff20bc6467ccf276c5b52487f7a8").unwrap(),
|
||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: H128::from_str("b5a7ec855ec9e2c405371356855fec83").unwrap(),
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: H256::from_str("7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc").unwrap(),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: H256::from_str("1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209").unwrap(),
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: H256::from_str("46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f").unwrap(),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: Some("{}".to_owned()),
|
||||
@@ -234,22 +234,22 @@ mod tests {
|
||||
}"#;
|
||||
|
||||
let expected = KeyFile {
|
||||
id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: H160::from_str("6edddfc6349aff20bc6467ccf276c5b52487f7a8").unwrap(),
|
||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: H128::from_str("b5a7ec855ec9e2c405371356855fec83").unwrap(),
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: H256::from_str("7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc").unwrap(),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: H256::from_str("1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209").unwrap(),
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: H256::from_str("46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f").unwrap(),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: None,
|
||||
meta: None,
|
||||
@@ -262,22 +262,22 @@ mod tests {
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = KeyFile {
|
||||
id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: H160::from_str("6edddfc6349aff20bc6467ccf276c5b52487f7a8").unwrap(),
|
||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: H128::from_str("b5a7ec855ec9e2c405371356855fec83").unwrap(),
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: H256::from_str("7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc").unwrap(),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: H256::from_str("1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209").unwrap(),
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: H256::from_str("46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f").unwrap(),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: None,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod bytes;
|
||||
mod cipher;
|
||||
mod crypto;
|
||||
mod error;
|
||||
@@ -8,8 +9,9 @@ mod key_file;
|
||||
mod presale;
|
||||
mod version;
|
||||
|
||||
pub use self::bytes::Bytes;
|
||||
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
|
||||
pub use self::crypto::Crypto;
|
||||
pub use self::crypto::{Crypto, CipherText};
|
||||
pub use self::error::Error;
|
||||
pub use self::hash::{H128, H160, H256};
|
||||
pub use self::id::UUID;
|
||||
|
||||
@@ -1,30 +1,8 @@
|
||||
use std::io::Read;
|
||||
use std::ops::Deref;
|
||||
use serde_json;
|
||||
use serde::{Deserialize, Deserializer, Error};
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use super::{H160};
|
||||
use super::{H160, Bytes};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Encseed(Vec<u8>);
|
||||
|
||||
impl Deref for Encseed {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Encseed {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||
where D: Deserializer
|
||||
{
|
||||
let s = try!(String::deserialize(deserializer));
|
||||
let data = try!(s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e))));
|
||||
Ok(Encseed(data))
|
||||
}
|
||||
}
|
||||
pub type Encseed = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct PresaleWallet {
|
||||
@@ -43,8 +21,7 @@ impl PresaleWallet {
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use json::{PresaleWallet, H160, Encseed};
|
||||
use json::{PresaleWallet, H160};
|
||||
|
||||
#[test]
|
||||
fn presale_wallet() {
|
||||
@@ -57,7 +34,7 @@ mod tests {
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: Encseed("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".from_hex().unwrap()),
|
||||
encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".into(),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
@@ -77,7 +54,7 @@ mod tests {
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: Encseed("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".from_hex().unwrap()),
|
||||
encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".into(),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
|
||||
@@ -19,9 +19,8 @@ extern crate ethstore;
|
||||
|
||||
mod util;
|
||||
|
||||
use std::str::FromStr;
|
||||
use ethstore::{SecretStore, EthStore};
|
||||
use ethstore::ethkey::{Random, Generator, Secret, Address};
|
||||
use ethstore::ethkey::{Random, Generator, Secret, KeyPair, verify_address};
|
||||
use ethstore::dir::DiskDirectory;
|
||||
use util::TransientDir;
|
||||
|
||||
@@ -103,14 +102,21 @@ fn pat_path() -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/ciphertext",
|
||||
Err(_) => "tests/res/ciphertext",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_laod_geth_files() {
|
||||
let dir = DiskDirectory::at(test_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap(),
|
||||
Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap(),
|
||||
Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap(),
|
||||
"3f49624084b67849c7b4e805c5988c21a430f9d9".parse().unwrap(),
|
||||
"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".parse().unwrap(),
|
||||
"63121b431a52f8043c16fcf0d1df9cb7b5f66649".parse().unwrap(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -119,9 +125,30 @@ fn secret_store_load_pat_files() {
|
||||
let dir = DiskDirectory::at(pat_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap(),
|
||||
Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap(),
|
||||
"3f49624084b67849c7b4e805c5988c21a430f9d9".parse().unwrap(),
|
||||
"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".parse().unwrap(),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypting_files_with_short_ciphertext() {
|
||||
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
|
||||
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap();
|
||||
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
|
||||
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap();
|
||||
let dir = DiskDirectory::at(ciphertext_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts, vec![
|
||||
"31e9d1e6d844bd3a536800ef8d8be6a9975db509".parse().unwrap(),
|
||||
"d1e64e5480bfaf733ba7d48712decb8227797a4e".parse().unwrap(),
|
||||
]);
|
||||
|
||||
let message = Default::default();
|
||||
|
||||
let s1 = store.sign(&accounts[0], "foo", &message).unwrap();
|
||||
let s2 = store.sign(&accounts[1], "foo", &message).unwrap();
|
||||
assert!(verify_address(&accounts[0], &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
|
||||
}
|
||||
|
||||
21
ethstore/tests/res/ciphertext/30.json
Normal file
21
ethstore/tests/res/ciphertext/30.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"address" : "31e9d1e6d844bd3a536800ef8d8be6a9975db509",
|
||||
"crypto" : {
|
||||
"cipher" : "aes-128-ctr",
|
||||
"cipherparams" : {
|
||||
"iv" : "3ca92af36ad7c2cd92454c59cea5ef00"
|
||||
},
|
||||
"ciphertext" : "108b7d34f3442fc26ab1ab90ca91476ba6bfa8c00975a49ef9051dc675aa",
|
||||
"kdf" : "scrypt",
|
||||
"kdfparams" : {
|
||||
"dklen" : 32,
|
||||
"n" : 2,
|
||||
"r" : 8,
|
||||
"p" : 1,
|
||||
"salt" : "d0769e608fb86cda848065642a9c6fa046845c928175662b8e356c77f914cd3b"
|
||||
},
|
||||
"mac" : "75d0e6759f7b3cefa319c3be41680ab6beea7d8328653474bd06706d4cc67420"
|
||||
},
|
||||
"id" : "a37e1559-5955-450d-8075-7b8931b392b2",
|
||||
"version" : 3
|
||||
}
|
||||
21
ethstore/tests/res/ciphertext/31.json
Normal file
21
ethstore/tests/res/ciphertext/31.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"address" : "d1e64e5480bfaf733ba7d48712decb8227797a4e",
|
||||
"crypto" : {
|
||||
"cipher" : "aes-128-ctr",
|
||||
"cipherparams" : {
|
||||
"iv" : "e0c41130a323adc1446fc82f724bca2f"
|
||||
},
|
||||
"ciphertext" : "9517cd5bdbe69076f9bf5057248c6c050141e970efa36ce53692d5d59a3984",
|
||||
"kdf" : "scrypt",
|
||||
"kdfparams" : {
|
||||
"dklen" : 32,
|
||||
"n" : 2,
|
||||
"r" : 8,
|
||||
"p" : 1,
|
||||
"salt" : "711f816911c92d649fb4c84b047915679933555030b3552c1212609b38208c63"
|
||||
},
|
||||
"mac" : "d5e116151c6aa71470e67a7d42c9620c75c4d23229847dcc127794f0732b0db5"
|
||||
},
|
||||
"id" : "fecfc4ce-e956-48fd-953b-30f8b52ed66c",
|
||||
"version" : 3
|
||||
}
|
||||
@@ -48,6 +48,14 @@ impl Ext for FakeExt {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn balance(&self, _address: &Address) -> U256 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
include!("lib.rs.in");
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
pub fn register_cleaner(reg: &mut syntex::Registry) {
|
||||
use syntax::{ast, fold};
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
@@ -59,6 +59,7 @@ pub fn register(reg: &mut syntex::Registry) {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref n, _) if n == &"ipc" => { return None; }
|
||||
ast::MetaItemKind::Word(ref n) if n == &"ipc" => { return None; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -73,19 +74,24 @@ pub fn register(reg: &mut syntex::Registry) {
|
||||
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
||||
}
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
reg.add_attr("feature(custom_derive)");
|
||||
reg.add_attr("feature(custom_attribute)");
|
||||
|
||||
reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation);
|
||||
reg.add_decorator("ipc", codegen::expand_ipc_implementation);
|
||||
reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation);
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
register_cleaner(reg);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_syntax_extension(
|
||||
syntax::parse::token::intern("derive_Ipc"),
|
||||
syntax::parse::token::intern("ipc"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(codegen::expand_ipc_implementation)));
|
||||
reg.register_syntax_extension(
|
||||
@@ -133,6 +139,30 @@ pub fn derive_ipc(src_path: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cleanup_ipc(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = try!(PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned()));
|
||||
let mut registry = syntex::Registry::new();
|
||||
register_cleaner(&mut registry);
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &Path::new(&out_dir).join(&file_name))
|
||||
{
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn derive_ipc_cond(src_path: &str, cond: bool) -> Result<(), Error> {
|
||||
if cond {
|
||||
derive_ipc(src_path)
|
||||
}
|
||||
else {
|
||||
cleanup_ipc(src_path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive_binary(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct HypervisorService {
|
||||
check_list: RwLock<HashMap<IpcModuleId, bool>>,
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
impl HypervisorService {
|
||||
fn module_ready(&self, module_id: u64) -> bool {
|
||||
let mut check_list = self.check_list.write().unwrap();
|
||||
|
||||
@@ -36,7 +36,7 @@ impl IpcConfig for DBWriter {}
|
||||
#[derive(Binary)]
|
||||
pub enum DBError { Write, Read }
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
impl<L: Sized> DBWriter for DB<L> {
|
||||
fn write(&self, data: Vec<u8>) -> Result<(), DBError> {
|
||||
let mut writes = self.writes.write().unwrap();
|
||||
@@ -51,7 +51,7 @@ impl<L: Sized> DBWriter for DB<L> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
trait DBNotify {
|
||||
fn notify(&self, a: u64, b: u64) -> bool;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct CustomData {
|
||||
pub b: u64,
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
impl Service {
|
||||
fn commit(&self, f: u32) -> u32 {
|
||||
let mut lock = self.commits.write().unwrap();
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::collections::VecDeque;
|
||||
|
||||
pub struct BadlyNamedService;
|
||||
|
||||
#[derive(Ipc)]
|
||||
//#[derive(Ipc)]
|
||||
#[ipc(client_ident="PrettyNamedClient")]
|
||||
impl BadlyNamedService {
|
||||
fn is_zero(&self, x: u64) -> bool {
|
||||
|
||||
@@ -63,7 +63,7 @@ mod tests {
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit" : "0x",
|
||||
"homesteadTransition" : "0x",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
"daoHardforkAccounts": []
|
||||
|
||||
@@ -40,8 +40,8 @@ pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
pub registrar: Option<Address>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="frontierCompatibilityModeLimit")]
|
||||
pub frontier_compatibility_mode_limit: Option<Uint>,
|
||||
#[serde(rename="homesteadTransition")]
|
||||
pub homestead_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="daoHardforkTransition")]
|
||||
pub dao_hardfork_transition: Option<Uint>,
|
||||
@@ -51,6 +51,37 @@ pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="daoHardforkAccounts")]
|
||||
pub dao_hardfork_accounts: Option<Vec<Address>>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="difficultyHardforkTransition")]
|
||||
pub difficulty_hardfork_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="difficultyHardforkBoundDivisor")]
|
||||
pub difficulty_hardfork_bound_divisor: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="bombDefuseTransition")]
|
||||
pub bomb_defuse_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip150Transition")]
|
||||
pub eip150_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip155Transition")]
|
||||
pub eip155_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip160Transition")]
|
||||
pub eip160_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip161abcTransition")]
|
||||
pub eip161abc_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip161dTransition")]
|
||||
pub eip161d_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="maxCodeSize")]
|
||||
pub max_code_size: Option<Uint>,
|
||||
}
|
||||
|
||||
/// Ethash engine deserialization.
|
||||
@@ -75,7 +106,7 @@ mod tests {
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x42",
|
||||
"homesteadTransition": "0x42",
|
||||
"daoHardforkTransition": "0x08",
|
||||
"daoHardforkBeneficiary": "0xabcabcabcabcabcabcabcabcabcabcabcabcabca",
|
||||
"daoHardforkAccounts": [
|
||||
@@ -99,7 +130,15 @@ mod tests {
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"difficultyHardforkTransition": "0x59d9",
|
||||
"difficultyHardforkBoundDivisor": "0x0200",
|
||||
"bombDefuseTransition": "0x42",
|
||||
"eip150Transition": "0x42",
|
||||
"eip155Transition": "0x42",
|
||||
"eip160Transition": "0x42",
|
||||
"eip161abcTransition": "0x42",
|
||||
"eip161dTransition": "0x42"
|
||||
}
|
||||
}"#;
|
||||
|
||||
|
||||
@@ -28,12 +28,17 @@ pub struct Params {
|
||||
/// Maximum size of extra data.
|
||||
#[serde(rename="maximumExtraDataSize")]
|
||||
pub maximum_extra_data_size: Uint,
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
pub network_id: Uint,
|
||||
/// Minimum gas limit.
|
||||
#[serde(rename="minGasLimit")]
|
||||
pub min_gas_limit: Uint,
|
||||
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
pub network_id: Uint,
|
||||
/// Name of the main ("eth") subprotocol.
|
||||
#[serde(rename="subprotocolName")]
|
||||
pub subprotocol_name: Option<String>,
|
||||
|
||||
/// Option fork block number to check.
|
||||
#[serde(rename="forkBlock")]
|
||||
pub fork_block: Option<Uint>,
|
||||
@@ -50,7 +55,7 @@ mod tests {
|
||||
#[test]
|
||||
fn params_deserialization() {
|
||||
let s = r#"{
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"networkID" : "0x1",
|
||||
"minGasLimit": "0x1388",
|
||||
|
||||
@@ -66,7 +66,7 @@ mod tests {
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit" : "0x",
|
||||
"homesteadTransition" : "0x",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
"daoHardforkAccounts": []
|
||||
@@ -75,7 +75,7 @@ mod tests {
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"homesteadTransition": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||
!define VERSIONMAJOR 1
|
||||
!define VERSIONMINOR 3
|
||||
!define VERSIONBUILD 3
|
||||
!define VERSIONBUILD 15
|
||||
|
||||
!addplugindir .\
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ pub struct ImportBlockchain {
|
||||
pub file_path: Option<String>,
|
||||
pub format: Option<DataFormat>,
|
||||
pub pruning: Pruning,
|
||||
pub pruning_history: u64,
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
pub wal: bool,
|
||||
pub mode: Mode,
|
||||
@@ -91,6 +92,7 @@ pub struct ExportBlockchain {
|
||||
pub file_path: Option<String>,
|
||||
pub format: Option<DataFormat>,
|
||||
pub pruning: Pruning,
|
||||
pub pruning_history: u64,
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
pub wal: bool,
|
||||
pub mode: Mode,
|
||||
@@ -131,7 +133,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<String, String> {
|
||||
try!(execute_upgrades(&cmd.dirs, genesis_hash, spec.fork_name.as_ref(), algorithm, cmd.compaction.compaction_profile()));
|
||||
|
||||
// prepare client config
|
||||
let client_config = to_client_config(&cmd.cache_config, &cmd.dirs, genesis_hash, cmd.mode, cmd.tracing, cmd.pruning, cmd.compaction, cmd.wal, cmd.vm_type, "".into(), spec.fork_name.as_ref());
|
||||
let client_config = to_client_config(&cmd.cache_config, &cmd.dirs, genesis_hash, cmd.mode, cmd.tracing, cmd.pruning, cmd.pruning_history, cmd.compaction, cmd.wal, cmd.vm_type, "".into(), spec.fork_name.as_ref());
|
||||
|
||||
// build client
|
||||
let service = try!(ClientService::start(
|
||||
@@ -242,7 +244,7 @@ fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
|
||||
try!(execute_upgrades(&cmd.dirs, genesis_hash, spec.fork_name.as_ref(), algorithm, cmd.compaction.compaction_profile()));
|
||||
|
||||
// prepare client config
|
||||
let client_config = to_client_config(&cmd.cache_config, &cmd.dirs, genesis_hash, cmd.mode, cmd.tracing, cmd.pruning, cmd.compaction, cmd.wal, VMType::default(), "".into(), spec.fork_name.as_ref());
|
||||
let client_config = to_client_config(&cmd.cache_config, &cmd.dirs, genesis_hash, cmd.mode, cmd.tracing, cmd.pruning, cmd.pruning_history, cmd.compaction, cmd.wal, VMType::default(), "".into(), spec.fork_name.as_ref());
|
||||
|
||||
let service = try!(ClientService::start(
|
||||
client_config,
|
||||
|
||||
@@ -50,7 +50,7 @@ Operating Options:
|
||||
[default: 3600].
|
||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a
|
||||
JSON chain specification file or olympic, frontier,
|
||||
homestead, mainnet, morden, classic or testnet
|
||||
homestead, mainnet, ropsten, morden, classic or testnet
|
||||
[default: homestead].
|
||||
-d --db-path PATH Specify the database & configuration directory path
|
||||
[default: $HOME/.parity].
|
||||
@@ -126,7 +126,6 @@ API and Console Options:
|
||||
--ipc-apis APIS Specify custom API set available via JSON-RPC over
|
||||
IPC [default: web3,eth,net,ethcore,personal,traces,rpc].
|
||||
|
||||
--no-dapps Disable the Dapps server (e.g. status page).
|
||||
--dapps-port PORT Specify the port portion of the Dapps server
|
||||
[default: 8080].
|
||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||
@@ -164,7 +163,7 @@ Sealing/Mining Options:
|
||||
--work-queue-size ITEMS Specify the number of historical work packages
|
||||
which are kept cached lest a solution is found for
|
||||
them later. High values take more memory but result
|
||||
in fewer unusable solutions [default: 20].
|
||||
in fewer unusable solutions [default: 5].
|
||||
--tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas
|
||||
a single transaction may have for it to be mined.
|
||||
--relay-set SET Set of transactions to relay. SET may be:
|
||||
@@ -193,7 +192,17 @@ Sealing/Mining Options:
|
||||
--extra-data STRING Specify a custom extra-data for authored blocks, no
|
||||
more than 32 characters.
|
||||
--tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting
|
||||
to be included in next block) [default: 1024].
|
||||
to be included in next block) [default: 2048].
|
||||
--tx-queue-strategy S Prioritization strategy used to order transactions
|
||||
in the queue. S may be:
|
||||
gas - Prioritize txs with low gas limit;
|
||||
gas_price - Prioritize txs with high gas price;
|
||||
gas_factor - Prioritize txs using gas price
|
||||
and gas limit ratio [default: gas_factor].
|
||||
--tx-queue-gas LIMIT Maximum amount of total gas for external transactions in
|
||||
the queue. LIMIT can be either an amount of gas or
|
||||
'auto' or 'off'. 'auto' sets the limit to be 20x
|
||||
the current block gas limit. [default: auto].
|
||||
--remove-solved Move solved blocks from the work package queue
|
||||
instead of cloning them. This gives a slightly
|
||||
faster import speed, but means that extra solutions
|
||||
@@ -213,6 +222,8 @@ Footprint Options:
|
||||
fast - maintain journal overlay. Fast but 50MB used.
|
||||
auto - use the method most recently synced or
|
||||
default to fast if none synced [default: auto].
|
||||
--pruning-history NUM Set a number of recent states to keep when pruning
|
||||
is active. [default: 64].
|
||||
--cache-size-db MB Override database cache size [default: 64].
|
||||
--cache-size-blocks MB Specify the prefered size of the blockchain cache in
|
||||
megabytes [default: 8].
|
||||
@@ -277,6 +288,7 @@ Legacy Options:
|
||||
--etherbase ADDRESS Equivalent to --author ADDRESS.
|
||||
--extradata STRING Equivalent to --extra-data STRING.
|
||||
--cache MB Equivalent to --cache-size MB.
|
||||
--no-dapps Disable the Dapps server (e.g. status page).
|
||||
|
||||
Miscellaneous Options:
|
||||
-l --logging LOGGING Specify the logging level. Must conform to the same
|
||||
@@ -317,8 +329,9 @@ pub struct Args {
|
||||
pub flag_keys_iterations: u32,
|
||||
pub flag_no_import_keys: bool,
|
||||
pub flag_bootnodes: Option<String>,
|
||||
pub flag_network_id: Option<String>,
|
||||
pub flag_network_id: Option<usize>,
|
||||
pub flag_pruning: String,
|
||||
pub flag_pruning_history: u64,
|
||||
pub flag_tracing: String,
|
||||
pub flag_port: u16,
|
||||
pub flag_min_peers: u16,
|
||||
@@ -372,12 +385,14 @@ pub struct Args {
|
||||
pub flag_gas_cap: String,
|
||||
pub flag_extra_data: Option<String>,
|
||||
pub flag_tx_queue_size: usize,
|
||||
pub flag_tx_queue_strategy: String,
|
||||
pub flag_tx_queue_gas: String,
|
||||
pub flag_notify_work: Option<String>,
|
||||
pub flag_logging: Option<String>,
|
||||
pub flag_version: bool,
|
||||
pub flag_from: String,
|
||||
pub flag_to: String,
|
||||
pub flag_at: String,
|
||||
pub flag_at: String,
|
||||
pub flag_format: Option<String>,
|
||||
pub flag_jitvm: bool,
|
||||
pub flag_log_file: Option<String>,
|
||||
@@ -400,7 +415,7 @@ pub struct Args {
|
||||
pub flag_rpccorsdomain: Option<String>,
|
||||
pub flag_rpcapi: Option<String>,
|
||||
pub flag_testnet: bool,
|
||||
pub flag_networkid: Option<String>,
|
||||
pub flag_networkid: Option<usize>,
|
||||
pub flag_ipcdisable: bool,
|
||||
pub flag_ipc_off: bool,
|
||||
pub flag_jsonrpc_off: bool,
|
||||
|
||||
@@ -25,13 +25,13 @@ use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address};
|
||||
use util::log::Colour;
|
||||
use ethsync::{NetworkConfiguration, is_valid_node_url};
|
||||
use ethcore::client::{VMType, Mode};
|
||||
use ethcore::miner::MinerOptions;
|
||||
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
|
||||
|
||||
use rpc::{IpcConfiguration, HttpConfiguration};
|
||||
use ethcore_rpc::NetworkSettings;
|
||||
use cache::CacheConfig;
|
||||
use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home,
|
||||
geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address};
|
||||
geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit};
|
||||
use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras};
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
use dir::Directories;
|
||||
@@ -73,6 +73,7 @@ impl Configuration {
|
||||
pub fn into_command(self) -> Result<Cmd, String> {
|
||||
let dirs = self.directories();
|
||||
let pruning = try!(self.args.flag_pruning.parse());
|
||||
let pruning_history = self.args.flag_pruning_history;
|
||||
let vm_type = try!(self.vm_type());
|
||||
let mode = try!(to_mode(&self.args.flag_mode, self.args.flag_mode_timeout, self.args.flag_mode_alarm));
|
||||
let miner_options = try!(self.miner_options());
|
||||
@@ -80,7 +81,7 @@ impl Configuration {
|
||||
let http_conf = try!(self.http_config());
|
||||
let ipc_conf = try!(self.ipc_config());
|
||||
let net_conf = try!(self.net_config());
|
||||
let network_id = try!(self.network_id());
|
||||
let network_id = self.network_id();
|
||||
let cache_config = self.cache_config();
|
||||
let spec = try!(self.chain().parse());
|
||||
let tracing = try!(self.args.flag_tracing.parse());
|
||||
@@ -134,6 +135,7 @@ impl Configuration {
|
||||
file_path: self.args.arg_file.clone(),
|
||||
format: format,
|
||||
pruning: pruning,
|
||||
pruning_history: pruning_history,
|
||||
compaction: compaction,
|
||||
wal: wal,
|
||||
mode: mode,
|
||||
@@ -150,6 +152,7 @@ impl Configuration {
|
||||
file_path: self.args.arg_file.clone(),
|
||||
format: format,
|
||||
pruning: pruning,
|
||||
pruning_history: pruning_history,
|
||||
compaction: compaction,
|
||||
wal: wal,
|
||||
mode: mode,
|
||||
@@ -164,6 +167,7 @@ impl Configuration {
|
||||
dirs: dirs,
|
||||
spec: spec,
|
||||
pruning: pruning,
|
||||
pruning_history: pruning_history,
|
||||
logger_config: logger_config,
|
||||
mode: mode,
|
||||
tracing: tracing,
|
||||
@@ -180,6 +184,7 @@ impl Configuration {
|
||||
dirs: dirs,
|
||||
spec: spec,
|
||||
pruning: pruning,
|
||||
pruning_history: pruning_history,
|
||||
logger_config: logger_config,
|
||||
mode: mode,
|
||||
tracing: tracing,
|
||||
@@ -202,6 +207,7 @@ impl Configuration {
|
||||
dirs: dirs,
|
||||
spec: spec,
|
||||
pruning: pruning,
|
||||
pruning_history: pruning_history,
|
||||
daemon: daemon,
|
||||
logger_config: logger_config,
|
||||
miner_options: miner_options,
|
||||
@@ -288,7 +294,7 @@ impl Configuration {
|
||||
|
||||
fn chain(&self) -> String {
|
||||
if self.args.flag_testnet {
|
||||
"morden".to_owned()
|
||||
"ropsten".to_owned()
|
||||
} else {
|
||||
self.args.flag_chain.clone()
|
||||
}
|
||||
@@ -319,6 +325,15 @@ impl Configuration {
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
fn transaction_queue_strategy(&self) -> Result<PrioritizationStrategy, String> {
|
||||
match self.args.flag_tx_queue_strategy.as_str() {
|
||||
"gas" => Ok(PrioritizationStrategy::GasAndGasPrice),
|
||||
"gas_price" => Ok(PrioritizationStrategy::GasPriceOnly),
|
||||
"gas_factor" => Ok(PrioritizationStrategy::GasFactorAndGasPrice),
|
||||
_ => Err(format!("Invalid queue strategy: {}", self.args.flag_tx_queue_strategy)),
|
||||
}
|
||||
}
|
||||
|
||||
fn miner_options(&self) -> Result<MinerOptions, String> {
|
||||
let reseal = try!(self.args.flag_reseal_on_txs.parse::<ResealPolicy>());
|
||||
|
||||
@@ -332,6 +347,8 @@ impl Configuration {
|
||||
None => U256::max_value(),
|
||||
},
|
||||
tx_queue_size: self.args.flag_tx_queue_size,
|
||||
tx_queue_strategy: try!(self.transaction_queue_strategy()),
|
||||
tx_queue_gas_limit: try!(to_gas_limit(&self.args.flag_tx_queue_gas)),
|
||||
pending_set: try!(to_pending_set(&self.args.flag_relay_set)),
|
||||
reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period),
|
||||
work_queue_size: self.args.flag_work_queue_size,
|
||||
@@ -448,12 +465,8 @@ impl Configuration {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn network_id(&self) -> Result<Option<U256>, String> {
|
||||
let net_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref());
|
||||
match net_id {
|
||||
Some(id) => Ok(Some(try!(to_u256(id)))),
|
||||
None => Ok(None),
|
||||
}
|
||||
fn network_id(&self) -> Option<usize> {
|
||||
self.args.flag_network_id.or(self.args.flag_networkid)
|
||||
}
|
||||
|
||||
fn rpc_apis(&self) -> String {
|
||||
@@ -535,10 +548,10 @@ impl Configuration {
|
||||
let dapps_path = replace_home(&self.args.flag_dapps_path);
|
||||
let signer_path = replace_home(&self.args.flag_signer_path);
|
||||
|
||||
if self.args.flag_geth {
|
||||
let geth_path = path::ethereum::default();
|
||||
::std::fs::create_dir_all(geth_path.as_path()).unwrap_or_else(
|
||||
|e| warn!("Failed to create '{}' for geth mode: {}", &geth_path.to_str().unwrap(), e));
|
||||
if self.args.flag_geth && !cfg!(windows) {
|
||||
let geth_root = if self.args.flag_testnet { path::ethereum::test() } else { path::ethereum::default() };
|
||||
::std::fs::create_dir_all(geth_root.as_path()).unwrap_or_else(
|
||||
|e| warn!("Failed to create '{}' for geth mode: {}", &geth_root.to_str().unwrap(), e));
|
||||
}
|
||||
|
||||
Directories {
|
||||
@@ -604,6 +617,7 @@ mod tests {
|
||||
use docopt::Docopt;
|
||||
use ethcore_rpc::NetworkSettings;
|
||||
use ethcore::client::{VMType, BlockID};
|
||||
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
|
||||
use helpers::{replace_home, default_network_config};
|
||||
use run::RunCmd;
|
||||
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat};
|
||||
@@ -683,6 +697,7 @@ mod tests {
|
||||
file_path: Some("blockchain.json".into()),
|
||||
format: Default::default(),
|
||||
pruning: Default::default(),
|
||||
pruning_history: 64,
|
||||
compaction: Default::default(),
|
||||
wal: true,
|
||||
mode: Default::default(),
|
||||
@@ -702,6 +717,7 @@ mod tests {
|
||||
dirs: Default::default(),
|
||||
file_path: Some("blockchain.json".into()),
|
||||
pruning: Default::default(),
|
||||
pruning_history: 64,
|
||||
format: Default::default(),
|
||||
compaction: Default::default(),
|
||||
wal: true,
|
||||
@@ -723,6 +739,7 @@ mod tests {
|
||||
dirs: Default::default(),
|
||||
file_path: Some("blockchain.json".into()),
|
||||
pruning: Default::default(),
|
||||
pruning_history: 64,
|
||||
format: Some(DataFormat::Hex),
|
||||
compaction: Default::default(),
|
||||
wal: true,
|
||||
@@ -750,6 +767,7 @@ mod tests {
|
||||
dirs: Default::default(),
|
||||
spec: Default::default(),
|
||||
pruning: Default::default(),
|
||||
pruning_history: 64,
|
||||
daemon: None,
|
||||
logger_config: Default::default(),
|
||||
miner_options: Default::default(),
|
||||
@@ -777,6 +795,27 @@ mod tests {
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_mining_options() {
|
||||
// given
|
||||
let mut mining_options = MinerOptions::default();
|
||||
|
||||
// when
|
||||
let conf0 = parse(&["parity"]);
|
||||
let conf1 = parse(&["parity", "--tx-queue-strategy", "gas_factor"]);
|
||||
let conf2 = parse(&["parity", "--tx-queue-strategy", "gas_price"]);
|
||||
let conf3 = parse(&["parity", "--tx-queue-strategy", "gas"]);
|
||||
|
||||
// then
|
||||
assert_eq!(conf0.miner_options().unwrap(), mining_options);
|
||||
mining_options.tx_queue_strategy = PrioritizationStrategy::GasFactorAndGasPrice;
|
||||
assert_eq!(conf1.miner_options().unwrap(), mining_options);
|
||||
mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly;
|
||||
assert_eq!(conf2.miner_options().unwrap(), mining_options);
|
||||
mining_options.tx_queue_strategy = PrioritizationStrategy::GasAndGasPrice;
|
||||
assert_eq!(conf3.miner_options().unwrap(), mining_options);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_network_settings() {
|
||||
// given
|
||||
@@ -787,7 +826,7 @@ mod tests {
|
||||
// then
|
||||
assert_eq!(conf.network_settings(), NetworkSettings {
|
||||
name: "testname".to_owned(),
|
||||
chain: "morden".to_owned(),
|
||||
chain: "ropsten".to_owned(),
|
||||
network_port: 30303,
|
||||
rpc_enabled: true,
|
||||
rpc_interface: "local".to_owned(),
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::fs::File;
|
||||
use util::{clean_0x, U256, Uint, Address, path, H256, CompactionProfile};
|
||||
use util::journaldb::Algorithm;
|
||||
use ethcore::client::{Mode, BlockID, Switch, VMType, DatabaseCompactionProfile, ClientConfig};
|
||||
use ethcore::miner::PendingSet;
|
||||
use ethcore::miner::{PendingSet, GasLimit};
|
||||
use cache::CacheConfig;
|
||||
use dir::Directories;
|
||||
use params::Pruning;
|
||||
@@ -94,6 +94,14 @@ pub fn to_pending_set(s: &str) -> Result<PendingSet, String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_gas_limit(s: &str) -> Result<GasLimit, String> {
|
||||
match s {
|
||||
"auto" => Ok(GasLimit::Auto),
|
||||
"off" => Ok(GasLimit::None),
|
||||
other => Ok(GasLimit::Fixed(try!(to_u256(other)))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_address(s: Option<String>) -> Result<Address, String> {
|
||||
match s {
|
||||
Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)),
|
||||
@@ -194,6 +202,7 @@ pub fn to_client_config(
|
||||
mode: Mode,
|
||||
tracing: Switch,
|
||||
pruning: Pruning,
|
||||
pruning_history: u64,
|
||||
compaction: DatabaseCompactionProfile,
|
||||
wal: bool,
|
||||
vm_type: VMType,
|
||||
@@ -221,6 +230,7 @@ pub fn to_client_config(
|
||||
client_config.mode = mode;
|
||||
client_config.tracing.enabled = tracing;
|
||||
client_config.pruning = pruning.to_algorithm(dirs, genesis_hash, fork_name);
|
||||
client_config.history = pruning_history;
|
||||
client_config.db_compaction = compaction;
|
||||
client_config.db_wal = wal;
|
||||
client_config.vm_type = vm_type;
|
||||
|
||||
@@ -81,7 +81,6 @@ mod account;
|
||||
mod blockchain;
|
||||
mod presale;
|
||||
mod run;
|
||||
mod sync;
|
||||
mod snapshot;
|
||||
|
||||
use std::{process, env};
|
||||
@@ -117,11 +116,8 @@ fn start() -> Result<String, String> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// just redirect to the sync::main()
|
||||
if std::env::args().nth(1).map_or(false, |arg| arg == "sync") {
|
||||
sync::main();
|
||||
return;
|
||||
}
|
||||
// Always print backtrace on panic.
|
||||
::std::env::set_var("RUST_BACKTRACE", "1");
|
||||
|
||||
match start() {
|
||||
Ok(result) => {
|
||||
|
||||
@@ -43,13 +43,15 @@ pub enum Error {
|
||||
/// Returned when current version cannot be read or guessed.
|
||||
UnknownDatabaseVersion,
|
||||
/// Migration does not support existing pruning algorithm.
|
||||
UnsuportedPruningMethod,
|
||||
UnsupportedPruningMethod,
|
||||
/// Existing DB is newer than the known one.
|
||||
FutureDBVersion,
|
||||
/// Migration is not possible.
|
||||
MigrationImpossible,
|
||||
/// Migration unexpectadly failed.
|
||||
MigrationFailed,
|
||||
/// Internal migration error.
|
||||
Internal(MigrationError),
|
||||
/// Migration was completed succesfully,
|
||||
/// but there was a problem with io.
|
||||
Io(IoError),
|
||||
@@ -59,10 +61,11 @@ impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
let out = match *self {
|
||||
Error::UnknownDatabaseVersion => "Current database version cannot be read".into(),
|
||||
Error::UnsuportedPruningMethod => "Unsupported pruning method for database migration. Delete DB and resync.".into(),
|
||||
Error::UnsupportedPruningMethod => "Unsupported pruning method for database migration. Delete DB and resync.".into(),
|
||||
Error::FutureDBVersion => "Database was created with newer client version. Upgrade your client or delete DB and resync.".into(),
|
||||
Error::MigrationImpossible => format!("Database migration to version {} is not possible.", CURRENT_VERSION),
|
||||
Error::MigrationFailed => "Database migration unexpectedly failed".into(),
|
||||
Error::Internal(ref err) => format!("{}", err),
|
||||
Error::Io(ref err) => format!("Unexpected io error on DB migration: {}.", err),
|
||||
};
|
||||
|
||||
@@ -80,7 +83,7 @@ impl From<MigrationError> for Error {
|
||||
fn from(err: MigrationError) -> Self {
|
||||
match err {
|
||||
MigrationError::Io(e) => Error::Io(e),
|
||||
_ => Error::MigrationFailed,
|
||||
_ => Error::Internal(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,9 +248,11 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
||||
// Perform pre-consolidation migrations
|
||||
if version < CONSOLIDATION_VERSION && exists(&legacy::blocks_database_path(path)) {
|
||||
println!("Migrating database from version {} to {}", version, CONSOLIDATION_VERSION);
|
||||
try!(migrate_database(version, legacy::blocks_database_path(path), try!(legacy::blocks_database_migrations(&compaction_profile))));
|
||||
|
||||
try!(migrate_database(version, legacy::extras_database_path(path), try!(legacy::extras_database_migrations(&compaction_profile))));
|
||||
try!(migrate_database(version, legacy::state_database_path(path), try!(legacy::state_database_migrations(pruning, &compaction_profile))));
|
||||
try!(migrate_database(version, legacy::blocks_database_path(path), try!(legacy::blocks_database_migrations(&compaction_profile))));
|
||||
|
||||
let db_path = consolidated_database_path(path);
|
||||
// Remove the database dir (it shouldn't exist anyway, but it might when migration was interrupted)
|
||||
let _ = fs::remove_dir_all(db_path.clone());
|
||||
@@ -263,6 +268,9 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
||||
println!("Migration finished");
|
||||
}
|
||||
|
||||
// update version so we can apply post-consolidation migrations.
|
||||
let version = ::std::cmp::max(CONSOLIDATION_VERSION, version);
|
||||
|
||||
// Further migrations
|
||||
if version >= CONSOLIDATION_VERSION && version < CURRENT_VERSION && exists(&consolidated_database_path(path)) {
|
||||
println!("Migrating database from version {} to {}", ::std::cmp::max(CONSOLIDATION_VERSION, version), CURRENT_VERSION);
|
||||
@@ -270,10 +278,13 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
||||
println!("Migration finished");
|
||||
}
|
||||
|
||||
// update version file.
|
||||
try!(update_version(path));
|
||||
|
||||
// run any inplace migrations for the fully upgraded database
|
||||
try!(run_inplace_upgrades(consolidated_database_path(path).as_path()));
|
||||
|
||||
// update version file.
|
||||
update_version(path)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Old migrations utilities
|
||||
@@ -333,7 +344,7 @@ mod legacy {
|
||||
let res = match pruning {
|
||||
Algorithm::Archive => manager.add_migration(migrations::state::ArchiveV7::default()),
|
||||
Algorithm::OverlayRecent => manager.add_migration(migrations::state::OverlayRecentV7::default()),
|
||||
_ => return Err(Error::UnsuportedPruningMethod),
|
||||
_ => return Err(Error::UnsupportedPruningMethod),
|
||||
};
|
||||
|
||||
try!(res.map_err(|_| Error::MigrationImpossible));
|
||||
|
||||
@@ -25,10 +25,6 @@ use self::ipc_deps::*;
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
|
||||
pub mod service_urls {
|
||||
pub const CLIENT: &'static str = "ipc:///tmp/parity-chain.ipc";
|
||||
pub const SYNC: &'static str = "ipc:///tmp/parity-sync.ipc";
|
||||
pub const SYNC_NOTIFY: &'static str = "ipc:///tmp/parity-sync-notify.ipc";
|
||||
pub const NETWORK_MANAGER: &'static str = "ipc:///tmp/parity-manage-net.ipc";
|
||||
}
|
||||
|
||||
#[cfg(not(feature="ipc"))]
|
||||
|
||||
@@ -28,6 +28,7 @@ use dir::Directories;
|
||||
pub enum SpecType {
|
||||
Mainnet,
|
||||
Testnet,
|
||||
Ropsten,
|
||||
Olympic,
|
||||
Classic,
|
||||
Custom(String),
|
||||
@@ -47,6 +48,7 @@ impl FromStr for SpecType {
|
||||
"frontier" | "homestead" | "mainnet" => SpecType::Mainnet,
|
||||
"frontier-dogmatic" | "homestead-dogmatic" | "classic" => SpecType::Classic,
|
||||
"morden" | "testnet" => SpecType::Testnet,
|
||||
"ropsten" => SpecType::Ropsten,
|
||||
"olympic" => SpecType::Olympic,
|
||||
other => SpecType::Custom(other.into()),
|
||||
};
|
||||
@@ -59,6 +61,7 @@ impl SpecType {
|
||||
match *self {
|
||||
SpecType::Mainnet => Ok(ethereum::new_frontier()),
|
||||
SpecType::Testnet => Ok(ethereum::new_morden()),
|
||||
SpecType::Ropsten => Ok(ethereum::new_ropsten()),
|
||||
SpecType::Olympic => Ok(ethereum::new_olympic()),
|
||||
SpecType::Classic => Ok(ethereum::new_classic()),
|
||||
SpecType::Custom(ref file) => Ok(Spec::load(&try!(contents(file).map_err(|_| "Could not load specification file."))))
|
||||
@@ -215,7 +218,7 @@ impl Default for MinerExtras {
|
||||
extra_data: version_data(),
|
||||
gas_floor_target: U256::from(4_700_000),
|
||||
gas_ceil_target: U256::from(6_283_184),
|
||||
transactions_limit: 1024,
|
||||
transactions_limit: 2048,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,6 +235,7 @@ mod tests {
|
||||
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, "ropsten".parse().unwrap());
|
||||
assert_eq!(SpecType::Olympic, "olympic".parse().unwrap());
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ use fdlimit::raise_fd_limit;
|
||||
use ethcore_logger::{Config as LogConfig, setup_log};
|
||||
use ethcore_rpc::NetworkSettings;
|
||||
use ethsync::NetworkConfiguration;
|
||||
use util::{Colour, version, U256};
|
||||
use util::{Colour, version};
|
||||
use io::{MayPanic, ForwardPanic, PanicHandler};
|
||||
use ethcore::client::{Mode, Switch, DatabaseCompactionProfile, VMType, ChainNotify};
|
||||
use ethcore::service::ClientService;
|
||||
@@ -52,6 +52,7 @@ pub struct RunCmd {
|
||||
pub dirs: Directories,
|
||||
pub spec: SpecType,
|
||||
pub pruning: Pruning,
|
||||
pub pruning_history: u64,
|
||||
/// Some if execution should be daemonized. Contains pid_file path.
|
||||
pub daemon: Option<String>,
|
||||
pub logger_config: LogConfig,
|
||||
@@ -59,7 +60,7 @@ pub struct RunCmd {
|
||||
pub http_conf: HttpConfiguration,
|
||||
pub ipc_conf: IpcConfiguration,
|
||||
pub net_conf: NetworkConfiguration,
|
||||
pub network_id: Option<U256>,
|
||||
pub network_id: Option<usize>,
|
||||
pub acc_conf: AccountsConfig,
|
||||
pub gas_pricer: GasPricerConfig,
|
||||
pub miner_extras: MinerExtras,
|
||||
@@ -79,13 +80,17 @@ pub struct RunCmd {
|
||||
pub custom_bootnodes: bool,
|
||||
}
|
||||
|
||||
pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
||||
pub fn execute(mut cmd: RunCmd) -> Result<(), String> {
|
||||
// increase max number of open files
|
||||
raise_fd_limit();
|
||||
|
||||
// set up logger
|
||||
let logger = try!(setup_log(&cmd.logger_config));
|
||||
|
||||
if cmd.dapps_conf.enabled {
|
||||
warn!("Warning: Parity UI is disabled in this release. Please upgrade to version 1.4.2 or higher.");
|
||||
cmd.dapps_conf.enabled = false;
|
||||
}
|
||||
// set up panic handler
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
@@ -149,6 +154,7 @@ pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
||||
cmd.mode,
|
||||
cmd.tracing,
|
||||
cmd.pruning,
|
||||
cmd.pruning_history,
|
||||
cmd.compaction,
|
||||
cmd.wal,
|
||||
cmd.vm_type,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user