Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e298ab30e4 | ||
|
|
63d3a4dd59 | ||
|
|
0e55b6c6c9 | ||
|
|
4d115987cb | ||
|
|
2d623f14db | ||
|
|
1d1a3b9d02 | ||
|
|
1d69b0e124 | ||
|
|
3c59475be6 | ||
|
|
facab31551 | ||
|
|
c9cfcd2728 | ||
|
|
557dbb73b8 | ||
|
|
6f772a28e2 | ||
|
|
16f3119547 | ||
|
|
c3741640f7 | ||
|
|
decca7f698 | ||
|
|
fc53726c7f | ||
|
|
e2b81bc08d | ||
|
|
4d2d1eac1f | ||
|
|
5572e3dd1d | ||
|
|
0db1c0d336 | ||
|
|
905f04dc9d | ||
|
|
f560cf360c | ||
|
|
f5d48cbf2a | ||
|
|
d59e2ecbc6 | ||
|
|
a97ca5aaf4 | ||
|
|
df61b1b328 | ||
|
|
52a69d19e6 | ||
|
|
33084aaa07 | ||
|
|
2a82fa0a47 | ||
|
|
1ccc90108c | ||
|
|
e60c5b59a6 | ||
|
|
b199fbb6c6 | ||
|
|
d9d506db57 | ||
|
|
425e1ce04c | ||
|
|
8741d0d155 | ||
|
|
dfb2ddfdc2 | ||
|
|
52ac5a00f5 | ||
|
|
6336bdecf3 |
178
.gitlab-ci.yml
178
.gitlab-ci.yml
@@ -1,6 +1,6 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- deploy
|
- test
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: "3"
|
GIT_DEPTH: "3"
|
||||||
SIMPLECOV: "true"
|
SIMPLECOV: "true"
|
||||||
@@ -8,9 +8,56 @@ variables:
|
|||||||
cache:
|
cache:
|
||||||
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
||||||
untracked: true
|
untracked: true
|
||||||
|
linux-stable:
|
||||||
|
stage: build
|
||||||
|
image: ethcore/rust:stable
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
|
script:
|
||||||
|
- cargo build --release --verbose
|
||||||
|
- strip target/release/parity
|
||||||
|
- 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
|
||||||
|
tags:
|
||||||
|
- rust
|
||||||
|
- rust-stable
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- target/release/parity
|
||||||
|
name: "stable-x86_64-unknown-linux-gnu_parity"
|
||||||
|
linux-stable-14.04:
|
||||||
|
stage: build
|
||||||
|
image: ethcore/rust-14.04:latest
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
|
script:
|
||||||
|
- cargo build --release --verbose
|
||||||
|
- strip target/release/parity
|
||||||
|
- 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
|
||||||
|
tags:
|
||||||
|
- rust
|
||||||
|
- rust-14.04
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- target/release/parity
|
||||||
|
name: "stable-x86_64-unknown-ubuntu_14_04-gnu_parity"
|
||||||
linux-beta:
|
linux-beta:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust:beta
|
image: ethcore/rust:beta
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
@@ -20,23 +67,16 @@ linux-beta:
|
|||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity
|
- target/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "beta-x86_64-unknown-linux-gnu_parity"
|
||||||
linux-stable:
|
allow_failure: true
|
||||||
stage: build
|
|
||||||
image: ethcore/rust:stable
|
|
||||||
script:
|
|
||||||
- cargo build --release --verbose
|
|
||||||
- strip target/release/parity
|
|
||||||
tags:
|
|
||||||
- rust
|
|
||||||
- rust-stable
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- target/release/parity
|
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
|
||||||
linux-nightly:
|
linux-nightly:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust:nightly
|
image: ethcore/rust:nightly
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
@@ -46,121 +86,189 @@ linux-nightly:
|
|||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity
|
- target/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "nigthly-x86_64-unknown-linux-gnu_parity"
|
||||||
|
allow_failure: true
|
||||||
linux-centos:
|
linux-centos:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-centos:latest
|
image: ethcore/rust-centos:latest
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- export CXX="g++"
|
- export CXX="g++"
|
||||||
- export CC="gcc"
|
- export CC="gcc"
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-centos
|
- rust-centos
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity
|
- target/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "x86_64-unknown-centos-gnu_parity"
|
||||||
linux-armv7:
|
linux-armv7:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-arm:latest
|
image: ethcore/rust-armv7:latest
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- export CXX=arm-linux-gnueabihf-g++
|
- rm -rf .cargo
|
||||||
- export CC=arm-linux-gnueabihf-gcc
|
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
||||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
||||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/armv7-unknown-linux-gnueabihf/release/parity
|
- target/armv7-unknown-linux-gnueabihf/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "armv7_unknown_linux_gnueabihf_parity"
|
||||||
|
allow_failure: true
|
||||||
linux-arm:
|
linux-arm:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-arm:latest
|
image: ethcore/rust-arm:latest
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- export CXX=arm-linux-gnueabihf-g++
|
- rm -rf .cargo
|
||||||
- export CC=arm-linux-gnueabihf-gcc
|
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
||||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
||||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/arm-unknown-linux-gnueabihf/release/parity
|
- target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "arm-unknown-linux-gnueabihf_parity"
|
||||||
|
allow_failure: true
|
||||||
linux-armv6:
|
linux-armv6:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-arm:latest
|
image: ethcore/rust-armv6:latest
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- export CXX=arm-linux-gnueabi-g++
|
- rm -rf .cargo
|
||||||
- export CC=arm-linux-gnueabi-gcc
|
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
||||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
||||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/arm-unknown-linux-gnueabi/release/parity
|
- target/arm-unknown-linux-gnueabi/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "arm-unknown-linux-gnueabi_parity"
|
||||||
|
allow_failure: true
|
||||||
linux-aarch64:
|
linux-aarch64:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-arm:latest
|
image: ethcore/rust-aarch64:latest
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- export CXX=aarch64-linux-gnu-g++
|
- rm -rf .cargo
|
||||||
- export CC=aarch64-linux-gnu-gcc
|
|
||||||
- mkdir -p .cargo
|
- mkdir -p .cargo
|
||||||
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
||||||
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
||||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/aarch64-unknown-linux-gnu/release/parity
|
- target/aarch64-unknown-linux-gnu/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "aarch64-unknown-linux-gnu_parity"
|
||||||
|
allow_failure: true
|
||||||
darwin:
|
darwin:
|
||||||
stage: build
|
stage: build
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- osx
|
- osx
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity
|
- target/release/parity
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "x86_64-apple-darwin_parity"
|
||||||
windows:
|
windows:
|
||||||
stage: build
|
stage: build
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
script:
|
script:
|
||||||
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
|
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
|
||||||
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
|
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
|
||||||
- set RUST_BACKTRACE=1
|
- set RUST_BACKTRACE=1
|
||||||
- SET
|
|
||||||
- rustup default stable-x86_64-pc-windows-msvc
|
- rustup default stable-x86_64-pc-windows-msvc
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
|
- 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
|
||||||
tags:
|
tags:
|
||||||
- rust-windows
|
- rust-windows
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity.exe
|
- target/release/parity.exe
|
||||||
- target/release/parity.pdb
|
- target/release/parity.pdb
|
||||||
name: "${CI_BUILD_NAME}_parity"
|
name: "x86_64-pc-windows-msvc_parity"
|
||||||
|
test-linux:
|
||||||
|
stage: test
|
||||||
|
before_script:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
script:
|
||||||
|
- export RUST_BACKTRACE=1
|
||||||
|
- ./test.sh --verbose
|
||||||
|
tags:
|
||||||
|
- rust-test
|
||||||
|
dependencies:
|
||||||
|
- linux-stable
|
||||||
|
|||||||
236
Cargo.lock
generated
236
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
|||||||
[root]
|
[root]
|
||||||
name = "parity"
|
name = "parity"
|
||||||
version = "1.3.0"
|
version = "1.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -20,7 +20,7 @@ dependencies = [
|
|||||||
"ethcore-logger 1.3.0",
|
"ethcore-logger 1.3.0",
|
||||||
"ethcore-rpc 1.3.0",
|
"ethcore-rpc 1.3.0",
|
||||||
"ethcore-signer 1.3.0",
|
"ethcore-signer 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"ethsync 1.3.0",
|
"ethsync 1.3.0",
|
||||||
"fdlimit 0.1.0",
|
"fdlimit 0.1.0",
|
||||||
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -112,9 +112,18 @@ name = "bloomchain"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bloomfilter"
|
||||||
|
version = "0.0.10"
|
||||||
|
source = "git+https://github.com/ethcore/rust-bloom-filter#e66a3f20443bc78810877390ad4da00ebdf74d78"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "0.5.2"
|
version = "0.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -122,6 +131,14 @@ name = "bytes"
|
|||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "0.4.0-dev"
|
||||||
|
source = "git+https://github.com/carllerche/bytes#6529f6392a9717585b8d67e1db96e6fa0fb6cb1f"
|
||||||
|
dependencies = [
|
||||||
|
"stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -155,7 +172,7 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -241,6 +258,8 @@ version = "1.3.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bloomfilter 0.0.10 (git+https://github.com/ethcore/rust-bloom-filter)",
|
||||||
|
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -250,13 +269,15 @@ dependencies = [
|
|||||||
"ethcore-ipc 1.3.0",
|
"ethcore-ipc 1.3.0",
|
||||||
"ethcore-ipc-codegen 1.3.0",
|
"ethcore-ipc-codegen 1.3.0",
|
||||||
"ethcore-ipc-nano 1.3.0",
|
"ethcore-ipc-nano 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"ethstore 0.1.0",
|
"ethstore 0.1.0",
|
||||||
|
"evmjit 1.3.0",
|
||||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -272,23 +293,23 @@ version = "1.3.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-rpc 1.3.0",
|
"ethcore-rpc 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
"parity-dapps-home 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
"parity-dapps-status 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
"parity-dapps-wallet 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.9 (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)",
|
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -314,7 +335,7 @@ name = "ethcore-ipc"
|
|||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-devtools 1.3.0",
|
"ethcore-devtools 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -359,7 +380,7 @@ dependencies = [
|
|||||||
"ethcore-ipc 1.3.0",
|
"ethcore-ipc 1.3.0",
|
||||||
"ethcore-ipc-codegen 1.3.0",
|
"ethcore-ipc-codegen 1.3.0",
|
||||||
"ethcore-ipc-nano 1.3.0",
|
"ethcore-ipc-nano 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -371,7 +392,7 @@ name = "ethcore-logger"
|
|||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -386,7 +407,7 @@ dependencies = [
|
|||||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.3.0",
|
"ethcore-devtools 1.3.0",
|
||||||
"ethcore-io 1.3.0",
|
"ethcore-io 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.12 (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)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -410,11 +431,11 @@ dependencies = [
|
|||||||
"ethcore-devtools 1.3.0",
|
"ethcore-devtools 1.3.0",
|
||||||
"ethcore-io 1.3.0",
|
"ethcore-io 1.3.0",
|
||||||
"ethcore-ipc 1.3.0",
|
"ethcore-ipc 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
"ethsync 1.3.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)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -433,18 +454,18 @@ dependencies = [
|
|||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-io 1.3.0",
|
"ethcore-io 1.3.0",
|
||||||
"ethcore-rpc 1.3.0",
|
"ethcore-rpc 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps-signer 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ws 0.5.0 (git+https://github.com/ethcore/ws-rs.git?branch=stable)",
|
"ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-util"
|
name = "ethcore-util"
|
||||||
version = "1.3.0"
|
version = "1.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -477,7 +498,7 @@ dependencies = [
|
|||||||
name = "ethjson"
|
name = "ethjson"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.9 (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)",
|
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -525,7 +546,7 @@ dependencies = [
|
|||||||
"ethcore-ipc-codegen 1.3.0",
|
"ethcore-ipc-codegen 1.3.0",
|
||||||
"ethcore-ipc-nano 1.3.0",
|
"ethcore-ipc-nano 1.3.0",
|
||||||
"ethcore-network 1.3.0",
|
"ethcore-network 1.3.0",
|
||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.2",
|
||||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -534,6 +555,13 @@ dependencies = [
|
|||||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "evmjit"
|
||||||
|
version = "1.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdlimit"
|
name = "fdlimit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -614,7 +642,7 @@ dependencies = [
|
|||||||
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -635,7 +663,7 @@ dependencies = [
|
|||||||
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -682,23 +710,23 @@ source = "git+https://github.com/ethcore/json-ipc-server.git#56b6307130710ebc73c
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-core"
|
name = "jsonrpc-core"
|
||||||
version = "2.0.7"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.9 (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)",
|
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -707,7 +735,7 @@ 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#4e3f93eb79125e91a46e04d77c25ff8885498b86"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -735,11 +763,24 @@ name = "libc"
|
|||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru-cache"
|
||||||
|
version = "0.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matches"
|
name = "matches"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -780,7 +821,7 @@ dependencies = [
|
|||||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.12 (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)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -796,7 +837,7 @@ dependencies = [
|
|||||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.12 (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)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -804,9 +845,24 @@ dependencies = [
|
|||||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.6.0-dev"
|
||||||
|
source = "git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d#62ec763c9cc34d8a452ed0392c575c50ddd5fc8d"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (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)",
|
||||||
|
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
|
||||||
|
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miow"
|
name = "miow"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -854,6 +910,19 @@ dependencies = [
|
|||||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nodrop"
|
name = "nodrop"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -956,8 +1025,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps"
|
name = "parity-dapps"
|
||||||
version = "0.6.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
source = "git+https://github.com/ethcore/parity-ui.git#e4dddf36e7c9fa5c6e746790119c71f67438784a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -970,34 +1039,34 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-home"
|
name = "parity-dapps-home"
|
||||||
version = "0.6.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
source = "git+https://github.com/ethcore/parity-ui.git#e4dddf36e7c9fa5c6e746790119c71f67438784a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-signer"
|
name = "parity-dapps-signer"
|
||||||
version = "0.6.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
source = "git+https://github.com/ethcore/parity-ui.git#e4dddf36e7c9fa5c6e746790119c71f67438784a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-status"
|
name = "parity-dapps-status"
|
||||||
version = "0.6.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
source = "git+https://github.com/ethcore/parity-ui.git#e4dddf36e7c9fa5c6e746790119c71f67438784a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-wallet"
|
name = "parity-dapps-wallet"
|
||||||
version = "0.6.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
source = "git+https://github.com/ethcore/parity-ui.git#e4dddf36e7c9fa5c6e746790119c71f67438784a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1152,7 +1221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb"
|
name = "rocksdb"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
source = "git+https://github.com/ethcore/rust-rocksdb#eadce7f74cfe92b99ce63a77af425b47857239b8"
|
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||||
@@ -1161,7 +1230,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb-sys"
|
name = "rocksdb-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/ethcore/rust-rocksdb#eadce7f74cfe92b99ce63a77af425b47857239b8"
|
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1255,11 +1324,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
|
||||||
"byteorder 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha3"
|
name = "sha3"
|
||||||
@@ -1273,6 +1339,11 @@ name = "slab"
|
|||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "git+https://github.com/carllerche/slab?rev=5476efcafb#5476efcafbc5ef4d7315b1bea3f756d8a1fe975e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -1297,6 +1368,11 @@ name = "spmc"
|
|||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable-heap"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47#3c5cd1ca4706f167a1de85658b5af0d6d3e65165"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -1447,7 +1523,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1506,15 +1582,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
version = "0.5.0"
|
version = "0.5.2"
|
||||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=stable#a876fc115c3ef50a17c8822c9bd2f6e94473e005"
|
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#afbff59776ce16ccec5ee9e218b8891830ee6fdf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
||||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
"mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
|
||||||
|
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1549,16 +1627,13 @@ dependencies = [
|
|||||||
"checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a"
|
"checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a"
|
||||||
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
||||||
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
||||||
"checksum bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "32866f4d103c4e438b1db1158aa1b1a80ee078e5d77a59a2f906fd62a577389c"
|
|
||||||
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
|
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
|
||||||
"checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23"
|
"checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23"
|
||||||
"checksum bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72cd7314bd4ee024071241147222c706e80385a1605ac7d4cd2fcc339da2ae46"
|
|
||||||
"checksum blastfig 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09640e0509d97d5cdff03a9f5daf087a8e04c735c3b113a75139634a19cfc7b2"
|
"checksum blastfig 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09640e0509d97d5cdff03a9f5daf087a8e04c735c3b113a75139634a19cfc7b2"
|
||||||
"checksum bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f421095d2a76fc24cd3fb3f912b90df06be7689912b1bdb423caefae59c258d"
|
"checksum bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f421095d2a76fc24cd3fb3f912b90df06be7689912b1bdb423caefae59c258d"
|
||||||
"checksum byteorder 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e68d0b3b234a583993a53d5b0063fb5fe8713590fe733d41b98a2cee6a9c26e"
|
|
||||||
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
||||||
|
"checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "<none>"
|
||||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||||
"checksum chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "a714b6792cb4bb07643c35d2a051d92988d4e296322a60825549dd0764bcd396"
|
|
||||||
"checksum clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "e96469b413984c78285727f94f9c626a1f2006cecdcf813b5d6893c0c85df42f"
|
"checksum clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "e96469b413984c78285727f94f9c626a1f2006cecdcf813b5d6893c0c85df42f"
|
||||||
"checksum clippy_lints 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "f11938c4b10c556903bb1c1e717eb038658324bf7197e4cfc159a16417327345"
|
"checksum clippy_lints 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "f11938c4b10c556903bb1c1e717eb038658324bf7197e4cfc159a16417327345"
|
||||||
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
||||||
@@ -1577,30 +1652,34 @@ dependencies = [
|
|||||||
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
|
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
|
||||||
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
|
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
|
||||||
"checksum hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0f4d00bb781e559b6e66ae4b5479df0fdf9ab15949f52fa2f1f5de16d4cc07"
|
"checksum hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0f4d00bb781e559b6e66ae4b5479df0fdf9ab15949f52fa2f1f5de16d4cc07"
|
||||||
|
"checksum hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "eb27e8a3e8f17ac43ffa41bbda9cf5ad3f9f13ef66fa4873409d4902310275f7"
|
||||||
"checksum hyper 0.9.4 (git+https://github.com/ethcore/hyper)" = "<none>"
|
"checksum hyper 0.9.4 (git+https://github.com/ethcore/hyper)" = "<none>"
|
||||||
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
|
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
|
||||||
"checksum igd 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5b93df68d6152576e9bc9f371e33e00b40738d528b3566ff41ea11d04401dc"
|
"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 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 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)" = "<none>"
|
||||||
"checksum jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "91755680900913f73576065c85359ee793ac3883bc461dbca90fc4a603be84cc"
|
"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)" = "<none>"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||||
"checksum libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "97def9dc7ce1d8e153e693e3a33020bc69972181adb2f871e87e888876feae49"
|
"checksum libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "97def9dc7ce1d8e153e693e3a33020bc69972181adb2f871e87e888876feae49"
|
||||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||||
|
"checksum lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "42d50dcb5d9f145df83b1043207e1ac0c37c9c779c4e128ca4655abc3f3cbf8c"
|
||||||
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
||||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||||
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
||||||
"checksum mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e50bf542f81754ef69e5cea856946a3819f7c09ea97b4903c8bc8a89f74e7b6"
|
"checksum mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e50bf542f81754ef69e5cea856946a3819f7c09ea97b4903c8bc8a89f74e7b6"
|
||||||
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
|
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
|
||||||
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
||||||
"checksum miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4e93d633d34b8ff65a24566d67d49703e7a5c7ac2844d6139a9fc441a799e89a"
|
"checksum mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)" = "<none>"
|
||||||
|
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
||||||
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||||
"checksum nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
"checksum nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||||
"checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204"
|
"checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204"
|
||||||
"checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e"
|
"checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e"
|
||||||
|
"checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2"
|
||||||
"checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f"
|
"checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f"
|
||||||
"checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e"
|
"checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e"
|
||||||
"checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2"
|
"checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2"
|
||||||
@@ -1613,11 +1692,11 @@ dependencies = [
|
|||||||
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
||||||
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
||||||
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
||||||
"checksum parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parity-dapps-home 0.6.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parity-dapps-signer 0.6.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parity-dapps-status 0.6.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parity-dapps-wallet 0.6.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0fd1be2c3cf5fef20a6d18fec252c4f3c87c14fc3039002eb7d4ed91e436826"
|
"checksum parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0fd1be2c3cf5fef20a6d18fec252c4f3c87c14fc3039002eb7d4ed91e436826"
|
||||||
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
||||||
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
||||||
@@ -1648,12 +1727,14 @@ dependencies = [
|
|||||||
"checksum serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b76133a8a02f1c6ebd3fb9a2ecaab3d54302565a51320e80931adba571aadb1b"
|
"checksum serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b76133a8a02f1c6ebd3fb9a2ecaab3d54302565a51320e80931adba571aadb1b"
|
||||||
"checksum serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c5b74ff4fb34013cc0b917dd182fefc05ee9af233b9d0d557078334554284d0e"
|
"checksum serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c5b74ff4fb34013cc0b917dd182fefc05ee9af233b9d0d557078334554284d0e"
|
||||||
"checksum serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c88a751caa8f0000058fb971cd443ed2e6b653f33f5a47f29892a8bd44ca4c1"
|
"checksum serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c88a751caa8f0000058fb971cd443ed2e6b653f33f5a47f29892a8bd44ca4c1"
|
||||||
"checksum sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a307a40d5834140e4213a6952483b84e9ad53bdcab918b7335a6e305e505a53c"
|
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||||
|
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
||||||
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
||||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||||
|
"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.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825"
|
||||||
"checksum syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393b6dd0889df2b064beeea954cfda6bc2571604ac460deeae0fed55a53988af"
|
"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 syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44bded3cabafc65c90b663b1071bd2d198a9ab7515e6ce729e4570aaf53c407e"
|
||||||
@@ -1673,7 +1754,7 @@ dependencies = [
|
|||||||
"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
|
"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
|
||||||
"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb"
|
"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb"
|
||||||
"checksum url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f6d04073d0fcd045a1cf57aea560d1be5ba812d8f28814e1e1cf0e90ff4d2f03"
|
"checksum url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f6d04073d0fcd045a1cf57aea560d1be5ba812d8f28814e1e1cf0e90ff4d2f03"
|
||||||
"checksum url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8ab4ca6f0107350f41a59a51cb0e71a04d905bc6a29181d2cb42fa4f040c65c9"
|
"checksum url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afe9ec54bc4db14bc8744b7fed060d785ac756791450959b2248443319d5b119"
|
||||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||||
"checksum uuid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9767696a9e1bc7a73f2d5f8e0f5428b076cecd9199c200c0364aa0b2d57b8dfa"
|
"checksum uuid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9767696a9e1bc7a73f2d5f8e0f5428b076cecd9199c200c0364aa0b2d57b8dfa"
|
||||||
"checksum vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0795a11576d29ae80525a3fda315bf7b534f8feb9d34101e5fe63fb95bb2fd24"
|
"checksum vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0795a11576d29ae80525a3fda315bf7b534f8feb9d34101e5fe63fb95bb2fd24"
|
||||||
@@ -1681,8 +1762,7 @@ dependencies = [
|
|||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
||||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
"checksum ws 0.5.0 (git+https://github.com/ethcore/ws-rs.git?branch=stable)" = "<none>"
|
"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||||
"checksum xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac8fd82b24db2dd3b54aa7b29f336d8b5ca1830065ce3aada71bce6f661519"
|
"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"
|
||||||
"checksum xml-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f11ef7864e55d06a38755beaf03ab70139a04e619acfe94ef800b11bd79eb52c"
|
|
||||||
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
|
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Ethcore client."
|
description = "Ethcore client."
|
||||||
name = "parity"
|
name = "parity"
|
||||||
version = "1.3.0"
|
version = "1.3.2"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Ethcore <admin@ethcore.io>"]
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
@@ -59,6 +59,7 @@ ui = ["dapps", "ethcore-signer/ui"]
|
|||||||
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
||||||
dapps = ["ethcore-dapps"]
|
dapps = ["ethcore-dapps"]
|
||||||
ipc = ["ethcore/ipc"]
|
ipc = ["ethcore/ipc"]
|
||||||
|
jit = ["ethcore/jit"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||||
json-tests = ["ethcore/json-tests"]
|
json-tests = ["ethcore/json-tests"]
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ build = "build.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
jsonrpc-core = "2.0"
|
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" }
|
||||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||||
unicase = "1.3"
|
unicase = "1.3"
|
||||||
@@ -21,11 +21,11 @@ serde_json = "0.7.0"
|
|||||||
serde_macros = { version = "0.7.0", optional = true }
|
serde_macros = { version = "0.7.0", optional = true }
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "0.6" }
|
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
# List of apps
|
# List of apps
|
||||||
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "0.6" }
|
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "0.6" }
|
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "0.6", optional = true }
|
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true }
|
||||||
mime_guess = { version = "1.6.1" }
|
mime_guess = { version = "1.6.1" }
|
||||||
clippy = { version = "0.0.80", optional = true}
|
clippy = { version = "0.0.80", optional = true}
|
||||||
|
|
||||||
|
|||||||
@@ -100,14 +100,26 @@ impl ServerBuilder {
|
|||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// returns result with `Server` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone())
|
Server::start_http(
|
||||||
|
addr,
|
||||||
|
hosts,
|
||||||
|
NoAuth,
|
||||||
|
self.handler.clone(),
|
||||||
|
self.dapps_path.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||||
/// return result with `Server` handle on success or an error.
|
/// return result with `Server` handle on success or an error.
|
||||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
|
pub fn start_basic_auth_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone())
|
Server::start_http(
|
||||||
|
addr,
|
||||||
|
hosts,
|
||||||
|
HttpBasicAuth::single_user(username, password),
|
||||||
|
self.handler.clone(),
|
||||||
|
self.dapps_path.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +130,28 @@ pub struct Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>, dapps_path: String) -> Result<Server, ServerError> {
|
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
|
||||||
|
fn allowed_hosts(hosts: Option<Vec<String>>, bind_address: String) -> Option<Vec<String>> {
|
||||||
|
let mut allowed = Vec::new();
|
||||||
|
|
||||||
|
match hosts {
|
||||||
|
Some(hosts) => allowed.extend_from_slice(&hosts),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add localhost domain as valid too if listening on loopback interface.
|
||||||
|
allowed.push(bind_address.replace("127.0.0.1", "localhost").into());
|
||||||
|
allowed.push(bind_address.into());
|
||||||
|
Some(allowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_http<A: Authorization + 'static>(
|
||||||
|
addr: &SocketAddr,
|
||||||
|
hosts: Option<Vec<String>>,
|
||||||
|
authorization: A,
|
||||||
|
handler: Arc<IoHandler>,
|
||||||
|
dapps_path: String,
|
||||||
|
) -> Result<Server, ServerError> {
|
||||||
let panic_handler = Arc::new(Mutex::new(None));
|
let panic_handler = Arc::new(Mutex::new(None));
|
||||||
let authorization = Arc::new(authorization);
|
let authorization = Arc::new(authorization);
|
||||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
||||||
@@ -129,7 +162,7 @@ impl Server {
|
|||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
let bind_address = format!("{}", addr);
|
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||||
|
|
||||||
try!(hyper::Server::http(addr))
|
try!(hyper::Server::http(addr))
|
||||||
.handle(move |_| router::Router::new(
|
.handle(move |_| router::Router::new(
|
||||||
@@ -137,7 +170,7 @@ impl Server {
|
|||||||
endpoints.clone(),
|
endpoints.clone(),
|
||||||
special.clone(),
|
special.clone(),
|
||||||
authorization.clone(),
|
authorization.clone(),
|
||||||
bind_address.clone(),
|
hosts.clone(),
|
||||||
))
|
))
|
||||||
.map(|(l, srv)| {
|
.map(|(l, srv)| {
|
||||||
|
|
||||||
@@ -182,3 +215,24 @@ impl From<hyper::error::Error> for ServerError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Server;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_allowed_hosts() {
|
||||||
|
// given
|
||||||
|
let bind_address = "127.0.0.1".to_owned();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let all = Server::allowed_hosts(None, bind_address.clone());
|
||||||
|
let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone());
|
||||||
|
let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone());
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(all, None);
|
||||||
|
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
||||||
|
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,14 +22,11 @@ use hyper::net::HttpStream;
|
|||||||
use jsonrpc_http_server::{is_host_header_valid};
|
use jsonrpc_http_server::{is_host_header_valid};
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
|
|
||||||
|
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
|
||||||
pub fn is_valid(request: &server::Request<HttpStream>, bind_address: &str, endpoints: Vec<String>) -> bool {
|
let mut endpoints = endpoints.iter()
|
||||||
let mut endpoints = endpoints.into_iter()
|
|
||||||
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
// Add localhost domain as valid too if listening on loopback interface.
|
endpoints.extend_from_slice(allowed_hosts);
|
||||||
endpoints.push(bind_address.replace("127.0.0.1", "localhost").into());
|
|
||||||
endpoints.push(bind_address.into());
|
|
||||||
|
|
||||||
is_host_header_valid(request, &endpoints)
|
is_host_header_valid(request, &endpoints)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ pub struct Router<A: Authorization + 'static> {
|
|||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
bind_address: String,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
handler: Box<server::Handler<HttpStream> + Send>,
|
handler: Box<server::Handler<HttpStream> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,9 +53,11 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
|
|
||||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||||
// Validate Host header
|
// Validate Host header
|
||||||
if !host_validation::is_valid(&req, &self.bind_address, self.endpoints.keys().cloned().collect()) {
|
if let Some(ref hosts) = self.allowed_hosts {
|
||||||
self.handler = host_validation::host_invalid_response();
|
if !host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()) {
|
||||||
return self.handler.on_request(req);
|
self.handler = host_validation::host_invalid_response();
|
||||||
|
return self.handler.on_request(req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check authorization
|
// Check authorization
|
||||||
@@ -114,7 +116,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
bind_address: String,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
||||||
@@ -123,7 +125,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
special: special,
|
special: special,
|
||||||
authorization: authorization,
|
authorization: authorization,
|
||||||
bind_address: bind_address,
|
allowed_hosts: allowed_hosts,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,9 @@ RUN rustup target add aarch64-unknown-linux-gnu
|
|||||||
# show backtraces
|
# show backtraces
|
||||||
ENV RUST_BACKTRACE 1
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
# set compilers
|
|
||||||
ENV CXX aarch64-linux-gnu-g++
|
|
||||||
ENV CC aarch64-linux-gnu-gcc
|
|
||||||
|
|
||||||
# show tools
|
# show tools
|
||||||
RUN rustc -vV && \
|
RUN rustc -vV && \
|
||||||
cargo -V && \
|
cargo -V
|
||||||
gcc -v &&\
|
|
||||||
g++ -v
|
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
RUN git clone https://github.com/ethcore/parity && \
|
||||||
|
|||||||
@@ -23,15 +23,9 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
|
|||||||
# show backtraces
|
# show backtraces
|
||||||
ENV RUST_BACKTRACE 1
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
# set compilers
|
|
||||||
ENV CXX arm-linux-gnueabihf-g++
|
|
||||||
ENV CC arm-linux-gnueabihf-gcc
|
|
||||||
|
|
||||||
# show tools
|
# show tools
|
||||||
RUN rustc -vV && \
|
RUN rustc -vV && \
|
||||||
cargo -V && \
|
cargo -V
|
||||||
gcc -v &&\
|
|
||||||
g++ -v
|
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
RUN git clone https://github.com/ethcore/parity && \
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ ethcore-ipc = { path = "../ipc/rpc" }
|
|||||||
ethstore = { path = "../ethstore" }
|
ethstore = { path = "../ethstore" }
|
||||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
|
lru-cache = "0.0.7"
|
||||||
|
bloomfilter = { git = "https://github.com/ethcore/rust-bloom-filter" }
|
||||||
|
byteorder = "0.5"
|
||||||
|
|
||||||
[dependencies.hyper]
|
[dependencies.hyper]
|
||||||
git = "https://github.com/ethcore/hyper"
|
git = "https://github.com/ethcore/hyper"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar": "",
|
"registrar": "0x8e4e9b13d4b45cb0befc93c3061b1408f67316b2",
|
||||||
"frontierCompatibilityModeLimit": "0x789b0"
|
"frontierCompatibilityModeLimit": "0x789b0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,17 @@
|
|||||||
|
|
||||||
//! Single account in the system.
|
//! Single account in the system.
|
||||||
|
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
use util::*;
|
use util::*;
|
||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
use account_db::*;
|
use account_db::*;
|
||||||
|
use lru_cache::LruCache;
|
||||||
|
|
||||||
use std::cell::{Ref, RefCell};
|
use std::cell::{RefCell, Cell};
|
||||||
|
|
||||||
|
const STORAGE_CACHE_ITEMS: usize = 4096;
|
||||||
|
|
||||||
/// Single account in the system.
|
/// Single account in the system.
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
// Balance of the account.
|
// Balance of the account.
|
||||||
balance: U256,
|
balance: U256,
|
||||||
@@ -31,14 +34,22 @@ pub struct Account {
|
|||||||
nonce: U256,
|
nonce: U256,
|
||||||
// Trie-backed storage.
|
// Trie-backed storage.
|
||||||
storage_root: H256,
|
storage_root: H256,
|
||||||
// Overlay on trie-backed storage - tuple is (<clean>, <value>).
|
// LRU Cache of the trie-backed storage.
|
||||||
storage_overlay: RefCell<HashMap<H256, (Filth, H256)>>,
|
// This is limited to `STORAGE_CACHE_ITEMS` recent queries
|
||||||
|
storage_cache: RefCell<LruCache<H256, H256>>,
|
||||||
|
// Modified storage. Accumulates changes to storage made in `set_storage`
|
||||||
|
// Takes precedence over `storage_cache`.
|
||||||
|
storage_changes: HashMap<H256, H256>,
|
||||||
// Code hash of the account. If None, means that it's a contract whose code has not yet been set.
|
// Code hash of the account. If None, means that it's a contract whose code has not yet been set.
|
||||||
code_hash: Option<H256>,
|
code_hash: Option<H256>,
|
||||||
|
// Size of the accoun code.
|
||||||
|
code_size: Option<u64>,
|
||||||
// Code cache of the account.
|
// Code cache of the account.
|
||||||
code_cache: Bytes,
|
code_cache: Bytes,
|
||||||
// Account is new or has been modified
|
// Account is new or has been modified
|
||||||
filth: Filth,
|
filth: Filth,
|
||||||
|
// Cached address hash.
|
||||||
|
address_hash: Cell<Option<H256>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
@@ -49,23 +60,33 @@ impl Account {
|
|||||||
balance: balance,
|
balance: balance,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
storage_root: SHA3_NULL_RLP,
|
storage_root: SHA3_NULL_RLP,
|
||||||
storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
storage_cache: Self::empty_storage_cache(),
|
||||||
|
storage_changes: storage,
|
||||||
code_hash: Some(code.sha3()),
|
code_hash: Some(code.sha3()),
|
||||||
|
code_size: Some(code.len() as u64),
|
||||||
code_cache: code,
|
code_cache: code,
|
||||||
filth: Filth::Dirty,
|
filth: Filth::Dirty,
|
||||||
|
address_hash: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn empty_storage_cache() -> RefCell<LruCache<H256, H256>> {
|
||||||
|
RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS))
|
||||||
|
}
|
||||||
|
|
||||||
/// General constructor.
|
/// General constructor.
|
||||||
pub fn from_pod(pod: PodAccount) -> Account {
|
pub fn from_pod(pod: PodAccount) -> Account {
|
||||||
Account {
|
Account {
|
||||||
balance: pod.balance,
|
balance: pod.balance,
|
||||||
nonce: pod.nonce,
|
nonce: pod.nonce,
|
||||||
storage_root: SHA3_NULL_RLP,
|
storage_root: SHA3_NULL_RLP,
|
||||||
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
storage_cache: Self::empty_storage_cache(),
|
||||||
|
storage_changes: pod.storage.into_iter().collect(),
|
||||||
code_hash: pod.code.as_ref().map(|c| c.sha3()),
|
code_hash: pod.code.as_ref().map(|c| c.sha3()),
|
||||||
code_cache: pod.code.as_ref().map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c.clone()),
|
code_size: Some(pod.code.as_ref().map_or(0, |c| c.len() as u64)),
|
||||||
|
code_cache: pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c),
|
||||||
filth: Filth::Dirty,
|
filth: Filth::Dirty,
|
||||||
|
address_hash: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,10 +96,13 @@ impl Account {
|
|||||||
balance: balance,
|
balance: balance,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
storage_root: SHA3_NULL_RLP,
|
storage_root: SHA3_NULL_RLP,
|
||||||
storage_overlay: RefCell::new(HashMap::new()),
|
storage_cache: Self::empty_storage_cache(),
|
||||||
|
storage_changes: HashMap::new(),
|
||||||
code_hash: Some(SHA3_EMPTY),
|
code_hash: Some(SHA3_EMPTY),
|
||||||
code_cache: vec![],
|
code_cache: vec![],
|
||||||
|
code_size: Some(0),
|
||||||
filth: Filth::Dirty,
|
filth: Filth::Dirty,
|
||||||
|
address_hash: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,10 +113,13 @@ impl Account {
|
|||||||
nonce: r.val_at(0),
|
nonce: r.val_at(0),
|
||||||
balance: r.val_at(1),
|
balance: r.val_at(1),
|
||||||
storage_root: r.val_at(2),
|
storage_root: r.val_at(2),
|
||||||
storage_overlay: RefCell::new(HashMap::new()),
|
storage_cache: Self::empty_storage_cache(),
|
||||||
|
storage_changes: HashMap::new(),
|
||||||
code_hash: Some(r.val_at(3)),
|
code_hash: Some(r.val_at(3)),
|
||||||
code_cache: vec![],
|
code_cache: vec![],
|
||||||
|
code_size: None,
|
||||||
filth: Filth::Clean,
|
filth: Filth::Clean,
|
||||||
|
address_hash: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,10 +130,13 @@ impl Account {
|
|||||||
balance: balance,
|
balance: balance,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
storage_root: SHA3_NULL_RLP,
|
storage_root: SHA3_NULL_RLP,
|
||||||
storage_overlay: RefCell::new(HashMap::new()),
|
storage_cache: Self::empty_storage_cache(),
|
||||||
|
storage_changes: HashMap::new(),
|
||||||
code_hash: None,
|
code_hash: None,
|
||||||
code_cache: vec![],
|
code_cache: vec![],
|
||||||
|
code_size: None,
|
||||||
filth: Filth::Dirty,
|
filth: Filth::Dirty,
|
||||||
|
address_hash: Cell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,35 +145,62 @@ impl Account {
|
|||||||
pub fn init_code(&mut self, code: Bytes) {
|
pub fn init_code(&mut self, code: Bytes) {
|
||||||
assert!(self.code_hash.is_none());
|
assert!(self.code_hash.is_none());
|
||||||
self.code_cache = code;
|
self.code_cache = code;
|
||||||
|
self.code_size = Some(self.code_cache.len() as u64);
|
||||||
self.filth = Filth::Dirty;
|
self.filth = Filth::Dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset this account's code to the given code.
|
/// Reset this account's code to the given code.
|
||||||
pub fn reset_code(&mut self, code: Bytes) {
|
pub fn reset_code(&mut self, code: Bytes) {
|
||||||
self.code_hash = None;
|
self.code_hash = None;
|
||||||
|
self.code_size = Some(0);
|
||||||
self.init_code(code);
|
self.init_code(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
|
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
|
||||||
pub fn set_storage(&mut self, key: H256, value: H256) {
|
pub fn set_storage(&mut self, key: H256, value: H256) {
|
||||||
self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
|
match self.storage_changes.entry(key) {
|
||||||
self.filth = Filth::Dirty;
|
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;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get (and cache) the contents of the trie's storage at `key`.
|
/// Get (and cache) the contents of the trie's storage at `key`.
|
||||||
|
/// Takes modifed storage into account.
|
||||||
pub fn storage_at(&self, db: &AccountDB, key: &H256) -> H256 {
|
pub fn storage_at(&self, db: &AccountDB, key: &H256) -> H256 {
|
||||||
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
|
if let Some(value) = self.cached_storage_at(key) {
|
||||||
let db = SecTrieDB::new(db, &self.storage_root)
|
return value;
|
||||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
}
|
||||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
let db = SecTrieDB::new(db, &self.storage_root)
|
||||||
using it will not fail.");
|
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||||
|
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||||
|
using it will not fail.");
|
||||||
|
|
||||||
let item: U256 = match db.get(key){
|
let item: U256 = match db.get(key){
|
||||||
Ok(x) => x.map_or_else(U256::zero, decode),
|
Ok(x) => x.map_or_else(U256::zero, decode),
|
||||||
Err(e) => panic!("Encountered potential DB corruption: {}", e),
|
Err(e) => panic!("Encountered potential DB corruption: {}", e),
|
||||||
};
|
};
|
||||||
(Filth::Clean, item.into())
|
let value: H256 = item.into();
|
||||||
}).1.clone()
|
self.storage_cache.borrow_mut().insert(key.clone(), value.clone());
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get cached storage value if any. Returns `None` if the
|
||||||
|
/// key is not in the cache.
|
||||||
|
pub fn cached_storage_at(&self, key: &H256) -> Option<H256> {
|
||||||
|
if let Some(value) = self.storage_changes.get(key) {
|
||||||
|
return Some(value.clone())
|
||||||
|
}
|
||||||
|
if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) {
|
||||||
|
return Some(value.clone())
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return the balance associated with this account.
|
/// return the balance associated with this account.
|
||||||
@@ -158,6 +215,16 @@ impl Account {
|
|||||||
self.code_hash.clone().unwrap_or(SHA3_EMPTY)
|
self.code_hash.clone().unwrap_or(SHA3_EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return the code hash associated with this account.
|
||||||
|
pub fn address_hash(&self, address: &Address) -> H256 {
|
||||||
|
let hash = self.address_hash.get();
|
||||||
|
hash.unwrap_or_else(|| {
|
||||||
|
let hash = address.sha3();
|
||||||
|
self.address_hash.set(Some(hash.clone()));
|
||||||
|
hash
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// returns the account's code. If `None` then the code cache isn't available -
|
/// returns the account's code. If `None` then the code cache isn't available -
|
||||||
/// get someone who knows to call `note_code`.
|
/// get someone who knows to call `note_code`.
|
||||||
pub fn code(&self) -> Option<&[u8]> {
|
pub fn code(&self) -> Option<&[u8]> {
|
||||||
@@ -169,6 +236,12 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns the account's code size. If `None` then the code cache or code size cache isn't available -
|
||||||
|
/// get someone who knows to call `note_code`.
|
||||||
|
pub fn code_size(&self) -> Option<u64> {
|
||||||
|
self.code_size.clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Provide a byte array which hashes to the `code_hash`. returns the hash as a result.
|
/// Provide a byte array which hashes to the `code_hash`. returns the hash as a result.
|
||||||
pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> {
|
pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> {
|
||||||
@@ -176,6 +249,7 @@ impl Account {
|
|||||||
match self.code_hash {
|
match self.code_hash {
|
||||||
Some(ref i) if h == *i => {
|
Some(ref i) if h == *i => {
|
||||||
self.code_cache = code;
|
self.code_cache = code;
|
||||||
|
self.code_size = Some(self.code_cache.len() as u64);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
_ => Err(h)
|
_ => Err(h)
|
||||||
@@ -189,11 +263,12 @@ impl Account {
|
|||||||
|
|
||||||
/// Is this a new or modified account?
|
/// Is this a new or modified account?
|
||||||
pub fn is_dirty(&self) -> bool {
|
pub fn is_dirty(&self) -> bool {
|
||||||
self.filth == Filth::Dirty
|
self.filth == Filth::Dirty || !self.storage_is_clean()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark account as clean.
|
/// Mark account as clean.
|
||||||
pub fn set_clean(&mut self) {
|
pub fn set_clean(&mut self) {
|
||||||
|
assert!(self.storage_is_clean());
|
||||||
self.filth = Filth::Clean
|
self.filth = Filth::Clean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +279,31 @@ impl Account {
|
|||||||
self.is_cached() ||
|
self.is_cached() ||
|
||||||
match self.code_hash {
|
match self.code_hash {
|
||||||
Some(ref h) => match db.get(h) {
|
Some(ref h) => match db.get(h) {
|
||||||
Some(x) => { self.code_cache = x.to_vec(); true },
|
Some(x) => {
|
||||||
|
self.code_cache = x.to_vec();
|
||||||
|
self.code_size = Some(x.len() as u64);
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
warn!("Failed reverse get of {}", h);
|
||||||
|
false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a database to get `code_size`. Should not be called if it is a contract without code.
|
||||||
|
pub fn cache_code_size(&mut self, db: &AccountDB) -> bool {
|
||||||
|
// TODO: fill out self.code_cache;
|
||||||
|
trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||||
|
self.code_size.is_some() ||
|
||||||
|
match self.code_hash {
|
||||||
|
Some(ref h) if h != &SHA3_EMPTY => match db.get(h) {
|
||||||
|
Some(x) => {
|
||||||
|
self.code_size = Some(x.len() as u64);
|
||||||
|
true
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Failed reverse get of {}", h);
|
warn!("Failed reverse get of {}", h);
|
||||||
false
|
false
|
||||||
@@ -214,16 +313,15 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
||||||
pub fn storage_is_clean(&self) -> bool { self.storage_overlay.borrow().iter().find(|&(_, &(f, _))| f == Filth::Dirty).is_none() }
|
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// return the storage root associated with this account or None if it has been altered via the overlay.
|
/// 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} }
|
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
|
||||||
|
|
||||||
/// return the storage overlay.
|
/// return the storage overlay.
|
||||||
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
|
pub fn storage_changes(&self) -> &HashMap<H256, H256> { &self.storage_changes }
|
||||||
|
|
||||||
/// Increment the nonce of the account by one.
|
/// Increment the nonce of the account by one.
|
||||||
pub fn inc_nonce(&mut self) {
|
pub fn inc_nonce(&mut self) {
|
||||||
@@ -233,38 +331,40 @@ impl Account {
|
|||||||
|
|
||||||
/// Increment the nonce of the account by one.
|
/// Increment the nonce of the account by one.
|
||||||
pub fn add_balance(&mut self, x: &U256) {
|
pub fn add_balance(&mut self, x: &U256) {
|
||||||
self.balance = self.balance + *x;
|
if !x.is_zero() {
|
||||||
self.filth = Filth::Dirty;
|
self.balance = self.balance + *x;
|
||||||
|
self.filth = Filth::Dirty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increment the nonce of the account by one.
|
/// Increment the nonce of the account by one.
|
||||||
/// Panics if balance is less than `x`
|
/// Panics if balance is less than `x`
|
||||||
pub fn sub_balance(&mut self, x: &U256) {
|
pub fn sub_balance(&mut self, x: &U256) {
|
||||||
assert!(self.balance >= *x);
|
if !x.is_zero() {
|
||||||
self.balance = self.balance - *x;
|
assert!(self.balance >= *x);
|
||||||
self.filth = Filth::Dirty;
|
self.balance = self.balance - *x;
|
||||||
|
self.filth = Filth::Dirty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
|
/// Commit the `storage_changes` to the backing DB and update `storage_root`.
|
||||||
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
|
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
|
||||||
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
|
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
|
||||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||||
using it will not fail.");
|
using it will not fail.");
|
||||||
for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() {
|
for (k, v) in self.storage_changes.drain() {
|
||||||
if f == &Filth::Dirty {
|
// cast key and value to trait type,
|
||||||
// cast key and value to trait type,
|
// so we can call overloaded `to_bytes` method
|
||||||
// so we can call overloaded `to_bytes` method
|
let res = match v.is_zero() {
|
||||||
let res = match v.is_zero() {
|
true => t.remove(k.as_slice()),
|
||||||
true => t.remove(k),
|
false => t.insert(k.as_slice(), &encode(&U256::from(v.as_slice()))),
|
||||||
false => t.insert(k, &encode(&U256::from(v.as_slice()))),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
warn!("Encountered potential DB corruption: {}", e);
|
warn!("Encountered potential DB corruption: {}", e);
|
||||||
}
|
|
||||||
*f = Filth::Clean;
|
|
||||||
}
|
}
|
||||||
|
self.storage_cache.borrow_mut().insert(k, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,9 +372,13 @@ impl Account {
|
|||||||
pub fn commit_code(&mut self, db: &mut AccountDBMut) {
|
pub fn commit_code(&mut self, db: &mut AccountDBMut) {
|
||||||
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
|
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
|
||||||
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
|
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
|
||||||
(true, true) => self.code_hash = Some(SHA3_EMPTY),
|
(true, true) => {
|
||||||
|
self.code_hash = Some(SHA3_EMPTY);
|
||||||
|
self.code_size = Some(0);
|
||||||
|
},
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
self.code_hash = Some(db.insert(&self.code_cache));
|
self.code_hash = Some(db.insert(&self.code_cache));
|
||||||
|
self.code_size = Some(self.code_cache.len() as u64);
|
||||||
},
|
},
|
||||||
(false, _) => {},
|
(false, _) => {},
|
||||||
}
|
}
|
||||||
@@ -286,9 +390,57 @@ impl Account {
|
|||||||
stream.append(&self.nonce);
|
stream.append(&self.nonce);
|
||||||
stream.append(&self.balance);
|
stream.append(&self.balance);
|
||||||
stream.append(&self.storage_root);
|
stream.append(&self.storage_root);
|
||||||
stream.append(self.code_hash.as_ref().expect("Cannot form RLP of contract account without code."));
|
stream.append(self.code_hash.as_ref().unwrap_or(&SHA3_EMPTY));
|
||||||
stream.out()
|
stream.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone basic account data
|
||||||
|
pub fn clone_basic(&self) -> Account {
|
||||||
|
Account {
|
||||||
|
balance: self.balance.clone(),
|
||||||
|
nonce: self.nonce.clone(),
|
||||||
|
storage_root: self.storage_root.clone(),
|
||||||
|
storage_cache: Self::empty_storage_cache(),
|
||||||
|
storage_changes: HashMap::new(),
|
||||||
|
code_hash: self.code_hash.clone(),
|
||||||
|
code_size: self.code_size.clone(),
|
||||||
|
code_cache: Bytes::new(),
|
||||||
|
filth: self.filth,
|
||||||
|
address_hash: self.address_hash.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone account data and dirty storage keys
|
||||||
|
pub fn clone_dirty(&self) -> Account {
|
||||||
|
let mut account = self.clone_basic();
|
||||||
|
account.storage_changes = self.storage_changes.clone();
|
||||||
|
account.code_cache = self.code_cache.clone();
|
||||||
|
account
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone account data, dirty storage keys and cached storage keys.
|
||||||
|
pub fn clone_all(&self) -> Account {
|
||||||
|
let mut account = self.clone_dirty();
|
||||||
|
account.storage_cache = self.storage_cache.clone();
|
||||||
|
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());
|
||||||
|
self.balance = other.balance;
|
||||||
|
self.nonce = other.nonce;
|
||||||
|
self.storage_root = other.storage_root;
|
||||||
|
self.code_hash = other.code_hash;
|
||||||
|
self.code_cache = other.code_cache;
|
||||||
|
self.code_size = other.code_size;
|
||||||
|
self.address_hash = other.address_hash;
|
||||||
|
let mut cache = self.storage_cache.borrow_mut();
|
||||||
|
for (k, v) in other.storage_cache.into_inner().into_iter() {
|
||||||
|
cache.insert(k.clone() , v.clone()); //TODO: cloning should not be required here
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Account {
|
impl fmt::Debug for Account {
|
||||||
@@ -384,6 +536,7 @@ mod tests {
|
|||||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||||
assert_eq!(a.code_hash(), SHA3_EMPTY);
|
assert_eq!(a.code_hash(), SHA3_EMPTY);
|
||||||
|
assert_eq!(a.code_size(), Some(3));
|
||||||
a.commit_code(&mut db);
|
a.commit_code(&mut db);
|
||||||
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
|
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use state::*;
|
use state::*;
|
||||||
|
use state_db::StateDB;
|
||||||
use verification::PreverifiedBlock;
|
use verification::PreverifiedBlock;
|
||||||
use trace::FlatTrace;
|
use trace::FlatTrace;
|
||||||
use evm::Factory as EvmFactory;
|
use evm::Factory as EvmFactory;
|
||||||
@@ -178,7 +179,7 @@ pub trait IsBlock {
|
|||||||
/// Trait for a object that has a state database.
|
/// Trait for a object that has a state database.
|
||||||
pub trait Drain {
|
pub trait Drain {
|
||||||
/// Drop this object and return the underlieing database.
|
/// Drop this object and return the underlieing database.
|
||||||
fn drain(self) -> Box<JournalDB>;
|
fn drain(self) -> StateDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsBlock for ExecutedBlock {
|
impl IsBlock for ExecutedBlock {
|
||||||
@@ -233,7 +234,7 @@ impl<'x> OpenBlock<'x> {
|
|||||||
vm_factory: &'x EvmFactory,
|
vm_factory: &'x EvmFactory,
|
||||||
trie_factory: TrieFactory,
|
trie_factory: TrieFactory,
|
||||||
tracing: bool,
|
tracing: bool,
|
||||||
db: Box<JournalDB>,
|
db: StateDB,
|
||||||
parent: &Header,
|
parent: &Header,
|
||||||
last_hashes: Arc<LastHashes>,
|
last_hashes: Arc<LastHashes>,
|
||||||
author: Address,
|
author: Address,
|
||||||
@@ -465,7 +466,9 @@ impl LockedBlock {
|
|||||||
|
|
||||||
impl Drain for LockedBlock {
|
impl Drain for LockedBlock {
|
||||||
/// Drop this object and return the underlieing database.
|
/// Drop this object and return the underlieing database.
|
||||||
fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
fn drain(self) -> StateDB {
|
||||||
|
self.block.state.drop().1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SealedBlock {
|
impl SealedBlock {
|
||||||
@@ -481,7 +484,9 @@ impl SealedBlock {
|
|||||||
|
|
||||||
impl Drain for SealedBlock {
|
impl Drain for SealedBlock {
|
||||||
/// Drop this object and return the underlieing database.
|
/// Drop this object and return the underlieing database.
|
||||||
fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
fn drain(self) -> StateDB {
|
||||||
|
self.block.state.drop().1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsBlock for SealedBlock {
|
impl IsBlock for SealedBlock {
|
||||||
@@ -496,7 +501,7 @@ pub fn enact(
|
|||||||
uncles: &[Header],
|
uncles: &[Header],
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
tracing: bool,
|
tracing: bool,
|
||||||
db: Box<JournalDB>,
|
db: StateDB,
|
||||||
parent: &Header,
|
parent: &Header,
|
||||||
last_hashes: Arc<LastHashes>,
|
last_hashes: Arc<LastHashes>,
|
||||||
vm_factory: &EvmFactory,
|
vm_factory: &EvmFactory,
|
||||||
@@ -505,7 +510,7 @@ pub fn enact(
|
|||||||
{
|
{
|
||||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone()));
|
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone()));
|
||||||
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
|
trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", header.number(), s.root(), header.author(), s.balance(&header.author()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +534,7 @@ pub fn enact_bytes(
|
|||||||
block_bytes: &[u8],
|
block_bytes: &[u8],
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
tracing: bool,
|
tracing: bool,
|
||||||
db: Box<JournalDB>,
|
db: StateDB,
|
||||||
parent: &Header,
|
parent: &Header,
|
||||||
last_hashes: Arc<LastHashes>,
|
last_hashes: Arc<LastHashes>,
|
||||||
vm_factory: &EvmFactory,
|
vm_factory: &EvmFactory,
|
||||||
@@ -546,7 +551,7 @@ pub fn enact_verified(
|
|||||||
block: &PreverifiedBlock,
|
block: &PreverifiedBlock,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
tracing: bool,
|
tracing: bool,
|
||||||
db: Box<JournalDB>,
|
db: StateDB,
|
||||||
parent: &Header,
|
parent: &Header,
|
||||||
last_hashes: Arc<LastHashes>,
|
last_hashes: Arc<LastHashes>,
|
||||||
vm_factory: &EvmFactory,
|
vm_factory: &EvmFactory,
|
||||||
@@ -562,7 +567,7 @@ pub fn enact_and_seal(
|
|||||||
block_bytes: &[u8],
|
block_bytes: &[u8],
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
tracing: bool,
|
tracing: bool,
|
||||||
db: Box<JournalDB>,
|
db: StateDB,
|
||||||
parent: &Header,
|
parent: &Header,
|
||||||
last_hashes: Arc<LastHashes>,
|
last_hashes: Arc<LastHashes>,
|
||||||
vm_factory: &EvmFactory,
|
vm_factory: &EvmFactory,
|
||||||
@@ -582,75 +587,74 @@ mod tests {
|
|||||||
fn open_block() {
|
fn open_block() {
|
||||||
use spec::*;
|
use spec::*;
|
||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
let engine = &spec.engine;
|
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let b = OpenBlock::new(&*spec.engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let b = b.close_and_lock();
|
let b = b.close_and_lock();
|
||||||
let _ = b.seal(engine.deref(), vec![]);
|
let _ = b.seal(&*spec.engine, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enact_block() {
|
fn enact_block() {
|
||||||
use spec::*;
|
use spec::*;
|
||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||||
.close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
.close_and_lock().seal(engine, vec![]).unwrap();
|
||||||
let orig_bytes = b.rlp_bytes();
|
let orig_bytes = b.rlp_bytes();
|
||||||
let orig_db = b.drain();
|
let orig_db = b.drain();
|
||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
||||||
|
|
||||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||||
|
|
||||||
let db = e.drain();
|
let db = e.drain();
|
||||||
assert_eq!(orig_db.keys(), db.keys());
|
assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys());
|
||||||
assert!(orig_db.keys().iter().filter(|k| orig_db.get(k.0) != db.get(k.0)).next() == None);
|
assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enact_block_with_uncle() {
|
fn enact_block_with_uncle() {
|
||||||
use spec::*;
|
use spec::*;
|
||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let mut open_block = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let mut uncle1_header = Header::new();
|
let mut uncle1_header = Header::new();
|
||||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
uncle1_header.extra_data = b"uncle1".to_vec();
|
||||||
let mut uncle2_header = Header::new();
|
let mut uncle2_header = Header::new();
|
||||||
uncle2_header.extra_data = b"uncle2".to_vec();
|
uncle2_header.extra_data = b"uncle2".to_vec();
|
||||||
open_block.push_uncle(uncle1_header).unwrap();
|
open_block.push_uncle(uncle1_header).unwrap();
|
||||||
open_block.push_uncle(uncle2_header).unwrap();
|
open_block.push_uncle(uncle2_header).unwrap();
|
||||||
let b = open_block.close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
let b = open_block.close_and_lock().seal(engine, vec![]).unwrap();
|
||||||
|
|
||||||
let orig_bytes = b.rlp_bytes();
|
let orig_bytes = b.rlp_bytes();
|
||||||
let orig_db = b.drain();
|
let orig_db = b.drain();
|
||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
||||||
|
|
||||||
let bytes = e.rlp_bytes();
|
let bytes = e.rlp_bytes();
|
||||||
assert_eq!(bytes, orig_bytes);
|
assert_eq!(bytes, orig_bytes);
|
||||||
@@ -658,7 +662,7 @@ mod tests {
|
|||||||
assert_eq!(uncles[1].extra_data, b"uncle2");
|
assert_eq!(uncles[1].extra_data, b"uncle2");
|
||||||
|
|
||||||
let db = e.drain();
|
let db = e.drain();
|
||||||
assert_eq!(orig_db.keys(), db.keys());
|
assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys());
|
||||||
assert!(orig_db.keys().iter().filter(|k| orig_db.get(k.0) != db.get(k.0)).next() == None);
|
assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ pub struct BlockInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Describes location of newly inserted block.
|
/// Describes location of newly inserted block.
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum BlockLocation {
|
pub enum BlockLocation {
|
||||||
/// It's part of the canon chain.
|
/// It's part of the canon chain.
|
||||||
CanonChain,
|
CanonChain,
|
||||||
@@ -43,7 +43,7 @@ pub enum BlockLocation {
|
|||||||
BranchBecomingCanonChain(BranchBecomingCanonChainData),
|
BranchBecomingCanonChain(BranchBecomingCanonChainData),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BranchBecomingCanonChainData {
|
pub struct BranchBecomingCanonChainData {
|
||||||
/// Hash of the newest common ancestor with old canon chain.
|
/// Hash of the newest common ancestor with old canon chain.
|
||||||
pub ancestor: H256,
|
pub ancestor: H256,
|
||||||
|
|||||||
@@ -133,8 +133,9 @@ enum CacheID {
|
|||||||
impl bc::group::BloomGroupDatabase for BlockChain {
|
impl bc::group::BloomGroupDatabase for BlockChain {
|
||||||
fn blooms_at(&self, position: &bc::group::GroupPosition) -> Option<bc::group::BloomGroup> {
|
fn blooms_at(&self, position: &bc::group::GroupPosition) -> Option<bc::group::BloomGroup> {
|
||||||
let position = LogGroupPosition::from(position.clone());
|
let position = LogGroupPosition::from(position.clone());
|
||||||
self.note_used(CacheID::BlocksBlooms(position.clone()));
|
let result = self.db.read_with_cache(DB_COL_EXTRA, &self.blocks_blooms, &position).map(Into::into);
|
||||||
self.db.read_with_cache(DB_COL_EXTRA, &self.blocks_blooms, &position).map(Into::into)
|
self.note_used(CacheID::BlocksBlooms(position));
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +165,7 @@ pub struct BlockChain {
|
|||||||
|
|
||||||
pending_best_block: RwLock<Option<BestBlock>>,
|
pending_best_block: RwLock<Option<BestBlock>>,
|
||||||
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
||||||
pending_transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
|
pending_transaction_addresses: RwLock<HashMap<H256, Option<TransactionAddress>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockProvider for BlockChain {
|
impl BlockProvider for BlockChain {
|
||||||
@@ -211,9 +212,7 @@ impl BlockProvider for BlockChain {
|
|||||||
let opt = self.db.get(DB_COL_HEADERS, hash)
|
let opt = self.db.get(DB_COL_HEADERS, hash)
|
||||||
.expect("Low level database error. Some issue with disk?");
|
.expect("Low level database error. Some issue with disk?");
|
||||||
|
|
||||||
self.note_used(CacheID::BlockHeader(hash.clone()));
|
let result = match opt {
|
||||||
|
|
||||||
match opt {
|
|
||||||
Some(b) => {
|
Some(b) => {
|
||||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
||||||
let mut write = self.block_headers.write();
|
let mut write = self.block_headers.write();
|
||||||
@@ -221,7 +220,10 @@ impl BlockProvider for BlockChain {
|
|||||||
Some(bytes)
|
Some(bytes)
|
||||||
},
|
},
|
||||||
None => None
|
None => None
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.note_used(CacheID::BlockHeader(hash.clone()));
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get block body data
|
/// Get block body data
|
||||||
@@ -246,9 +248,7 @@ impl BlockProvider for BlockChain {
|
|||||||
let opt = self.db.get(DB_COL_BODIES, hash)
|
let opt = self.db.get(DB_COL_BODIES, hash)
|
||||||
.expect("Low level database error. Some issue with disk?");
|
.expect("Low level database error. Some issue with disk?");
|
||||||
|
|
||||||
self.note_used(CacheID::BlockBody(hash.clone()));
|
let result = match opt {
|
||||||
|
|
||||||
match opt {
|
|
||||||
Some(b) => {
|
Some(b) => {
|
||||||
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
let bytes: Bytes = UntrustedRlp::new(&b).decompress(RlpType::Blocks).to_vec();
|
||||||
let mut write = self.block_bodies.write();
|
let mut write = self.block_bodies.write();
|
||||||
@@ -256,31 +256,39 @@ impl BlockProvider for BlockChain {
|
|||||||
Some(bytes)
|
Some(bytes)
|
||||||
},
|
},
|
||||||
None => None
|
None => None
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.note_used(CacheID::BlockBody(hash.clone()));
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the familial details concerning a block.
|
/// Get the familial details concerning a block.
|
||||||
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
|
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
|
||||||
|
let result = self.db.read_with_cache(DB_COL_EXTRA, &self.block_details, hash);
|
||||||
self.note_used(CacheID::BlockDetails(hash.clone()));
|
self.note_used(CacheID::BlockDetails(hash.clone()));
|
||||||
self.db.read_with_cache(DB_COL_EXTRA, &self.block_details, hash)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the hash of given block's number.
|
/// Get the hash of given block's number.
|
||||||
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
|
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
|
||||||
|
let result = self.db.read_with_cache(DB_COL_EXTRA, &self.block_hashes, &index);
|
||||||
self.note_used(CacheID::BlockHashes(index));
|
self.note_used(CacheID::BlockHashes(index));
|
||||||
self.db.read_with_cache(DB_COL_EXTRA, &self.block_hashes, &index)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the address of transaction with given hash.
|
/// Get the address of transaction with given hash.
|
||||||
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress> {
|
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress> {
|
||||||
|
let result = self.db.read_with_cache(DB_COL_EXTRA, &self.transaction_addresses, hash);
|
||||||
self.note_used(CacheID::TransactionAddresses(hash.clone()));
|
self.note_used(CacheID::TransactionAddresses(hash.clone()));
|
||||||
self.db.read_with_cache(DB_COL_EXTRA, &self.transaction_addresses, hash)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get receipts of block with given hash.
|
/// Get receipts of block with given hash.
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
|
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
|
||||||
|
let result = self.db.read_with_cache(DB_COL_EXTRA, &self.block_receipts, hash);
|
||||||
self.note_used(CacheID::BlockReceipts(hash.clone()));
|
self.note_used(CacheID::BlockReceipts(hash.clone()));
|
||||||
self.db.read_with_cache(DB_COL_EXTRA, &self.block_receipts, hash)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns numbers of blocks containing given bloom.
|
/// Returns numbers of blocks containing given bloom.
|
||||||
@@ -576,8 +584,8 @@ impl BlockChain {
|
|||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: self.prepare_block_details_update(bytes, &info),
|
block_details: self.prepare_block_details_update(bytes, &info),
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
|
||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
info: info,
|
info: info,
|
||||||
block: bytes
|
block: bytes
|
||||||
}, is_best);
|
}, is_best);
|
||||||
@@ -610,8 +618,8 @@ impl BlockChain {
|
|||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: update,
|
block_details: update,
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
|
||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
info: info,
|
info: info,
|
||||||
block: bytes,
|
block: bytes,
|
||||||
}, is_best);
|
}, is_best);
|
||||||
@@ -635,11 +643,12 @@ impl BlockChain {
|
|||||||
let mut update = HashMap::new();
|
let mut update = HashMap::new();
|
||||||
update.insert(block_hash, parent_details);
|
update.insert(block_hash, parent_details);
|
||||||
|
|
||||||
self.note_used(CacheID::BlockDetails(block_hash));
|
|
||||||
|
|
||||||
let mut write_details = self.block_details.write();
|
let mut write_details = self.block_details.write();
|
||||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite);
|
||||||
|
|
||||||
|
self.note_used(CacheID::BlockDetails(block_hash));
|
||||||
|
|
||||||
self.db.write(batch).unwrap();
|
self.db.write(batch).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,8 +687,8 @@ impl BlockChain {
|
|||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: self.prepare_block_details_update(bytes, &info),
|
block_details: self.prepare_block_details_update(bytes, &info),
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
|
||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
info: info.clone(),
|
info: info.clone(),
|
||||||
block: bytes,
|
block: bytes,
|
||||||
}, true);
|
}, true);
|
||||||
@@ -730,12 +739,15 @@ impl BlockChain {
|
|||||||
/// Prepares extras update.
|
/// Prepares extras update.
|
||||||
fn prepare_update(&self, batch: &DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
fn prepare_update(&self, batch: &DBTransaction, update: ExtrasUpdate, is_best: bool) {
|
||||||
{
|
{
|
||||||
for hash in update.block_details.keys().cloned() {
|
let block_hashes: Vec<_> = update.block_details.keys().cloned().collect();
|
||||||
self.note_used(CacheID::BlockDetails(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut write_details = self.block_details.write();
|
let mut write_details = self.block_details.write();
|
||||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
|
||||||
|
|
||||||
|
let mut cache_man = self.cache_man.write();
|
||||||
|
for hash in block_hashes.into_iter() {
|
||||||
|
cache_man.note_used(CacheID::BlockDetails(hash));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -769,25 +781,45 @@ impl BlockChain {
|
|||||||
let mut write_txs = self.pending_transaction_addresses.write();
|
let mut write_txs = self.pending_transaction_addresses.write();
|
||||||
|
|
||||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
|
||||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
|
batch.extend_with_option_cache(DB_COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applt pending insertion updates
|
/// Apply pending insertion updates
|
||||||
pub fn commit(&self) {
|
pub fn commit(&self) {
|
||||||
let mut best_block = self.best_block.write();
|
|
||||||
let mut write_hashes = self.block_hashes.write();
|
|
||||||
let mut write_txs = self.transaction_addresses.write();
|
|
||||||
let mut pending_best_block = self.pending_best_block.write();
|
let mut pending_best_block = self.pending_best_block.write();
|
||||||
let mut pending_write_hashes = self.pending_block_hashes.write();
|
let mut pending_write_hashes = self.pending_block_hashes.write();
|
||||||
let mut pending_write_txs = self.pending_transaction_addresses.write();
|
let mut pending_write_txs = self.pending_transaction_addresses.write();
|
||||||
|
|
||||||
|
let mut best_block = self.best_block.write();
|
||||||
|
let mut write_hashes = self.block_hashes.write();
|
||||||
|
let mut write_txs = self.transaction_addresses.write();
|
||||||
// update best block
|
// update best block
|
||||||
if let Some(block) = pending_best_block.take() {
|
if let Some(block) = pending_best_block.take() {
|
||||||
*best_block = block;
|
*best_block = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pending_txs = mem::replace(&mut *pending_write_txs, HashMap::new());
|
||||||
|
let (retracted_txs, enacted_txs) = pending_txs.into_iter().partition::<HashMap<_, _>, _>(|&(_, ref value)| value.is_none());
|
||||||
|
|
||||||
|
let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect();
|
||||||
|
let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect();
|
||||||
|
|
||||||
write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new()));
|
write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new()));
|
||||||
write_txs.extend(mem::replace(&mut *pending_write_txs, HashMap::new()));
|
write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed"))));
|
||||||
|
|
||||||
|
for hash in retracted_txs.keys() {
|
||||||
|
write_txs.remove(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cache_man = self.cache_man.write();
|
||||||
|
for n in pending_hashes_keys.into_iter() {
|
||||||
|
cache_man.note_used(CacheID::BlockHashes(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
for hash in enacted_txs_keys {
|
||||||
|
cache_man.note_used(CacheID::TransactionAddresses(hash));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
|
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
|
||||||
@@ -887,19 +919,56 @@ impl BlockChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function returns modified transaction addresses.
|
/// This function returns modified transaction addresses.
|
||||||
fn prepare_transaction_addresses_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, TransactionAddress> {
|
fn prepare_transaction_addresses_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, Option<TransactionAddress>> {
|
||||||
let block = BlockView::new(block_bytes);
|
let block = BlockView::new(block_bytes);
|
||||||
let transaction_hashes = block.transaction_hashes();
|
let transaction_hashes = block.transaction_hashes();
|
||||||
|
|
||||||
transaction_hashes.into_iter()
|
match info.location {
|
||||||
.enumerate()
|
BlockLocation::CanonChain => {
|
||||||
.fold(HashMap::new(), |mut acc, (i ,tx_hash)| {
|
transaction_hashes.into_iter()
|
||||||
acc.insert(tx_hash, TransactionAddress {
|
.enumerate()
|
||||||
block_hash: info.hash.clone(),
|
.map(|(i ,tx_hash)| {
|
||||||
index: i
|
(tx_hash, Some(TransactionAddress {
|
||||||
|
block_hash: info.hash.clone(),
|
||||||
|
index: i
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
},
|
||||||
|
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
||||||
|
let addresses = data.enacted.iter()
|
||||||
|
.flat_map(|hash| {
|
||||||
|
let bytes = self.block_body(hash).expect("Enacted block must be in database.");
|
||||||
|
let hashes = BodyView::new(&bytes).transaction_hashes();
|
||||||
|
hashes.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, tx_hash)| (tx_hash, Some(TransactionAddress {
|
||||||
|
block_hash: hash.clone(),
|
||||||
|
index: i,
|
||||||
|
})))
|
||||||
|
.collect::<HashMap<H256, Option<TransactionAddress>>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
let current_addresses = transaction_hashes.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i ,tx_hash)| {
|
||||||
|
(tx_hash, Some(TransactionAddress {
|
||||||
|
block_hash: info.hash.clone(),
|
||||||
|
index: i
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
let retracted = data.retracted.iter().flat_map(|hash| {
|
||||||
|
let bytes = self.block_body(hash).expect("Retracted block must be in database.");
|
||||||
|
let hashes = BodyView::new(&bytes).transaction_hashes();
|
||||||
|
hashes.into_iter().map(|hash| (hash, None)).collect::<HashMap<H256, Option<TransactionAddress>>>()
|
||||||
});
|
});
|
||||||
acc
|
|
||||||
})
|
// The order here is important! Don't remove transaction if it was part of enacted blocks as well.
|
||||||
|
retracted.chain(addresses).chain(current_addresses).collect()
|
||||||
|
},
|
||||||
|
BlockLocation::Branch => HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This functions returns modified blocks blooms.
|
/// This functions returns modified blocks blooms.
|
||||||
@@ -992,16 +1061,18 @@ impl BlockChain {
|
|||||||
|
|
||||||
/// Ticks our cache system and throws out any old data.
|
/// Ticks our cache system and throws out any old data.
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
let mut cache_man = self.cache_man.write();
|
let current_size = self.cache_size().total();
|
||||||
cache_man.collect_garbage(|| self.cache_size().total(), | ids | {
|
|
||||||
let mut block_headers = self.block_headers.write();
|
|
||||||
let mut block_bodies = self.block_bodies.write();
|
|
||||||
let mut block_details = self.block_details.write();
|
|
||||||
let mut block_hashes = self.block_hashes.write();
|
|
||||||
let mut transaction_addresses = self.transaction_addresses.write();
|
|
||||||
let mut blocks_blooms = self.blocks_blooms.write();
|
|
||||||
let mut block_receipts = self.block_receipts.write();
|
|
||||||
|
|
||||||
|
let mut block_headers = self.block_headers.write();
|
||||||
|
let mut block_bodies = self.block_bodies.write();
|
||||||
|
let mut block_details = self.block_details.write();
|
||||||
|
let mut block_hashes = self.block_hashes.write();
|
||||||
|
let mut transaction_addresses = self.transaction_addresses.write();
|
||||||
|
let mut blocks_blooms = self.blocks_blooms.write();
|
||||||
|
let mut block_receipts = self.block_receipts.write();
|
||||||
|
|
||||||
|
let mut cache_man = self.cache_man.write();
|
||||||
|
cache_man.collect_garbage(current_size, | ids | {
|
||||||
for id in &ids {
|
for id in &ids {
|
||||||
match *id {
|
match *id {
|
||||||
CacheID::BlockHeader(ref h) => { block_headers.remove(h); },
|
CacheID::BlockHeader(ref h) => { block_headers.remove(h); },
|
||||||
@@ -1013,6 +1084,7 @@ impl BlockChain {
|
|||||||
CacheID::BlockReceipts(ref h) => { block_receipts.remove(h); }
|
CacheID::BlockReceipts(ref h) => { block_receipts.remove(h); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block_headers.shrink_to_fit();
|
block_headers.shrink_to_fit();
|
||||||
block_bodies.shrink_to_fit();
|
block_bodies.shrink_to_fit();
|
||||||
block_details.shrink_to_fit();
|
block_details.shrink_to_fit();
|
||||||
@@ -1020,6 +1092,14 @@ impl BlockChain {
|
|||||||
transaction_addresses.shrink_to_fit();
|
transaction_addresses.shrink_to_fit();
|
||||||
blocks_blooms.shrink_to_fit();
|
blocks_blooms.shrink_to_fit();
|
||||||
block_receipts.shrink_to_fit();
|
block_receipts.shrink_to_fit();
|
||||||
|
|
||||||
|
block_headers.heap_size_of_children() +
|
||||||
|
block_bodies.heap_size_of_children() +
|
||||||
|
block_details.heap_size_of_children() +
|
||||||
|
block_hashes.heap_size_of_children() +
|
||||||
|
transaction_addresses.heap_size_of_children() +
|
||||||
|
blocks_blooms.heap_size_of_children() +
|
||||||
|
block_receipts.heap_size_of_children()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,7 +1116,6 @@ impl BlockChain {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![cfg_attr(feature="dev", allow(similar_names))]
|
#![cfg_attr(feature="dev", allow(similar_names))]
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
use util::{Database, DatabaseConfig};
|
use util::{Database, DatabaseConfig};
|
||||||
@@ -1047,8 +1126,10 @@ mod tests {
|
|||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use devtools::*;
|
use devtools::*;
|
||||||
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||||
|
use blockchain::extras::TransactionAddress;
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use client;
|
use client;
|
||||||
|
use transaction::{Transaction, Action};
|
||||||
|
|
||||||
fn new_db(path: &str) -> Arc<Database> {
|
fn new_db(path: &str) -> Arc<Database> {
|
||||||
Arc::new(Database::open(&DatabaseConfig::with_columns(client::DB_NO_OF_COLUMNS), path).unwrap())
|
Arc::new(Database::open(&DatabaseConfig::with_columns(client::DB_NO_OF_COLUMNS), path).unwrap())
|
||||||
@@ -1183,6 +1264,170 @@ mod tests {
|
|||||||
// TODO: insert block that already includes one of them as an uncle to check it's not allowed.
|
// TODO: insert block that already includes one of them as an uncle to check it's not allowed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fork_transaction_addresses() {
|
||||||
|
let mut canon_chain = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let mut fork_chain = canon_chain.fork(1);
|
||||||
|
let mut fork_finalizer = finalizer.fork();
|
||||||
|
|
||||||
|
let t1 = Transaction {
|
||||||
|
nonce: 0.into(),
|
||||||
|
gas_price: 0.into(),
|
||||||
|
gas: 100_000.into(),
|
||||||
|
action: Action::Create,
|
||||||
|
value: 100.into(),
|
||||||
|
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||||
|
}.sign(&"".sha3());
|
||||||
|
|
||||||
|
|
||||||
|
let b1a = canon_chain
|
||||||
|
.with_transaction(t1.clone())
|
||||||
|
.generate(&mut finalizer).unwrap();
|
||||||
|
|
||||||
|
// Empty block
|
||||||
|
let b1b = fork_chain
|
||||||
|
.generate(&mut fork_finalizer).unwrap();
|
||||||
|
|
||||||
|
let b2 = fork_chain
|
||||||
|
.generate(&mut fork_finalizer).unwrap();
|
||||||
|
|
||||||
|
let b1a_hash = BlockView::new(&b1a).header_view().sha3();
|
||||||
|
let b2_hash = BlockView::new(&b2).header_view().sha3();
|
||||||
|
|
||||||
|
let t1_hash = t1.hash();
|
||||||
|
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let db = new_db(temp.as_str());
|
||||||
|
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||||
|
|
||||||
|
let mut batch = db.transaction();
|
||||||
|
let _ = bc.insert_block(&mut batch, &b1a, vec![]);
|
||||||
|
bc.commit();
|
||||||
|
let _ = bc.insert_block(&mut batch, &b1b, vec![]);
|
||||||
|
bc.commit();
|
||||||
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(bc.best_block_hash(), b1a_hash);
|
||||||
|
assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress {
|
||||||
|
block_hash: b1a_hash.clone(),
|
||||||
|
index: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// now let's make forked chain the canon chain
|
||||||
|
let mut batch = db.transaction();
|
||||||
|
let _ = bc.insert_block(&mut batch, &b2, vec![]);
|
||||||
|
bc.commit();
|
||||||
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
|
// Transaction should be retracted
|
||||||
|
assert_eq!(bc.best_block_hash(), b2_hash);
|
||||||
|
assert_eq!(bc.transaction_address(&t1_hash), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_overwriting_transaction_addresses() {
|
||||||
|
let mut canon_chain = ChainGenerator::default();
|
||||||
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||||
|
let mut fork_chain = canon_chain.fork(1);
|
||||||
|
let mut fork_finalizer = finalizer.fork();
|
||||||
|
|
||||||
|
let t1 = Transaction {
|
||||||
|
nonce: 0.into(),
|
||||||
|
gas_price: 0.into(),
|
||||||
|
gas: 100_000.into(),
|
||||||
|
action: Action::Create,
|
||||||
|
value: 100.into(),
|
||||||
|
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||||
|
}.sign(&"".sha3());
|
||||||
|
|
||||||
|
let t2 = Transaction {
|
||||||
|
nonce: 1.into(),
|
||||||
|
gas_price: 0.into(),
|
||||||
|
gas: 100_000.into(),
|
||||||
|
action: Action::Create,
|
||||||
|
value: 100.into(),
|
||||||
|
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||||
|
}.sign(&"".sha3());
|
||||||
|
|
||||||
|
let t3 = Transaction {
|
||||||
|
nonce: 2.into(),
|
||||||
|
gas_price: 0.into(),
|
||||||
|
gas: 100_000.into(),
|
||||||
|
action: Action::Create,
|
||||||
|
value: 100.into(),
|
||||||
|
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||||
|
}.sign(&"".sha3());
|
||||||
|
|
||||||
|
let b1a = canon_chain
|
||||||
|
.with_transaction(t1.clone())
|
||||||
|
.with_transaction(t2.clone())
|
||||||
|
.generate(&mut finalizer).unwrap();
|
||||||
|
|
||||||
|
// insert transactions in different order
|
||||||
|
let b1b = fork_chain
|
||||||
|
.with_transaction(t2.clone())
|
||||||
|
.with_transaction(t1.clone())
|
||||||
|
.generate(&mut fork_finalizer).unwrap();
|
||||||
|
|
||||||
|
let b2 = fork_chain
|
||||||
|
.with_transaction(t3.clone())
|
||||||
|
.generate(&mut fork_finalizer).unwrap();
|
||||||
|
|
||||||
|
let b1a_hash = BlockView::new(&b1a).header_view().sha3();
|
||||||
|
let b1b_hash = BlockView::new(&b1b).header_view().sha3();
|
||||||
|
let b2_hash = BlockView::new(&b2).header_view().sha3();
|
||||||
|
|
||||||
|
let t1_hash = t1.hash();
|
||||||
|
let t2_hash = t2.hash();
|
||||||
|
let t3_hash = t3.hash();
|
||||||
|
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let db = new_db(temp.as_str());
|
||||||
|
let bc = BlockChain::new(Config::default(), &genesis, db.clone());
|
||||||
|
|
||||||
|
let mut batch = db.transaction();
|
||||||
|
let _ = bc.insert_block(&mut batch, &b1a, vec![]);
|
||||||
|
bc.commit();
|
||||||
|
let _ = bc.insert_block(&mut batch, &b1b, vec![]);
|
||||||
|
bc.commit();
|
||||||
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(bc.best_block_hash(), b1a_hash);
|
||||||
|
assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress {
|
||||||
|
block_hash: b1a_hash.clone(),
|
||||||
|
index: 0,
|
||||||
|
}));
|
||||||
|
assert_eq!(bc.transaction_address(&t2_hash), Some(TransactionAddress {
|
||||||
|
block_hash: b1a_hash.clone(),
|
||||||
|
index: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// now let's make forked chain the canon chain
|
||||||
|
let mut batch = db.transaction();
|
||||||
|
let _ = bc.insert_block(&mut batch, &b2, vec![]);
|
||||||
|
bc.commit();
|
||||||
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(bc.best_block_hash(), b2_hash);
|
||||||
|
assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress {
|
||||||
|
block_hash: b1b_hash.clone(),
|
||||||
|
index: 1,
|
||||||
|
}));
|
||||||
|
assert_eq!(bc.transaction_address(&t2_hash), Some(TransactionAddress {
|
||||||
|
block_hash: b1b_hash.clone(),
|
||||||
|
index: 0,
|
||||||
|
}));
|
||||||
|
assert_eq!(bc.transaction_address(&t3_hash), Some(TransactionAddress {
|
||||||
|
block_hash: b2_hash.clone(),
|
||||||
|
index: 0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||||
fn test_small_fork() {
|
fn test_small_fork() {
|
||||||
@@ -1383,7 +1628,7 @@ mod tests {
|
|||||||
fn find_transaction_by_hash() {
|
fn find_transaction_by_hash() {
|
||||||
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap();
|
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap();
|
||||||
let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap();
|
let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap();
|
||||||
let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap();
|
let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into();
|
||||||
|
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let db = new_db(temp.as_str());
|
let db = new_db(temp.as_str());
|
||||||
@@ -1411,11 +1656,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_bloom_filter_simple() {
|
fn test_bloom_filter_simple() {
|
||||||
// TODO: From here
|
// TODO: From here
|
||||||
let bloom_b1 = H2048::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap();
|
let bloom_b1: H2048 = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into();
|
||||||
|
|
||||||
let bloom_b2 = H2048::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let bloom_b2: H2048 = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||||
|
|
||||||
let bloom_ba = H2048::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
let bloom_ba: H2048 = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into();
|
||||||
|
|
||||||
let mut canon_chain = ChainGenerator::default();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
let mut finalizer = BlockFinalizer::default();
|
let mut finalizer = BlockFinalizer::default();
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ impl Encodable for BlockDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents address of certain transaction within block
|
/// Represents address of certain transaction within block
|
||||||
#[derive(Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TransactionAddress {
|
pub struct TransactionAddress {
|
||||||
/// Block hash
|
/// Block hash
|
||||||
pub block_hash: H256,
|
pub block_hash: H256,
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
use util::rlp::*;
|
use util::rlp::*;
|
||||||
use util::{H256, H2048};
|
use util::{H256, H2048};
|
||||||
use util::U256;
|
|
||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
use header::Header;
|
use header::Header;
|
||||||
use transaction::SignedTransaction;
|
use transaction::SignedTransaction;
|
||||||
@@ -24,6 +23,7 @@ use transaction::SignedTransaction;
|
|||||||
use super::fork::Forkable;
|
use super::fork::Forkable;
|
||||||
use super::bloom::WithBloom;
|
use super::bloom::WithBloom;
|
||||||
use super::complete::CompleteBlock;
|
use super::complete::CompleteBlock;
|
||||||
|
use super::transaction::WithTransaction;
|
||||||
|
|
||||||
/// Helper structure, used for encoding blocks.
|
/// Helper structure, used for encoding blocks.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -44,7 +44,8 @@ impl Encodable for Block {
|
|||||||
|
|
||||||
impl Forkable for Block {
|
impl Forkable for Block {
|
||||||
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
||||||
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
|
let difficulty = self.header.difficulty().clone() - fork_number.into();
|
||||||
|
self.header.difficulty = difficulty;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +57,13 @@ impl WithBloom for Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WithTransaction for Block {
|
||||||
|
fn with_transaction(mut self, transaction: SignedTransaction) -> Self where Self: Sized {
|
||||||
|
self.transactions.push(transaction);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CompleteBlock for Block {
|
impl CompleteBlock for Block {
|
||||||
fn complete(mut self, parent_hash: H256) -> Bytes {
|
fn complete(mut self, parent_hash: H256) -> Bytes {
|
||||||
self.header.parent_hash = parent_hash;
|
self.header.parent_hash = parent_hash;
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ use util::hash::H2048;
|
|||||||
use util::numbers::U256;
|
use util::numbers::U256;
|
||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
use super::fork::Fork;
|
use super::fork::Fork;
|
||||||
use super::bloom::Bloom;
|
use super::bloom::Bloom;
|
||||||
use super::complete::{BlockFinalizer, CompleteBlock, Complete};
|
use super::complete::{BlockFinalizer, CompleteBlock, Complete};
|
||||||
use super::block::Block;
|
use super::block::Block;
|
||||||
|
use super::transaction::Transaction;
|
||||||
|
|
||||||
/// Chain iterator interface.
|
/// Chain iterator interface.
|
||||||
pub trait ChainIterator: Iterator + Sized {
|
pub trait ChainIterator: Iterator + Sized {
|
||||||
@@ -30,6 +32,8 @@ pub trait ChainIterator: Iterator + Sized {
|
|||||||
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
|
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
|
||||||
/// Should be called to make every consecutive block have given bloom.
|
/// Should be called to make every consecutive block have given bloom.
|
||||||
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self>;
|
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self>;
|
||||||
|
/// Should be called to make every consecutive block have given transaction.
|
||||||
|
fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction<Self>;
|
||||||
/// Should be called to complete block. Without complete, block may have incorrect hash.
|
/// Should be called to complete block. Without complete, block may have incorrect hash.
|
||||||
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
|
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
|
||||||
/// Completes and generates block.
|
/// Completes and generates block.
|
||||||
@@ -51,6 +55,13 @@ impl<I> ChainIterator for I where I: Iterator + Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction<Self> {
|
||||||
|
Transaction {
|
||||||
|
iter: self,
|
||||||
|
transaction: transaction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self> {
|
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self> {
|
||||||
Complete {
|
Complete {
|
||||||
iter: self,
|
iter: self,
|
||||||
@@ -85,7 +96,7 @@ impl Default for ChainGenerator {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ChainGenerator {
|
ChainGenerator {
|
||||||
number: 0,
|
number: 0,
|
||||||
difficulty: U256::from(1000),
|
difficulty: 1000.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ mod block;
|
|||||||
mod complete;
|
mod complete;
|
||||||
mod fork;
|
mod fork;
|
||||||
pub mod generator;
|
pub mod generator;
|
||||||
|
mod transaction;
|
||||||
|
|
||||||
pub use self::complete::BlockFinalizer;
|
pub use self::complete::BlockFinalizer;
|
||||||
pub use self::generator::{ChainIterator, ChainGenerator};
|
pub use self::generator::{ChainIterator, ChainGenerator};
|
||||||
|
|||||||
35
ethcore/src/blockchain/generator/transaction.rs
Normal file
35
ethcore/src/blockchain/generator/transaction.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// 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 transaction::SignedTransaction;
|
||||||
|
|
||||||
|
pub trait WithTransaction {
|
||||||
|
fn with_transaction(self, transaction: SignedTransaction) -> Self where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Transaction<'a, I> where I: 'a {
|
||||||
|
pub iter: &'a mut I,
|
||||||
|
pub transaction: SignedTransaction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a, I> Iterator for Transaction<'a, I> where I: Iterator, <I as Iterator>::Item: WithTransaction {
|
||||||
|
type Item = <I as Iterator>::Item;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next().map(|item| item.with_transaction(self.transaction.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,8 @@ pub struct ExtrasUpdate<'a> {
|
|||||||
pub block_details: HashMap<H256, BlockDetails>,
|
pub block_details: HashMap<H256, BlockDetails>,
|
||||||
/// Modified block receipts.
|
/// Modified block receipts.
|
||||||
pub block_receipts: HashMap<H256, BlockReceipts>,
|
pub block_receipts: HashMap<H256, BlockReceipts>,
|
||||||
/// Modified transaction addresses.
|
|
||||||
pub transactions_addresses: HashMap<H256, TransactionAddress>,
|
|
||||||
/// Modified blocks blooms.
|
/// Modified blocks blooms.
|
||||||
pub blocks_blooms: HashMap<LogGroupPosition, BloomGroup>,
|
pub blocks_blooms: HashMap<LogGroupPosition, BloomGroup>,
|
||||||
|
/// Modified transaction addresses (None signifies removed transactions).
|
||||||
|
pub transactions_addresses: HashMap<H256, Option<TransactionAddress>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,16 +45,19 @@ impl<T> CacheManager<T> where T: Eq + Hash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_garbage<C, F>(&mut self, current_size: C, mut notify_unused: F) where C: Fn() -> usize, F: FnMut(HashSet<T>) {
|
/// Collects unused objects from cache.
|
||||||
if current_size() < self.pref_cache_size {
|
/// First params is the current size of the cache.
|
||||||
|
/// Second one is an with objects to remove. It should also return new size of the cache.
|
||||||
|
pub fn collect_garbage<F>(&mut self, current_size: usize, mut notify_unused: F) where F: FnMut(HashSet<T>) -> usize {
|
||||||
|
if current_size < self.pref_cache_size {
|
||||||
self.rotate_cache_if_needed();
|
self.rotate_cache_if_needed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..COLLECTION_QUEUE_SIZE {
|
for _ in 0..COLLECTION_QUEUE_SIZE {
|
||||||
notify_unused(self.cache_usage.pop_back().unwrap());
|
let current_size = notify_unused(self.cache_usage.pop_back().unwrap());
|
||||||
self.cache_usage.push_front(Default::default());
|
self.cache_usage.push_front(Default::default());
|
||||||
if current_size() < self.max_cache_size {
|
if current_size < self.max_cache_size {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
use std::collections::{HashSet, HashMap, VecDeque};
|
use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::path::{Path};
|
use std::path::{Path};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -23,7 +23,6 @@ use time::precise_time_ns;
|
|||||||
|
|
||||||
// util
|
// util
|
||||||
use util::{journaldb, rlp, Bytes, View, PerfTimer, Itertools, Mutex, RwLock};
|
use util::{journaldb, rlp, Bytes, View, PerfTimer, Itertools, Mutex, RwLock};
|
||||||
use util::journaldb::JournalDB;
|
|
||||||
use util::rlp::{UntrustedRlp};
|
use util::rlp::{UntrustedRlp};
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
@@ -49,8 +48,11 @@ use types::filter::Filter;
|
|||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||||
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient,
|
use client::{
|
||||||
TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify};
|
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
|
||||||
|
MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
|
||||||
|
ChainNotify
|
||||||
|
};
|
||||||
use client::Error as ClientError;
|
use client::Error as ClientError;
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||||
@@ -62,6 +64,7 @@ use evm::Factory as EvmFactory;
|
|||||||
use miner::{Miner, MinerService};
|
use miner::{Miner, MinerService};
|
||||||
use util::TrieFactory;
|
use util::TrieFactory;
|
||||||
use snapshot::{self, io as snapshot_io};
|
use snapshot::{self, io as snapshot_io};
|
||||||
|
use state_db::StateDB;
|
||||||
|
|
||||||
// re-export
|
// re-export
|
||||||
pub use types::blockchain_info::BlockChainInfo;
|
pub use types::blockchain_info::BlockChainInfo;
|
||||||
@@ -121,7 +124,7 @@ pub struct Client {
|
|||||||
tracedb: Arc<TraceDB<BlockChain>>,
|
tracedb: Arc<TraceDB<BlockChain>>,
|
||||||
engine: Arc<Engine>,
|
engine: Arc<Engine>,
|
||||||
db: Arc<Database>,
|
db: Arc<Database>,
|
||||||
state_db: Mutex<Box<JournalDB>>,
|
state_db: Mutex<StateDB>,
|
||||||
block_queue: BlockQueue,
|
block_queue: BlockQueue,
|
||||||
report: RwLock<ClientReport>,
|
report: RwLock<ClientReport>,
|
||||||
import_lock: Mutex<()>,
|
import_lock: Mutex<()>,
|
||||||
@@ -151,8 +154,10 @@ pub const DB_COL_BODIES: Option<u32> = Some(2);
|
|||||||
pub const DB_COL_EXTRA: Option<u32> = Some(3);
|
pub const DB_COL_EXTRA: Option<u32> = Some(3);
|
||||||
/// Column for Traces
|
/// Column for Traces
|
||||||
pub const DB_COL_TRACE: Option<u32> = Some(4);
|
pub const DB_COL_TRACE: Option<u32> = Some(4);
|
||||||
|
/// Column for Traces
|
||||||
|
pub const DB_COL_ACCOUNT_BLOOM: Option<u32> = Some(5);
|
||||||
/// Number of columns in DB
|
/// Number of columns in DB
|
||||||
pub const DB_NO_OF_COLUMNS: Option<u32> = Some(5);
|
pub const DB_NO_OF_COLUMNS: Option<u32> = Some(6);
|
||||||
|
|
||||||
/// Append a path element to the given path and return the string.
|
/// 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> {
|
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {
|
||||||
@@ -181,14 +186,15 @@ impl Client {
|
|||||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
|
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
|
||||||
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
|
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
|
||||||
|
|
||||||
let mut state_db = journaldb::new(db.clone(), config.pruning, DB_COL_STATE);
|
let journal_db = journaldb::new(db.clone(), config.pruning, DB_COL_STATE);
|
||||||
if state_db.is_empty() && try!(spec.ensure_db_good(state_db.as_hashdb_mut())) {
|
let mut state_db = StateDB::new(journal_db);
|
||||||
|
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
||||||
let batch = DBTransaction::new(&db);
|
let batch = DBTransaction::new(&db);
|
||||||
try!(state_db.commit(&batch, 0, &spec.genesis_header().hash(), None));
|
try!(state_db.commit(&batch, 0, &spec.genesis_header().hash(), None));
|
||||||
try!(db.write(batch).map_err(ClientError::Database));
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
|
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
||||||
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +304,8 @@ impl Client {
|
|||||||
// Enact Verified Block
|
// Enact Verified Block
|
||||||
let parent = chain_has_parent.unwrap();
|
let parent = chain_has_parent.unwrap();
|
||||||
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
||||||
let db = self.state_db.lock().boxed_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 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 {
|
if let Err(e) = enact_result {
|
||||||
@@ -440,7 +447,8 @@ impl Client {
|
|||||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||||
// already-imported block of the same number.
|
// already-imported block of the same number.
|
||||||
// TODO: Prove it with a test.
|
// TODO: Prove it with a test.
|
||||||
block.drain().commit(&batch, number, hash, ancient).expect("DB commit failed.");
|
let mut state = block.drain();
|
||||||
|
state.commit(&batch, number, hash, ancient).expect("DB commit failed.");
|
||||||
|
|
||||||
let route = self.chain.insert_block(&batch, block_data, receipts);
|
let route = self.chain.insert_block(&batch, block_data, receipts);
|
||||||
self.tracedb.import(&batch, TraceImportRequest {
|
self.tracedb.import(&batch, TraceImportRequest {
|
||||||
@@ -453,7 +461,6 @@ impl Client {
|
|||||||
// Final commit to the DB
|
// Final commit to the DB
|
||||||
self.db.write_buffered(batch).expect("DB write failed.");
|
self.db.write_buffered(batch).expect("DB write failed.");
|
||||||
self.chain.commit();
|
self.chain.commit();
|
||||||
|
|
||||||
self.update_last_hashes(&parent, hash);
|
self.update_last_hashes(&parent, hash);
|
||||||
route
|
route
|
||||||
}
|
}
|
||||||
@@ -594,19 +601,35 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a snapshot.
|
/// Take a snapshot at the given block.
|
||||||
pub fn take_snapshot<W: snapshot_io::SnapshotWriter + Send>(&self, writer: W) -> Result<(), ::error::Error> {
|
/// If the ID given is "latest", this will default to 1000 blocks behind.
|
||||||
let db = self.state_db.lock().boxed_clone();
|
pub fn take_snapshot<W: snapshot_io::SnapshotWriter + Send>(&self, writer: W, at: BlockID, p: &snapshot::Progress) -> Result<(), ::error::Error> {
|
||||||
|
let db = self.state_db.lock().journal_db().boxed_clone();
|
||||||
let best_block_number = self.chain_info().best_block_number;
|
let best_block_number = self.chain_info().best_block_number;
|
||||||
let start_block_number = if best_block_number > 1000 {
|
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
||||||
best_block_number - 1000
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
let start_hash = self.block_hash(BlockID::Number(start_block_number))
|
|
||||||
.expect("blocks within HISTORY are always stored.");
|
|
||||||
|
|
||||||
try!(snapshot::take_snapshot(&self.chain, start_hash, db.as_hashdb(), writer));
|
if best_block_number > HISTORY + block_number && db.is_pruned() {
|
||||||
|
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_hash = match at {
|
||||||
|
BlockID::Latest => {
|
||||||
|
let start_num = if best_block_number > 1000 {
|
||||||
|
best_block_number - 1000
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.block_hash(BlockID::Number(start_num))
|
||||||
|
.expect("blocks within HISTORY are always stored.")
|
||||||
|
}
|
||||||
|
_ => match self.block_hash(at) {
|
||||||
|
Some(hash) => hash,
|
||||||
|
None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try!(snapshot::take_snapshot(&self.chain, start_hash, db.as_hashdb(), writer, p));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -784,8 +807,8 @@ impl BlockChainClient for Client {
|
|||||||
Self::block_hash(&self.chain, id)
|
Self::block_hash(&self.chain, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(&self, address: &Address) -> Option<Bytes> {
|
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
|
||||||
self.state().code(address)
|
self.state_at(id).map(|s| s.code(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
|
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
|
||||||
@@ -862,7 +885,7 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn state_data(&self, hash: &H256) -> Option<Bytes> {
|
fn state_data(&self, hash: &H256) -> Option<Bytes> {
|
||||||
self.state_db.lock().state(hash)
|
self.state_db.lock().journal_db().state(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||||
@@ -900,6 +923,10 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||||
|
self.engine.additional_params().into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>> {
|
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>> {
|
||||||
match (self.block_number(from_block), self.block_number(to_block)) {
|
match (self.block_number(from_block), self.block_number(to_block)) {
|
||||||
(Some(from), Some(to)) => Some(self.chain.blocks_with_bloom(bloom, from, to)),
|
(Some(from), Some(to)) => Some(self.chain.blocks_with_bloom(bloom, from, to)),
|
||||||
@@ -1057,6 +1084,8 @@ impl MiningBlockChainClient for Client {
|
|||||||
let number = block.header().number();
|
let number = block.header().number();
|
||||||
|
|
||||||
let block_data = block.rlp_bytes();
|
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);
|
let route = self.commit_block(block, &h, &block_data);
|
||||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ use util::*;
|
|||||||
use devtools::*;
|
use devtools::*;
|
||||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
||||||
use blockchain::TreeRoute;
|
use blockchain::TreeRoute;
|
||||||
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
|
use client::{
|
||||||
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics,
|
BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
|
||||||
BlockImportError};
|
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
|
||||||
|
DB_NO_OF_COLUMNS, DB_COL_STATE,
|
||||||
|
};
|
||||||
use header::{Header as BlockHeader, BlockNumber};
|
use header::{Header as BlockHeader, BlockNumber};
|
||||||
use filter::Filter;
|
use filter::Filter;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
@@ -39,6 +41,7 @@ use block::{OpenBlock, SealedBlock};
|
|||||||
use executive::Executed;
|
use executive::Executed;
|
||||||
use error::CallError;
|
use error::CallError;
|
||||||
use trace::LocalizedTrace;
|
use trace::LocalizedTrace;
|
||||||
|
use state_db::StateDB;
|
||||||
|
|
||||||
/// Test client.
|
/// Test client.
|
||||||
pub struct TestBlockChainClient {
|
pub struct TestBlockChainClient {
|
||||||
@@ -246,27 +249,28 @@ impl TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
|
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let db = Database::open_default(temp.as_str()).unwrap();
|
let db = Database::open(&DatabaseConfig::with_columns(DB_NO_OF_COLUMNS), temp.as_str()).unwrap();
|
||||||
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, None);
|
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, DB_COL_STATE);
|
||||||
|
let state_db = StateDB::new(journal_db);
|
||||||
GuardedTempResult {
|
GuardedTempResult {
|
||||||
_temp: temp,
|
_temp: temp,
|
||||||
result: Some(journal_db)
|
result: Some(state_db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MiningBlockChainClient for TestBlockChainClient {
|
impl MiningBlockChainClient for TestBlockChainClient {
|
||||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
||||||
let engine = &self.spec.engine;
|
let engine = &*self.spec.engine;
|
||||||
let genesis_header = self.spec.genesis_header();
|
let genesis_header = self.spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
self.spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
self.spec.ensure_db_good(&mut db).unwrap();
|
||||||
|
|
||||||
let last_hashes = vec![genesis_header.hash()];
|
let last_hashes = vec![genesis_header.hash()];
|
||||||
let mut open_block = OpenBlock::new(
|
let mut open_block = OpenBlock::new(
|
||||||
engine.deref(),
|
engine,
|
||||||
self.vm_factory(),
|
self.vm_factory(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
false,
|
false,
|
||||||
@@ -319,8 +323,11 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
self.nonce(address, BlockID::Latest).unwrap()
|
self.nonce(address, BlockID::Latest).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(&self, address: &Address) -> Option<Bytes> {
|
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
|
||||||
self.code.read().get(address).cloned()
|
match id {
|
||||||
|
BlockID::Latest => Some(self.code.read().get(address).cloned()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
|
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
|
||||||
@@ -344,11 +351,11 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
|
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
|
||||||
unimplemented!();
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
|
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
|
||||||
unimplemented!();
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
|
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
|
||||||
@@ -479,9 +486,9 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
if number == len {
|
if number == len {
|
||||||
{
|
{
|
||||||
let mut difficulty = self.difficulty.write();
|
let mut difficulty = self.difficulty.write();
|
||||||
*difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
|
*difficulty = *difficulty + header.difficulty;
|
||||||
}
|
}
|
||||||
mem::replace(self.last_hash.write().deref_mut(), h.clone());
|
mem::replace(&mut *self.last_hash.write(), h.clone());
|
||||||
self.blocks.write().insert(h.clone(), b);
|
self.blocks.write().insert(h.clone(), b);
|
||||||
self.numbers.write().insert(number, h.clone());
|
self.numbers.write().insert(number, h.clone());
|
||||||
let mut parent_hash = header.parent_hash;
|
let mut parent_hash = header.parent_hash;
|
||||||
@@ -514,6 +521,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
fn clear_queue(&self) {
|
fn clear_queue(&self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
BlockChainInfo {
|
BlockChainInfo {
|
||||||
total_difficulty: *self.difficulty.read(),
|
total_difficulty: *self.difficulty.read(),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::collections::{BTreeMap};
|
||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
use util::hash::{Address, H256, H2048};
|
use util::hash::{Address, H256, H2048};
|
||||||
use util::numbers::U256;
|
use util::numbers::U256;
|
||||||
@@ -81,8 +82,14 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get block hash.
|
/// Get block hash.
|
||||||
fn block_hash(&self, id: BlockID) -> Option<H256>;
|
fn block_hash(&self, id: BlockID) -> Option<H256>;
|
||||||
|
|
||||||
/// Get address code.
|
/// Get address code at given block's state.
|
||||||
fn code(&self, address: &Address) -> Option<Bytes>;
|
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>>;
|
||||||
|
|
||||||
|
/// Get address code at the latest block's state.
|
||||||
|
fn latest_code(&self, address: &Address) -> Option<Bytes> {
|
||||||
|
self.code(address, BlockID::Latest)
|
||||||
|
.expect("code will return Some if given BlockID::Latest; qed")
|
||||||
|
}
|
||||||
|
|
||||||
/// Get address balance at the given block's state.
|
/// Get address balance at the given block's state.
|
||||||
///
|
///
|
||||||
@@ -144,6 +151,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get blockchain information.
|
/// Get blockchain information.
|
||||||
fn chain_info(&self) -> BlockChainInfo;
|
fn chain_info(&self) -> BlockChainInfo;
|
||||||
|
|
||||||
|
/// Get the registrar address, if it exists.
|
||||||
|
fn additional_params(&self) -> BTreeMap<String, String>;
|
||||||
|
|
||||||
/// Get the best block header.
|
/// Get the best block header.
|
||||||
fn best_block_header(&self) -> Bytes;
|
fn best_block_header(&self) -> Bytes;
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ pub trait Writable {
|
|||||||
/// Writes the value into the database.
|
/// Writes the value into the database.
|
||||||
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
||||||
|
|
||||||
|
/// Deletes key from the databse.
|
||||||
|
fn delete<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) where T: Encodable, R: Deref<Target = [u8]>;
|
||||||
|
|
||||||
/// Writes the value into the database and updates the cache.
|
/// Writes the value into the database and updates the cache.
|
||||||
fn write_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
fn write_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||||
K: Key<T, Target = R> + Hash + Eq,
|
K: Key<T, Target = R> + Hash + Eq,
|
||||||
@@ -100,6 +103,34 @@ pub trait Writable {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes and removes the values into the database and updates the cache.
|
||||||
|
fn extend_with_option_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, Option<T>>, values: HashMap<K, Option<T>>, policy: CacheUpdatePolicy) where
|
||||||
|
K: Key<T, Target = R> + Hash + Eq,
|
||||||
|
T: Encodable,
|
||||||
|
R: Deref<Target = [u8]> {
|
||||||
|
match policy {
|
||||||
|
CacheUpdatePolicy::Overwrite => {
|
||||||
|
for (key, value) in values.into_iter() {
|
||||||
|
match value {
|
||||||
|
Some(ref v) => self.write(col, &key, v),
|
||||||
|
None => self.delete(col, &key),
|
||||||
|
}
|
||||||
|
cache.insert(key, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CacheUpdatePolicy::Remove => {
|
||||||
|
for (key, value) in values.into_iter() {
|
||||||
|
match value {
|
||||||
|
Some(v) => self.write(col, &key, &v),
|
||||||
|
None => self.delete(col, &key),
|
||||||
|
}
|
||||||
|
cache.remove(&key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should be used to read values from database.
|
/// Should be used to read values from database.
|
||||||
@@ -154,6 +185,13 @@ impl Writable for DBTransaction {
|
|||||||
panic!("db put failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
panic!("db put failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn delete<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) where T: Encodable, R: Deref<Target = [u8]> {
|
||||||
|
let result = DBTransaction::delete(self, col, &key.key());
|
||||||
|
if let Err(err) = result {
|
||||||
|
panic!("db delete failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Readable for Database {
|
impl Readable for Database {
|
||||||
|
|||||||
@@ -246,16 +246,16 @@ mod tests {
|
|||||||
tap.unlock_account_permanently(addr, "".into()).unwrap();
|
tap.unlock_account_permanently(addr, "".into()).unwrap();
|
||||||
|
|
||||||
let spec = new_test_authority();
|
let spec = new_test_authority();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let b = b.close_and_lock();
|
let b = b.close_and_lock();
|
||||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||||
assert!(b.try_seal(engine.deref(), seal).is_ok());
|
assert!(b.try_seal(engine, seal).is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,18 +80,18 @@ mod tests {
|
|||||||
let addr = tap.insert_account("".sha3(), "").unwrap();
|
let addr = tap.insert_account("".sha3(), "").unwrap();
|
||||||
|
|
||||||
let spec = new_test_instant();
|
let spec = new_test_instant();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let b = b.close_and_lock();
|
let b = b.close_and_lock();
|
||||||
// Seal with empty AccountProvider.
|
// Seal with empty AccountProvider.
|
||||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||||
assert!(b.try_seal(engine.deref(), seal).is_ok());
|
assert!(b.try_seal(engine, seal).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ pub trait Engine : Sync + Send {
|
|||||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||||
|
|
||||||
|
/// Additional information.
|
||||||
|
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
|
||||||
|
|
||||||
/// Get the general parameters of the chain.
|
/// Get the general parameters of the chain.
|
||||||
fn params(&self) -> &CommonParams;
|
fn params(&self) -> &CommonParams;
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ impl Engine for Ethash {
|
|||||||
fn seal_fields(&self) -> usize { 2 }
|
fn seal_fields(&self) -> usize { 2 }
|
||||||
|
|
||||||
fn params(&self) -> &CommonParams { &self.params }
|
fn params(&self) -> &CommonParams { &self.params }
|
||||||
|
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.ethash_params.registrar.hex()] }
|
||||||
|
|
||||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||||
&self.builtins
|
&self.builtins
|
||||||
@@ -350,14 +351,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn on_close_block() {
|
fn on_close_block() {
|
||||||
let spec = new_morden();
|
let spec = new_morden();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let b = b.close();
|
let b = b.close();
|
||||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
||||||
}
|
}
|
||||||
@@ -365,14 +366,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn on_close_block_with_uncle() {
|
fn on_close_block_with_uncle() {
|
||||||
let spec = new_morden();
|
let spec = new_morden();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let mut b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
let mut b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||||
let mut uncle = Header::new();
|
let mut uncle = Header::new();
|
||||||
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
|
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
|
||||||
uncle.author = uncle_author.clone();
|
uncle.author = uncle_author.clone();
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ mod tests {
|
|||||||
let spec = new_morden();
|
let spec = new_morden();
|
||||||
let engine = &spec.engine;
|
let engine = &spec.engine;
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
spec.ensure_db_good(&mut db).unwrap();
|
||||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
|
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
|
||||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
|
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
|
||||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
|
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
|
||||||
|
|||||||
@@ -151,10 +151,14 @@ impl CostType for usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn from_u256(val: U256) -> Result<Self> {
|
fn from_u256(val: U256) -> Result<Self> {
|
||||||
if U256::from(val.low_u64()) != val {
|
let res = val.low_u64() as usize;
|
||||||
|
|
||||||
|
// validate if value fits into usize
|
||||||
|
if U256::from(res) != val {
|
||||||
return Err(Error::OutOfGas);
|
return Err(Error::OutOfGas);
|
||||||
}
|
}
|
||||||
Ok(val.low_u64() as usize)
|
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_usize(&self) -> usize {
|
fn as_usize(&self) -> usize {
|
||||||
@@ -191,6 +195,7 @@ pub trait Evm {
|
|||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(test)]
|
||||||
fn should_calculate_overflow_mul_shr_without_overflow() {
|
fn should_calculate_overflow_mul_shr_without_overflow() {
|
||||||
// given
|
// given
|
||||||
let num = 1048576;
|
let num = 1048576;
|
||||||
@@ -207,6 +212,7 @@ fn should_calculate_overflow_mul_shr_without_overflow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(test)]
|
||||||
fn should_calculate_overflow_mul_shr_with_overflow() {
|
fn should_calculate_overflow_mul_shr_with_overflow() {
|
||||||
// given
|
// given
|
||||||
let max = ::std::u64::MAX;
|
let max = ::std::u64::MAX;
|
||||||
@@ -225,3 +231,15 @@ fn should_calculate_overflow_mul_shr_with_overflow() {
|
|||||||
assert!(o1);
|
assert!(o1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(test)]
|
||||||
|
fn should_validate_u256_to_usize_conversion() {
|
||||||
|
// given
|
||||||
|
let v = U256::from(::std::usize::MAX) + U256::from(1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = usize::from_u256(v);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ pub trait Ext {
|
|||||||
/// Returns code at given address
|
/// Returns code at given address
|
||||||
fn extcode(&self, address: &Address) -> Bytes;
|
fn extcode(&self, address: &Address) -> Bytes;
|
||||||
|
|
||||||
|
/// Returns code length in bytes at given address
|
||||||
|
fn extcode_len(&self, address: &Address) -> u64;
|
||||||
|
|
||||||
/// Creates log entry with given topics and data
|
/// Creates log entry with given topics and data
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]);
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]);
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,17 @@ fn color(instruction: Instruction, name: &'static str) -> String {
|
|||||||
type CodePosition = usize;
|
type CodePosition = usize;
|
||||||
type ProgramCounter = usize;
|
type ProgramCounter = usize;
|
||||||
|
|
||||||
|
const ONE: U256 = U256([1, 0, 0, 0]);
|
||||||
|
const TWO: U256 = U256([2, 0, 0, 0]);
|
||||||
|
const TWO_POW_5: U256 = U256([0x20, 0, 0, 0]);
|
||||||
|
const TWO_POW_8: U256 = U256([0x100, 0, 0, 0]);
|
||||||
|
const TWO_POW_16: U256 = U256([0x10000, 0, 0, 0]);
|
||||||
|
const TWO_POW_24: U256 = U256([0x1000000, 0, 0, 0]);
|
||||||
|
const TWO_POW_64: U256 = U256([0, 0x1, 0, 0]); // 0x1 00000000 00000000
|
||||||
|
const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 00000000
|
||||||
|
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||||
|
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
|
||||||
|
|
||||||
/// Abstraction over raw vector of Bytes. Easier state management of PC.
|
/// Abstraction over raw vector of Bytes. Easier state management of PC.
|
||||||
struct CodeReader<'a> {
|
struct CodeReader<'a> {
|
||||||
position: ProgramCounter,
|
position: ProgramCounter,
|
||||||
@@ -471,7 +482,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
},
|
},
|
||||||
instructions::EXTCODESIZE => {
|
instructions::EXTCODESIZE => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let len = ext.extcode(&address).len();
|
let len = ext.extcode_len(&address);
|
||||||
stack.push(U256::from(len));
|
stack.push(U256::from(len));
|
||||||
},
|
},
|
||||||
instructions::CALLDATACOPY => {
|
instructions::CALLDATACOPY => {
|
||||||
@@ -515,11 +526,11 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
Ok(InstructionResult::Ok)
|
Ok(InstructionResult::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, data: &[u8]) {
|
fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, source: &[u8]) {
|
||||||
let dest_offset = stack.pop_back();
|
let dest_offset = stack.pop_back();
|
||||||
let source_offset = stack.pop_back();
|
let source_offset = stack.pop_back();
|
||||||
let size = stack.pop_back();
|
let size = stack.pop_back();
|
||||||
let source_size = U256::from(data.len());
|
let source_size = U256::from(source.len());
|
||||||
|
|
||||||
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
|
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
|
||||||
true => {
|
true => {
|
||||||
@@ -531,14 +542,14 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
for i in zero_slice.iter_mut() {
|
for i in zero_slice.iter_mut() {
|
||||||
*i = 0;
|
*i = 0;
|
||||||
}
|
}
|
||||||
data.len()
|
source.len()
|
||||||
},
|
},
|
||||||
false => (size.low_u64() + source_offset.low_u64()) as usize
|
false => (size.low_u64() + source_offset.low_u64()) as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
if source_offset < source_size {
|
if source_offset < source_size {
|
||||||
let output_begin = source_offset.low_u64() as usize;
|
let output_begin = source_offset.low_u64() as usize;
|
||||||
self.mem.write_slice(dest_offset, &data[output_begin..output_end]);
|
self.mem.write_slice(dest_offset, &source[output_begin..output_end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,7 +610,19 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
let a = stack.pop_back();
|
let a = stack.pop_back();
|
||||||
let b = stack.pop_back();
|
let b = stack.pop_back();
|
||||||
stack.push(if !self.is_zero(&b) {
|
stack.push(if !self.is_zero(&b) {
|
||||||
a.overflowing_div(b).0
|
match b {
|
||||||
|
ONE => a,
|
||||||
|
TWO => a >> 1,
|
||||||
|
TWO_POW_5 => a >> 5,
|
||||||
|
TWO_POW_8 => a >> 8,
|
||||||
|
TWO_POW_16 => a >> 16,
|
||||||
|
TWO_POW_24 => a >> 24,
|
||||||
|
TWO_POW_64 => a >> 64,
|
||||||
|
TWO_POW_96 => a >> 96,
|
||||||
|
TWO_POW_224 => a >> 224,
|
||||||
|
TWO_POW_248 => a >> 248,
|
||||||
|
_ => a.overflowing_div(b).0,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
U256::zero()
|
U256::zero()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use evmjit;
|
use evmjit;
|
||||||
use evm::{self, GasLeft};
|
use evm::{self, GasLeft};
|
||||||
|
use types::executed::CallType;
|
||||||
|
|
||||||
/// Should be used to convert jit types to ethcore
|
/// Should be used to convert jit types to ethcore
|
||||||
trait FromJit<T>: Sized {
|
trait FromJit<T>: Sized {
|
||||||
@@ -77,10 +78,11 @@ impl IntoJit<evmjit::I256> for U256 {
|
|||||||
impl IntoJit<evmjit::I256> for H256 {
|
impl IntoJit<evmjit::I256> for H256 {
|
||||||
fn into_jit(self) -> evmjit::I256 {
|
fn into_jit(self) -> evmjit::I256 {
|
||||||
let mut ret = [0; 4];
|
let mut ret = [0; 4];
|
||||||
for i in 0..self.bytes().len() {
|
let len = self.len();
|
||||||
let rev = self.bytes().len() - 1 - i;
|
for i in 0..len {
|
||||||
|
let rev = len - 1 - i;
|
||||||
let pos = rev / 8;
|
let pos = rev / 8;
|
||||||
ret[pos] += (self.bytes()[i] as u64) << ((rev % 8) * 8);
|
ret[pos] += (self[i] as u64) << ((rev % 8) * 8);
|
||||||
}
|
}
|
||||||
evmjit::I256 { words: ret }
|
evmjit::I256 { words: ret }
|
||||||
}
|
}
|
||||||
@@ -194,6 +196,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
receive_address: *const evmjit::H256,
|
receive_address: *const evmjit::H256,
|
||||||
code_address: *const evmjit::H256,
|
code_address: *const evmjit::H256,
|
||||||
transfer_value: *const evmjit::I256,
|
transfer_value: *const evmjit::I256,
|
||||||
|
// Ignoring apparent value - it's handled correctly in executive.
|
||||||
_apparent_value: *const evmjit::I256,
|
_apparent_value: *const evmjit::I256,
|
||||||
in_beg: *const u8,
|
in_beg: *const u8,
|
||||||
in_size: u64,
|
in_size: u64,
|
||||||
@@ -207,10 +210,12 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
let receive_address = unsafe { Address::from_jit(&*receive_address) };
|
let receive_address = unsafe { Address::from_jit(&*receive_address) };
|
||||||
let code_address = unsafe { Address::from_jit(&*code_address) };
|
let code_address = unsafe { Address::from_jit(&*code_address) };
|
||||||
let transfer_value = unsafe { U256::from_jit(&*transfer_value) };
|
let transfer_value = unsafe { U256::from_jit(&*transfer_value) };
|
||||||
let value = Some(transfer_value);
|
|
||||||
|
|
||||||
// receive address and code address are the same in normal calls
|
// receive address and code address are the same in normal calls
|
||||||
let is_callcode = receive_address != code_address;
|
let is_callcode = receive_address != code_address;
|
||||||
|
let is_delegatecall = is_callcode && sender_address != receive_address;
|
||||||
|
|
||||||
|
let value = if is_delegatecall { None } else { Some(transfer_value) };
|
||||||
|
|
||||||
if !is_callcode && !self.ext.exists(&code_address) {
|
if !is_callcode && !self.ext.exists(&code_address) {
|
||||||
gas_cost = gas_cost + U256::from(self.ext.schedule().call_new_account_gas);
|
gas_cost = gas_cost + U256::from(self.ext.schedule().call_new_account_gas);
|
||||||
@@ -239,6 +244,12 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let call_type = match (is_callcode, is_delegatecall) {
|
||||||
|
(_, true) => CallType::DelegateCall,
|
||||||
|
(true, false) => CallType::CallCode,
|
||||||
|
(false, false) => CallType::Call,
|
||||||
|
};
|
||||||
|
|
||||||
match self.ext.call(
|
match self.ext.call(
|
||||||
&call_gas,
|
&call_gas,
|
||||||
&sender_address,
|
&sender_address,
|
||||||
@@ -246,7 +257,9 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
value,
|
value,
|
||||||
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
|
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
|
||||||
&code_address,
|
&code_address,
|
||||||
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) {
|
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) },
|
||||||
|
call_type,
|
||||||
|
) {
|
||||||
evm::MessageCallResult::Success(gas_left) => unsafe {
|
evm::MessageCallResult::Success(gas_left) => unsafe {
|
||||||
*io_gas = (gas + gas_left).low_u64();
|
*io_gas = (gas + gas_left).low_u64();
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ impl Ext for FakeExt {
|
|||||||
self.codes.get(address).unwrap_or(&Bytes::new()).clone()
|
self.codes.get(address).unwrap_or(&Bytes::new()).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extcode_len(&self, address: &Address) -> u64 {
|
||||||
|
self.codes.get(address).map_or(0, |c| c.len() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
||||||
self.logs.push(FakeLogEntry {
|
self.logs.push(FakeLogEntry {
|
||||||
topics: topics,
|
topics: topics,
|
||||||
|
|||||||
@@ -206,6 +206,10 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
|||||||
self.state.code(address).unwrap_or_else(|| vec![])
|
self.state.code(address).unwrap_or_else(|| vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extcode_len(&self, address: &Address) -> u64 {
|
||||||
|
self.state.code_size(address).unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
||||||
fn ret(mut self, gas: &U256, data: &[u8]) -> evm::Result<U256>
|
fn ret(mut self, gas: &U256, data: &[u8]) -> evm::Result<U256>
|
||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
|
|||||||
@@ -132,6 +132,10 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
|
|||||||
self.ext.extcode(address)
|
self.ext.extcode(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extcode_len(&self, address: &Address) -> u64 {
|
||||||
|
self.ext.extcode_len(address)
|
||||||
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
|
||||||
self.ext.log(topics, data)
|
self.ext.log(topics, data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
state.commit()
|
state.commit()
|
||||||
.expect(&format!("State test {} failed due to internal error.", name));
|
.expect(&format!("State test {} failed due to internal error.", name));
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let res = state.apply(&env, engine.deref(), &vm_factory, &transaction, false);
|
let res = state.apply(&env, &*engine, &vm_factory, &transaction, false);
|
||||||
|
|
||||||
if fail_unless(state.root() == &post_state_root) {
|
if fail_unless(state.root() == &post_state_root) {
|
||||||
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
|
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#![cfg_attr(feature="dev", allow(match_bool))]
|
#![cfg_attr(feature="dev", allow(match_bool))]
|
||||||
// Keeps consistency (all lines with `.clone()`).
|
// Keeps consistency (all lines with `.clone()`).
|
||||||
#![cfg_attr(feature="dev", allow(clone_on_copy))]
|
#![cfg_attr(feature="dev", allow(clone_on_copy))]
|
||||||
|
// Complains on Box<E> when implementing From<Box<E>>
|
||||||
|
#![cfg_attr(feature="dev", allow(boxed_local))]
|
||||||
// TODO [todr] a lot of warnings to be fixed
|
// TODO [todr] a lot of warnings to be fixed
|
||||||
#![cfg_attr(feature="dev", allow(assign_op_pattern))]
|
#![cfg_attr(feature="dev", allow(assign_op_pattern))]
|
||||||
|
|
||||||
@@ -98,8 +100,12 @@ extern crate ethcore_ipc_nano as nanoipc;
|
|||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate bit_set;
|
extern crate bit_set;
|
||||||
|
extern crate lru_cache;
|
||||||
|
extern crate bloomfilter;
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
#[cfg(feature = "jit" )]
|
||||||
|
extern crate evmjit;
|
||||||
|
|
||||||
pub mod account_provider;
|
pub mod account_provider;
|
||||||
pub mod engines;
|
pub mod engines;
|
||||||
@@ -128,6 +134,7 @@ mod basic_types;
|
|||||||
mod env_info;
|
mod env_info;
|
||||||
mod pod_account;
|
mod pod_account;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod state_db;
|
||||||
mod account;
|
mod account;
|
||||||
mod account_db;
|
mod account_db;
|
||||||
mod builtin;
|
mod builtin;
|
||||||
|
|||||||
104
ethcore/src/migrations/account_bloom.rs
Normal file
104
ethcore/src/migrations/account_bloom.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Bloom upgrade
|
||||||
|
|
||||||
|
use client::{DB_COL_EXTRA, DB_COL_HEADERS, DB_NO_OF_COLUMNS, DB_COL_STATE, DB_COL_ACCOUNT_BLOOM};
|
||||||
|
use state_db::{ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET, StateDB, ACCOUNT_BLOOM_HASHCOUNT_KEY};
|
||||||
|
use util::trie::TrieDB;
|
||||||
|
use views::HeaderView;
|
||||||
|
use bloomfilter::Bloom;
|
||||||
|
use util::migration::Error;
|
||||||
|
use util::journaldb;
|
||||||
|
use util::{H256, FixedHash, BytesConvertable};
|
||||||
|
use util::{Database, DatabaseConfig, DBTransaction, CompactionProfile};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn check_bloom_exists(db: &Database) -> bool {
|
||||||
|
let hash_count_entry = db.get(DB_COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
|
||||||
|
.expect("Low-level database error");
|
||||||
|
|
||||||
|
hash_count_entry.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Account bloom upgrade routine. If bloom already present, does nothing.
|
||||||
|
/// If database empty (no best block), does nothing.
|
||||||
|
/// Can be called on upgraded database with no issues (will do nothing).
|
||||||
|
pub fn upgrade_account_bloom(db_path: &Path) -> Result<(), Error> {
|
||||||
|
let path = try!(db_path.to_str().ok_or(Error::MigrationImpossible));
|
||||||
|
trace!(target: "migration", "Account bloom upgrade at {:?}", db_path);
|
||||||
|
|
||||||
|
let source = try!(Database::open(&DatabaseConfig {
|
||||||
|
max_open_files: 64,
|
||||||
|
cache_size: None,
|
||||||
|
compaction: CompactionProfile::default(),
|
||||||
|
columns: DB_NO_OF_COLUMNS,
|
||||||
|
wal: true,
|
||||||
|
}, path));
|
||||||
|
|
||||||
|
let best_block_hash = match try!(source.get(DB_COL_EXTRA, b"best")) {
|
||||||
|
// no migration needed
|
||||||
|
None => {
|
||||||
|
trace!(target: "migration", "No best block hash, skipping");
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
Some(hash) => hash,
|
||||||
|
};
|
||||||
|
let best_block_header = match try!(source.get(DB_COL_HEADERS, &best_block_hash)) {
|
||||||
|
// no best block, nothing to do
|
||||||
|
None => {
|
||||||
|
trace!(target: "migration", "No best block header, skipping");
|
||||||
|
return Ok(())
|
||||||
|
},
|
||||||
|
Some(x) => x,
|
||||||
|
};
|
||||||
|
let state_root = HeaderView::new(&best_block_header).state_root();
|
||||||
|
|
||||||
|
if check_bloom_exists(&source) {
|
||||||
|
// bloom already exists, nothing to do
|
||||||
|
trace!(target: "migration", "Bloom already present, skipping");
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Adding accounts bloom (one-time upgrade)");
|
||||||
|
let db = ::std::sync::Arc::new(source);
|
||||||
|
let bloom_journal = {
|
||||||
|
let mut bloom = Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET);
|
||||||
|
// no difference what algorithm is passed, since there will be no writes
|
||||||
|
let state_db = journaldb::new(
|
||||||
|
db.clone(),
|
||||||
|
journaldb::Algorithm::OverlayRecent,
|
||||||
|
DB_COL_STATE);
|
||||||
|
let account_trie = try!(TrieDB::new(state_db.as_hashdb(), &state_root).map_err(|e| Error::Custom(format!("Cannot open trie: {:?}", e))));
|
||||||
|
for (ref account_key, _) in account_trie.iter() {
|
||||||
|
let account_key_hash = H256::from_slice(&account_key);
|
||||||
|
bloom.set(account_key_hash.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
bloom.drain_journal()
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!(target: "migration", "Generated {} bloom updates", bloom_journal.entries.len());
|
||||||
|
|
||||||
|
let batch = DBTransaction::new(&db);
|
||||||
|
try!(StateDB::commit_bloom(&batch, bloom_journal).map_err(|_| Error::Custom("Failed to commit bloom".to_owned())));
|
||||||
|
try!(db.write(batch));
|
||||||
|
|
||||||
|
trace!(target: "migration", "Finished bloom update");
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -23,3 +23,9 @@ pub mod extras;
|
|||||||
mod v9;
|
mod v9;
|
||||||
pub use self::v9::ToV9;
|
pub use self::v9::ToV9;
|
||||||
pub use self::v9::Extract;
|
pub use self::v9::Extract;
|
||||||
|
|
||||||
|
mod account_bloom;
|
||||||
|
pub use self::account_bloom::upgrade_account_bloom;
|
||||||
|
|
||||||
|
mod v10;
|
||||||
|
pub use self::v10::ToV10;
|
||||||
|
|||||||
35
ethcore/src/migrations/v10.rs
Normal file
35
ethcore/src/migrations/v10.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! This migration compresses the state db.
|
||||||
|
|
||||||
|
use util::migration::SimpleMigration;
|
||||||
|
|
||||||
|
/// Compressing migration.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ToV10;
|
||||||
|
|
||||||
|
impl SimpleMigration for ToV10 {
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
|
||||||
|
fn columns(&self) -> Option<u32> { Some(6) }
|
||||||
|
|
||||||
|
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||||
|
Some((key, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,10 +23,11 @@ use account_provider::AccountProvider;
|
|||||||
use views::{BlockView, HeaderView};
|
use views::{BlockView, HeaderView};
|
||||||
use state::State;
|
use state::State;
|
||||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||||
|
use executive::contract_address;
|
||||||
use block::{ClosedBlock, IsBlock, Block};
|
use block::{ClosedBlock, IsBlock, Block};
|
||||||
use error::*;
|
use error::*;
|
||||||
use transaction::SignedTransaction;
|
use transaction::{Action, SignedTransaction};
|
||||||
use receipt::Receipt;
|
use receipt::{Receipt, RichReceipt};
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
|
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
|
||||||
@@ -168,12 +169,11 @@ pub struct Miner {
|
|||||||
// NOTE [ToDr] When locking always lock in this order!
|
// NOTE [ToDr] When locking always lock in this order!
|
||||||
transaction_queue: Arc<Mutex<TransactionQueue>>,
|
transaction_queue: Arc<Mutex<TransactionQueue>>,
|
||||||
sealing_work: Mutex<SealingWork>,
|
sealing_work: Mutex<SealingWork>,
|
||||||
|
next_allowed_reseal: Mutex<Instant>,
|
||||||
|
sealing_block_last_request: Mutex<u64>,
|
||||||
// for sealing...
|
// for sealing...
|
||||||
options: MinerOptions,
|
options: MinerOptions,
|
||||||
|
|
||||||
next_allowed_reseal: Mutex<Instant>,
|
|
||||||
sealing_block_last_request: Mutex<u64>,
|
|
||||||
gas_range_target: RwLock<(U256, U256)>,
|
gas_range_target: RwLock<(U256, U256)>,
|
||||||
author: RwLock<Address>,
|
author: RwLock<Address>,
|
||||||
extra_data: RwLock<Bytes>,
|
extra_data: RwLock<Bytes>,
|
||||||
@@ -286,13 +286,20 @@ impl Miner {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut invalid_transactions = HashSet::new();
|
let mut invalid_transactions = HashSet::new();
|
||||||
|
let mut transactions_to_penalize = HashSet::new();
|
||||||
let block_number = open_block.block().fields().header.number();
|
let block_number = open_block.block().fields().header.number();
|
||||||
// TODO: push new uncles, too.
|
// TODO: push new uncles, too.
|
||||||
for tx in transactions {
|
for tx in transactions {
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
match open_block.push_transaction(tx, None) {
|
match open_block.push_transaction(tx, None) {
|
||||||
Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => {
|
Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) => {
|
||||||
debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash);
|
debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas);
|
||||||
|
|
||||||
|
// Penalize transaction if it's above current gas limit
|
||||||
|
if gas > gas_limit {
|
||||||
|
transactions_to_penalize.insert(hash);
|
||||||
|
}
|
||||||
|
|
||||||
// Exit early if gas left is smaller then min_tx_gas
|
// Exit early if gas left is smaller then min_tx_gas
|
||||||
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
|
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
|
||||||
if gas_limit - gas_used < min_tx_gas {
|
if gas_limit - gas_used < min_tx_gas {
|
||||||
@@ -301,8 +308,8 @@ impl Miner {
|
|||||||
},
|
},
|
||||||
// Invalid nonce error can happen only if previous transaction is skipped because of gas limit.
|
// Invalid nonce error can happen only if previous transaction is skipped because of gas limit.
|
||||||
// If there is errornous state of transaction queue it will be fixed when next block is imported.
|
// If there is errornous state of transaction queue it will be fixed when next block is imported.
|
||||||
Err(Error::Execution(ExecutionError::InvalidNonce { .. })) => {
|
Err(Error::Execution(ExecutionError::InvalidNonce { expected, got })) => {
|
||||||
debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?}", hash);
|
debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?} (expected: {:?}, got: {:?})", hash, expected, got);
|
||||||
},
|
},
|
||||||
// already have transaction - ignore
|
// already have transaction - ignore
|
||||||
Err(Error::Transaction(TransactionError::AlreadyImported)) => {},
|
Err(Error::Transaction(TransactionError::AlreadyImported)) => {},
|
||||||
@@ -328,6 +335,9 @@ impl Miner {
|
|||||||
for hash in invalid_transactions.into_iter() {
|
for hash in invalid_transactions.into_iter() {
|
||||||
queue.remove_invalid(&hash, &fetch_account);
|
queue.remove_invalid(&hash, &fetch_account);
|
||||||
}
|
}
|
||||||
|
for hash in transactions_to_penalize {
|
||||||
|
queue.penalize(&hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !block.transactions().is_empty() {
|
if !block.transactions().is_empty() {
|
||||||
@@ -528,7 +538,7 @@ impl MinerService for Miner {
|
|||||||
|
|
||||||
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
||||||
let sealing_work = self.sealing_work.lock();
|
let sealing_work = self.sealing_work.lock();
|
||||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
|
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_author(&self, author: Address) {
|
fn set_author(&self, author: Address) {
|
||||||
@@ -713,6 +723,35 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)) => {
|
||||||
|
let txs = pending.transactions();
|
||||||
|
txs.iter()
|
||||||
|
.map(|t| t.hash())
|
||||||
|
.position(|t| t == *hash)
|
||||||
|
.map(|index| {
|
||||||
|
let prev_gas = if index == 0 { Default::default() } else { pending.receipts()[index - 1].gas_used };
|
||||||
|
let ref tx = txs[index];
|
||||||
|
let ref receipt = pending.receipts()[index];
|
||||||
|
RichReceipt {
|
||||||
|
transaction_hash: hash.clone(),
|
||||||
|
transaction_index: index,
|
||||||
|
cumulative_gas_used: receipt.gas_used,
|
||||||
|
gas_used: receipt.gas_used - prev_gas,
|
||||||
|
contract_address: match tx.action {
|
||||||
|
Action::Call(_) => None,
|
||||||
|
Action::Create => Some(contract_address(&tx.sender().unwrap(), &tx.nonce)),
|
||||||
|
},
|
||||||
|
logs: receipt.logs.clone(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
||||||
let sealing_work = self.sealing_work.lock();
|
let sealing_work = self.sealing_work.lock();
|
||||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||||
@@ -736,11 +775,11 @@ impl MinerService for Miner {
|
|||||||
fn update_sealing(&self, chain: &MiningBlockChainClient) {
|
fn update_sealing(&self, chain: &MiningBlockChainClient) {
|
||||||
trace!(target: "miner", "update_sealing");
|
trace!(target: "miner", "update_sealing");
|
||||||
let requires_reseal = {
|
let requires_reseal = {
|
||||||
|
let has_local_transactions = self.transaction_queue.lock().has_local_pending_transactions();
|
||||||
let mut sealing_work = self.sealing_work.lock();
|
let mut sealing_work = self.sealing_work.lock();
|
||||||
if sealing_work.enabled {
|
if sealing_work.enabled {
|
||||||
trace!(target: "miner", "update_sealing: sealing enabled");
|
trace!(target: "miner", "update_sealing: sealing enabled");
|
||||||
let current_no = chain.chain_info().best_block_number;
|
let current_no = chain.chain_info().best_block_number;
|
||||||
let has_local_transactions = self.transaction_queue.lock().has_local_pending_transactions();
|
|
||||||
let last_request = *self.sealing_block_last_request.lock();
|
let last_request = *self.sealing_block_last_request.lock();
|
||||||
let should_disable_sealing = !self.forced_sealing()
|
let should_disable_sealing = !self.forced_sealing()
|
||||||
&& !has_local_transactions
|
&& !has_local_transactions
|
||||||
@@ -840,7 +879,7 @@ impl MinerService for Miner {
|
|||||||
out_of_chain.for_each(|txs| {
|
out_of_chain.for_each(|txs| {
|
||||||
let mut transaction_queue = self.transaction_queue.lock();
|
let mut transaction_queue = self.transaction_queue.lock();
|
||||||
let _ = self.add_transactions_to_queue(
|
let _ = self.add_transactions_to_queue(
|
||||||
chain, txs, TransactionOrigin::External, &mut transaction_queue
|
chain, txs, TransactionOrigin::RetractedBlock, &mut transaction_queue
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ use std::collections::BTreeMap;
|
|||||||
use util::{H256, U256, Address, Bytes};
|
use util::{H256, U256, Address, Bytes};
|
||||||
use client::{MiningBlockChainClient, Executed, CallAnalytics};
|
use client::{MiningBlockChainClient, Executed, CallAnalytics};
|
||||||
use block::ClosedBlock;
|
use block::ClosedBlock;
|
||||||
use receipt::Receipt;
|
use receipt::{RichReceipt, Receipt};
|
||||||
use error::{Error, CallError};
|
use error::{Error, CallError};
|
||||||
use transaction::SignedTransaction;
|
use transaction::SignedTransaction;
|
||||||
|
|
||||||
@@ -146,6 +146,9 @@ pub trait MinerService : Send + Sync {
|
|||||||
/// Get a list of all pending receipts.
|
/// Get a list of all pending receipts.
|
||||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt>;
|
fn pending_receipts(&self) -> BTreeMap<H256, Receipt>;
|
||||||
|
|
||||||
|
/// Get a particular reciept.
|
||||||
|
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt>;
|
||||||
|
|
||||||
/// Returns highest transaction nonce for given address.
|
/// Returns highest transaction nonce for given address.
|
||||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ pub enum TransactionOrigin {
|
|||||||
Local,
|
Local,
|
||||||
/// External transaction received from network
|
/// External transaction received from network
|
||||||
External,
|
External,
|
||||||
|
/// Transactions from retracted blocks
|
||||||
|
RetractedBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for TransactionOrigin {
|
impl PartialOrd for TransactionOrigin {
|
||||||
@@ -113,10 +115,11 @@ impl Ord for TransactionOrigin {
|
|||||||
return Ordering::Equal;
|
return Ordering::Equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if *self == TransactionOrigin::Local {
|
match (*self, *other) {
|
||||||
Ordering::Less
|
(TransactionOrigin::RetractedBlock, _) => Ordering::Less,
|
||||||
} else {
|
(_, TransactionOrigin::RetractedBlock) => Ordering::Greater,
|
||||||
Ordering::Greater
|
(TransactionOrigin::Local, _) => Ordering::Less,
|
||||||
|
_ => Ordering::Greater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,10 +134,15 @@ struct TransactionOrder {
|
|||||||
/// Gas Price of the transaction.
|
/// Gas Price of the transaction.
|
||||||
/// Low gas price = Low priority (processed later)
|
/// Low gas price = Low priority (processed later)
|
||||||
gas_price: U256,
|
gas_price: U256,
|
||||||
|
/// Gas (limit) of the transaction.
|
||||||
|
/// Low gas limit = High priority (processed earlier)
|
||||||
|
gas: U256,
|
||||||
/// Hash to identify associated transaction
|
/// Hash to identify associated transaction
|
||||||
hash: H256,
|
hash: H256,
|
||||||
/// Origin of the transaction
|
/// Origin of the transaction
|
||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
|
/// Penalties
|
||||||
|
penalties: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -143,8 +151,10 @@ impl TransactionOrder {
|
|||||||
TransactionOrder {
|
TransactionOrder {
|
||||||
nonce_height: tx.nonce() - base_nonce,
|
nonce_height: tx.nonce() - base_nonce,
|
||||||
gas_price: tx.transaction.gas_price,
|
gas_price: tx.transaction.gas_price,
|
||||||
|
gas: tx.transaction.gas,
|
||||||
hash: tx.hash(),
|
hash: tx.hash(),
|
||||||
origin: tx.origin,
|
origin: tx.origin,
|
||||||
|
penalties: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +162,11 @@ impl TransactionOrder {
|
|||||||
self.nonce_height = nonce - base_nonce;
|
self.nonce_height = nonce - base_nonce;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn penalize(mut self) -> Self {
|
||||||
|
self.penalties = self.penalties.saturating_add(1);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for TransactionOrder {}
|
impl Eq for TransactionOrder {}
|
||||||
@@ -168,6 +183,11 @@ impl PartialOrd for TransactionOrder {
|
|||||||
|
|
||||||
impl Ord for TransactionOrder {
|
impl Ord for TransactionOrder {
|
||||||
fn cmp(&self, b: &TransactionOrder) -> Ordering {
|
fn cmp(&self, b: &TransactionOrder) -> Ordering {
|
||||||
|
// First check number of penalties
|
||||||
|
if self.penalties != b.penalties {
|
||||||
|
return self.penalties.cmp(&b.penalties);
|
||||||
|
}
|
||||||
|
|
||||||
// First check nonce_height
|
// First check nonce_height
|
||||||
if self.nonce_height != b.nonce_height {
|
if self.nonce_height != b.nonce_height {
|
||||||
return self.nonce_height.cmp(&b.nonce_height);
|
return self.nonce_height.cmp(&b.nonce_height);
|
||||||
@@ -179,11 +199,18 @@ impl Ord for TransactionOrder {
|
|||||||
return self.origin.cmp(&b.origin);
|
return self.origin.cmp(&b.origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then compare gas_prices
|
// Then compare gas usage
|
||||||
let a_gas = self.gas_price;
|
let a_gas = self.gas;
|
||||||
let b_gas = b.gas_price;
|
let b_gas = b.gas;
|
||||||
if a_gas != b_gas {
|
if a_gas != b_gas {
|
||||||
return b_gas.cmp(&a_gas);
|
return a_gas.cmp(&b_gas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare hashes
|
// Compare hashes
|
||||||
@@ -321,7 +348,7 @@ pub struct AccountDetails {
|
|||||||
|
|
||||||
|
|
||||||
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
||||||
const GAS_LIMIT_HYSTERESIS: usize = 10; // %
|
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
|
||||||
|
|
||||||
/// `TransactionQueue` implementation
|
/// `TransactionQueue` implementation
|
||||||
pub struct TransactionQueue {
|
pub struct TransactionQueue {
|
||||||
@@ -504,6 +531,39 @@ impl TransactionQueue {
|
|||||||
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
|
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Penalize transactions from sender of transaction with given hash.
|
||||||
|
/// I.e. it should change the priority of the transaction in the queue.
|
||||||
|
///
|
||||||
|
/// NOTE: We need to penalize all transactions from particular sender
|
||||||
|
/// to avoid breaking invariants in queue (ordered by nonces).
|
||||||
|
/// Consecutive transactions from this sender would fail otherwise (because of invalid nonce).
|
||||||
|
pub fn penalize(&mut self, transaction_hash: &H256) {
|
||||||
|
let transaction = match self.by_hash.get(transaction_hash) {
|
||||||
|
None => return,
|
||||||
|
Some(t) => t,
|
||||||
|
};
|
||||||
|
let sender = transaction.sender();
|
||||||
|
|
||||||
|
// Penalize all transactions from this sender
|
||||||
|
let nonces_from_sender = match self.current.by_address.row(&sender) {
|
||||||
|
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
for k in nonces_from_sender {
|
||||||
|
let order = self.current.drop(&sender, &k).unwrap();
|
||||||
|
self.current.insert(sender, k, order.penalize());
|
||||||
|
}
|
||||||
|
// Same thing for future
|
||||||
|
let nonces_from_sender = match self.future.by_address.row(&sender) {
|
||||||
|
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
for k in nonces_from_sender {
|
||||||
|
let order = self.future.drop(&sender, &k).unwrap();
|
||||||
|
self.current.insert(sender, k, order.penalize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes invalid transaction identified by hash from queue.
|
/// Removes invalid transaction identified by hash from queue.
|
||||||
/// Assumption is that this transaction nonce is not related to client nonce,
|
/// Assumption is that this transaction nonce is not related to client nonce,
|
||||||
/// so transactions left in queue are processed according to client nonce.
|
/// so transactions left in queue are processed according to client nonce.
|
||||||
@@ -577,7 +637,10 @@ impl TransactionQueue {
|
|||||||
// Goes to future or is removed
|
// Goes to future or is removed
|
||||||
let order = self.current.drop(sender, &k).unwrap();
|
let order = self.current.drop(sender, &k).unwrap();
|
||||||
if k >= current_nonce {
|
if k >= current_nonce {
|
||||||
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
let order = order.update_height(k, current_nonce);
|
||||||
|
if let Some(old) = self.future.insert(*sender, k, order.clone()) {
|
||||||
|
Self::replace_orders(*sender, k, old, order, &mut self.future, &mut self.by_hash);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
|
trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
|
||||||
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
|
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
|
||||||
@@ -641,7 +704,9 @@ impl TransactionQueue {
|
|||||||
self.future.by_priority.remove(&order);
|
self.future.by_priority.remove(&order);
|
||||||
// Put to current
|
// Put to current
|
||||||
let order = order.update_height(current_nonce, first_nonce);
|
let order = order.update_height(current_nonce, first_nonce);
|
||||||
self.current.insert(address, current_nonce, order);
|
if let Some(old) = self.current.insert(address, current_nonce, order.clone()) {
|
||||||
|
Self::replace_orders(address, current_nonce, old, order, &mut self.current, &mut self.by_hash);
|
||||||
|
}
|
||||||
update_last_nonce_to = Some(current_nonce);
|
update_last_nonce_to = Some(current_nonce);
|
||||||
current_nonce = current_nonce + U256::one();
|
current_nonce = current_nonce + U256::one();
|
||||||
}
|
}
|
||||||
@@ -673,45 +738,51 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
let address = tx.sender();
|
let address = tx.sender();
|
||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
|
let hash = tx.hash();
|
||||||
let next_nonce = self.last_nonces
|
|
||||||
.get(&address)
|
|
||||||
.cloned()
|
|
||||||
.map_or(state_nonce, |n| n + U256::one());
|
|
||||||
|
|
||||||
// The transaction might be old, let's check that.
|
// The transaction might be old, let's check that.
|
||||||
// This has to be the first test, otherwise calculating
|
// This has to be the first test, otherwise calculating
|
||||||
// nonce height would result in overflow.
|
// nonce height would result in overflow.
|
||||||
if nonce < state_nonce {
|
if nonce < state_nonce {
|
||||||
// Droping transaction
|
// Droping transaction
|
||||||
trace!(target: "txqueue", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce);
|
trace!(target: "txqueue", "Dropping old transaction: {:?} (nonce: {} < {})", hash, nonce, state_nonce);
|
||||||
return Err(TransactionError::Old);
|
return Err(TransactionError::Old);
|
||||||
} else if nonce > next_nonce {
|
}
|
||||||
|
|
||||||
|
// Update nonces of transactions in future (remove old transactions)
|
||||||
|
self.update_future(&address, state_nonce);
|
||||||
|
// State nonce could be updated. Maybe there are some more items waiting in future?
|
||||||
|
self.move_matching_future_to_current(address, state_nonce, state_nonce);
|
||||||
|
// Check the next expected nonce (might be updated by move above)
|
||||||
|
let next_nonce = self.last_nonces
|
||||||
|
.get(&address)
|
||||||
|
.cloned()
|
||||||
|
.map_or(state_nonce, |n| n + U256::one());
|
||||||
|
|
||||||
|
// Future transaction
|
||||||
|
if nonce > next_nonce {
|
||||||
// We have a gap - put to future.
|
// We have a gap - put to future.
|
||||||
// Update nonces of transactions in future (remove old transactions)
|
|
||||||
self.update_future(&address, state_nonce);
|
|
||||||
// Insert transaction (or replace old one with lower gas price)
|
// 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, &mut self.future, &mut self.by_hash)));
|
||||||
// Return an error if this transaction is not imported because of limit.
|
// Enforce limit in Future
|
||||||
try!(check_if_removed(&address, &nonce, self.future.enforce_limit(&mut self.by_hash)));
|
let removed = self.future.enforce_limit(&mut self.by_hash);
|
||||||
|
// Return an error if this transaction was not imported because of limit.
|
||||||
|
try!(check_if_removed(&address, &nonce, removed));
|
||||||
|
|
||||||
|
debug!(target: "txqueue", "Importing transaction to future: {:?}", hash);
|
||||||
|
debug!(target: "txqueue", "status: {:?}", self.status());
|
||||||
return Ok(TransactionImportResult::Future);
|
return Ok(TransactionImportResult::Future);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We might have filled a gap - move some more transactions from future
|
||||||
|
self.move_matching_future_to_current(address, nonce, state_nonce);
|
||||||
|
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, &mut self.current, &mut self.by_hash)));
|
||||||
// Keep track of highest nonce stored in current
|
// Keep track of highest nonce stored in current
|
||||||
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
||||||
self.last_nonces.insert(address, new_max);
|
self.last_nonces.insert(address, new_max);
|
||||||
// Update nonces of transactions in future
|
|
||||||
self.update_future(&address, state_nonce);
|
|
||||||
// Maybe there are some more items waiting in future?
|
|
||||||
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
|
|
||||||
// There might be exactly the same transaction waiting in future
|
|
||||||
// same (sender, nonce), but above function would not move it.
|
|
||||||
if let Some(order) = self.future.drop(&address, &nonce) {
|
|
||||||
// Let's insert that transaction to current (if it has higher gas_price)
|
|
||||||
let future_tx = self.by_hash.remove(&order.hash).expect("All transactions in `future` are always in `by_hash`.");
|
|
||||||
// if transaction in `current` (then one we are importing) is replaced it means that it has to low gas_price
|
|
||||||
try!(check_too_cheap(!Self::replace_transaction(future_tx, state_nonce, &mut self.current, &mut self.by_hash)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also enforce the limit
|
// Also enforce the limit
|
||||||
let removed = self.current.enforce_limit(&mut self.by_hash);
|
let removed = self.current.enforce_limit(&mut self.by_hash);
|
||||||
@@ -755,24 +826,28 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
|
|
||||||
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
||||||
// There was already transaction in queue. Let's check which one should stay
|
Self::replace_orders(address, nonce, old, order, set, by_hash)
|
||||||
let old_fee = old.gas_price;
|
|
||||||
let new_fee = order.gas_price;
|
|
||||||
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
|
||||||
// Put back old transaction since it has greater priority (higher gas_price)
|
|
||||||
set.insert(address, nonce, old);
|
|
||||||
// and remove new one
|
|
||||||
by_hash.remove(&hash).expect("The hash has been just inserted and no other line is altering `by_hash`.");
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn replace_orders(address: Address, nonce: U256, old: TransactionOrder, order: TransactionOrder, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||||
|
// There was already transaction in queue. Let's check which one should stay
|
||||||
|
let old_fee = old.gas_price;
|
||||||
|
let new_fee = order.gas_price;
|
||||||
|
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
||||||
|
// 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 {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_too_cheap(is_in: bool) -> Result<(), TransactionError> {
|
fn check_too_cheap(is_in: bool) -> Result<(), TransactionError> {
|
||||||
@@ -814,25 +889,40 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_unsigned_tx(nonce: U256) -> Transaction {
|
fn default_nonce_val() -> U256 {
|
||||||
|
123.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_gas_val() -> U256 {
|
||||||
|
100_000.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_gas_price_val() -> U256 {
|
||||||
|
1.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_unsigned_tx_with_gas(nonce: U256, gas: U256, gas_price: U256) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
action: Action::Create,
|
action: Action::Create,
|
||||||
value: U256::from(100),
|
value: U256::from(100),
|
||||||
data: "3331600055".from_hex().unwrap(),
|
data: "3331600055".from_hex().unwrap(),
|
||||||
gas: U256::from(100_000),
|
gas: gas,
|
||||||
gas_price: U256::one(),
|
gas_price: gas_price,
|
||||||
nonce: nonce
|
nonce: nonce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_tx() -> SignedTransaction {
|
fn new_unsigned_tx(nonce: U256) -> Transaction {
|
||||||
let keypair = KeyPair::create().unwrap();
|
new_unsigned_tx_with_gas(nonce, default_gas_val(), default_gas_price_val())
|
||||||
new_unsigned_tx(U256::from(123)).sign(keypair.secret())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
fn default_nonce_val() -> U256 {
|
fn new_tx() -> SignedTransaction {
|
||||||
U256::from(123)
|
new_tx_with_gas(default_gas_val(), default_gas_price_val())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_nonce(_address: &Address) -> AccountDetails {
|
fn default_nonce(_address: &Address) -> AccountDetails {
|
||||||
@@ -846,10 +936,10 @@ mod test {
|
|||||||
fn new_similar_txs() -> (SignedTransaction, SignedTransaction) {
|
fn new_similar_txs() -> (SignedTransaction, SignedTransaction) {
|
||||||
let keypair = KeyPair::create().unwrap();
|
let keypair = KeyPair::create().unwrap();
|
||||||
let secret = &keypair.secret();
|
let secret = &keypair.secret();
|
||||||
let nonce = U256::from(123);
|
let nonce = default_nonce_val();
|
||||||
let tx = new_unsigned_tx(nonce);
|
let tx = new_unsigned_tx(nonce);
|
||||||
let mut tx2 = new_unsigned_tx(nonce);
|
let mut tx2 = new_unsigned_tx(nonce);
|
||||||
tx2.gas_price = U256::from(2);
|
tx2.gas_price = 2.into();
|
||||||
|
|
||||||
(tx.sign(secret), tx2.sign(secret))
|
(tx.sign(secret), tx2.sign(secret))
|
||||||
}
|
}
|
||||||
@@ -858,6 +948,18 @@ mod test {
|
|||||||
new_txs_with_gas_price_diff(second_nonce, U256::zero())
|
new_txs_with_gas_price_diff(second_nonce, U256::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_txs_with_higher_gas_price(gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
let secret = &keypair.secret();
|
||||||
|
let nonce = U256::from(123);
|
||||||
|
let mut tx = new_unsigned_tx(nonce);
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
fn new_txs_with_gas_price_diff(second_nonce: U256, gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
fn new_txs_with_gas_price_diff(second_nonce: U256, gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
let keypair = KeyPair::create().unwrap();
|
let keypair = KeyPair::create().unwrap();
|
||||||
let secret = &keypair.secret();
|
let secret = &keypair.secret();
|
||||||
@@ -869,6 +971,17 @@ mod test {
|
|||||||
(tx.sign(secret), tx2.sign(secret))
|
(tx.sign(secret), tx2.sign(secret))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ordering() {
|
||||||
|
assert_eq!(TransactionOrigin::Local.cmp(&TransactionOrigin::External), Ordering::Less);
|
||||||
|
assert_eq!(TransactionOrigin::RetractedBlock.cmp(&TransactionOrigin::Local), Ordering::Less);
|
||||||
|
assert_eq!(TransactionOrigin::RetractedBlock.cmp(&TransactionOrigin::External), Ordering::Less);
|
||||||
|
|
||||||
|
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::Local), Ordering::Greater);
|
||||||
|
assert_eq!(TransactionOrigin::Local.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
|
||||||
|
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_create_transaction_set() {
|
fn should_create_transaction_set() {
|
||||||
// given
|
// given
|
||||||
@@ -972,6 +1085,31 @@ mod test {
|
|||||||
assert_eq!(txq.top_transactions()[0], tx2);
|
assert_eq!(txq.top_transactions()[0], tx2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_move_all_transactions_from_future() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
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() };
|
||||||
|
|
||||||
|
// First insert one transaction to future
|
||||||
|
let res = txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External);
|
||||||
|
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
|
// now import second transaction to current
|
||||||
|
let res = txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
assert_eq!(txq.status().future, 0);
|
||||||
|
assert_eq!(txq.current.by_priority.len(), 2);
|
||||||
|
assert_eq!(txq.current.by_address.len(), 2);
|
||||||
|
assert_eq!(txq.top_transactions()[0], tx);
|
||||||
|
assert_eq!(txq.top_transactions()[1], tx2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_import_tx() {
|
fn should_import_tx() {
|
||||||
@@ -988,6 +1126,39 @@ mod test {
|
|||||||
assert_eq!(stats.pending, 1);
|
assert_eq!(stats.pending, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_order_by_gas() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
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());
|
||||||
|
let tx4 = new_tx_with_gas(50000.into(), 20.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!(unwrap_tx_err(res3), TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: U256::from(15),
|
||||||
|
got: U256::from(10),
|
||||||
|
});
|
||||||
|
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 3);
|
||||||
|
assert_eq!(txq.top_transactions()[0].gas, 40000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[1].gas, 50000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[2].gas, 50000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[1].gas_price, 40.into());
|
||||||
|
assert_eq!(txq.top_transactions()[2].gas_price, 20.into());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gas_limit_should_never_overflow() {
|
fn gas_limit_should_never_overflow() {
|
||||||
// given
|
// given
|
||||||
@@ -1024,7 +1195,6 @@ mod test {
|
|||||||
assert_eq!(stats.future, 0);
|
assert_eq!(stats.future, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_drop_transactions_from_senders_without_balance() {
|
fn should_drop_transactions_from_senders_without_balance() {
|
||||||
// given
|
// given
|
||||||
@@ -1086,7 +1256,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_reject_incorectly_signed_transaction() {
|
fn should_reject_incorrectly_signed_transaction() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
let tx = new_unsigned_tx(U256::from(123));
|
let tx = new_unsigned_tx(U256::from(123));
|
||||||
@@ -1149,6 +1319,27 @@ mod test {
|
|||||||
assert_eq!(top.len(), 2);
|
assert_eq!(top.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_prioritize_reimported_transactions_within_same_nonce_height() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let tx = new_tx();
|
||||||
|
// the second one has same nonce but higher `gas_price`
|
||||||
|
let (_, tx2) = new_similar_txs();
|
||||||
|
|
||||||
|
// when
|
||||||
|
// first insert local one with higher gas price
|
||||||
|
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||||
|
// then the one with lower gas price, but from retracted block
|
||||||
|
txq.add(tx.clone(), &default_nonce, TransactionOrigin::RetractedBlock).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let top = txq.top_transactions();
|
||||||
|
assert_eq!(top[0], tx); // retracted should be first
|
||||||
|
assert_eq!(top[1], tx2);
|
||||||
|
assert_eq!(top.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
||||||
// given
|
// given
|
||||||
@@ -1166,6 +1357,39 @@ mod test {
|
|||||||
assert_eq!(top.len(), 2);
|
assert_eq!(top.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_penalize_transactions_from_sender() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
// 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(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||||
|
txq.add(txb.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||||
|
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||||
|
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||||
|
|
||||||
|
let top = txq.top_transactions();
|
||||||
|
assert_eq!(top[0], tx1);
|
||||||
|
assert_eq!(top[1], txa);
|
||||||
|
assert_eq!(top[2], tx2);
|
||||||
|
assert_eq!(top[3], txb);
|
||||||
|
assert_eq!(top.len(), 4);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.penalize(&tx1.hash());
|
||||||
|
|
||||||
|
// then
|
||||||
|
let top = txq.top_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]
|
#[test]
|
||||||
fn should_return_pending_hashes() {
|
fn should_return_pending_hashes() {
|
||||||
// given
|
// given
|
||||||
@@ -1467,7 +1691,7 @@ mod test {
|
|||||||
let keypair = KeyPair::create().unwrap();
|
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());
|
||||||
let tx2 = {
|
let tx2 = {
|
||||||
let mut tx2 = tx.deref().clone();
|
let mut tx2 = (*tx).clone();
|
||||||
tx2.gas_price = U256::from(200);
|
tx2.gas_price = U256::from(200);
|
||||||
tx2.sign(keypair.secret())
|
tx2.sign(keypair.secret())
|
||||||
};
|
};
|
||||||
@@ -1490,12 +1714,12 @@ mod test {
|
|||||||
let keypair = KeyPair::create().unwrap();
|
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());
|
||||||
let tx1 = {
|
let tx1 = {
|
||||||
let mut tx1 = tx0.deref().clone();
|
let mut tx1 = (*tx0).clone();
|
||||||
tx1.nonce = U256::from(124);
|
tx1.nonce = U256::from(124);
|
||||||
tx1.sign(keypair.secret())
|
tx1.sign(keypair.secret())
|
||||||
};
|
};
|
||||||
let tx2 = {
|
let tx2 = {
|
||||||
let mut tx2 = tx1.deref().clone();
|
let mut tx2 = (*tx1).clone();
|
||||||
tx2.gas_price = U256::from(200);
|
tx2.gas_price = U256::from(200);
|
||||||
tx2.sign(keypair.secret())
|
tx2.sign(keypair.secret())
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ impl PodAccount {
|
|||||||
PodAccount {
|
PodAccount {
|
||||||
balance: *acc.balance(),
|
balance: *acc.balance(),
|
||||||
nonce: *acc.nonce(),
|
nonce: *acc.nonce(),
|
||||||
storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
|
storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}),
|
||||||
code: acc.code().map(|x| x.to_vec()),
|
code: acc.code().map(|x| x.to_vec()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ impl Account {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use account_db::{AccountDB, AccountDBMut};
|
use account_db::{AccountDB, AccountDBMut};
|
||||||
use tests::helpers::get_temp_journal_db;
|
use tests::helpers::get_temp_state_db;
|
||||||
use snapshot::tests::helpers::fill_storage;
|
use snapshot::tests::helpers::fill_storage;
|
||||||
|
|
||||||
use util::{SHA3_NULL_RLP, SHA3_EMPTY};
|
use util::{SHA3_NULL_RLP, SHA3_EMPTY};
|
||||||
@@ -154,8 +154,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoding_basic() {
|
fn encoding_basic() {
|
||||||
let mut db = get_temp_journal_db();
|
let mut db = get_temp_state_db();
|
||||||
let mut db = &mut **db;
|
|
||||||
let addr = Address::random();
|
let addr = Address::random();
|
||||||
|
|
||||||
let account = Account {
|
let account = Account {
|
||||||
@@ -175,8 +174,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoding_storage() {
|
fn encoding_storage() {
|
||||||
let mut db = get_temp_journal_db();
|
let mut db = get_temp_state_db();
|
||||||
let mut db = &mut **db;
|
|
||||||
let addr = Address::random();
|
let addr = Address::random();
|
||||||
|
|
||||||
let account = {
|
let account = {
|
||||||
@@ -198,4 +196,4 @@ mod tests {
|
|||||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap(), account);
|
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap(), account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use ids::BlockID;
|
||||||
|
|
||||||
use util::H256;
|
use util::H256;
|
||||||
use util::trie::TrieError;
|
use util::trie::TrieError;
|
||||||
use util::rlp::DecoderError;
|
use util::rlp::DecoderError;
|
||||||
@@ -26,9 +28,13 @@ use util::rlp::DecoderError;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Invalid starting block for snapshot.
|
/// Invalid starting block for snapshot.
|
||||||
InvalidStartingBlock(H256),
|
InvalidStartingBlock(BlockID),
|
||||||
/// Block not found.
|
/// Block not found.
|
||||||
BlockNotFound(H256),
|
BlockNotFound(H256),
|
||||||
|
/// Incomplete chain.
|
||||||
|
IncompleteChain,
|
||||||
|
/// Old starting block in a pruned database.
|
||||||
|
OldBlockPrunedDB,
|
||||||
/// Trie error.
|
/// Trie error.
|
||||||
Trie(TrieError),
|
Trie(TrieError),
|
||||||
/// Decoder error.
|
/// Decoder error.
|
||||||
@@ -40,8 +46,11 @@ pub enum Error {
|
|||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::InvalidStartingBlock(ref hash) => write!(f, "Invalid starting block hash: {}", hash),
|
Error::InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id),
|
||||||
Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash),
|
Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash),
|
||||||
|
Error::IncompleteChain => write!(f, "Cannot create snapshot due to incomplete chain."),
|
||||||
|
Error::OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \
|
||||||
|
a pruned database. Please re-run with the --pruning archive flag."),
|
||||||
Error::Io(ref err) => err.fmt(f),
|
Error::Io(ref err) => err.fmt(f),
|
||||||
Error::Decoder(ref err) => err.fmt(f),
|
Error::Decoder(ref err) => err.fmt(f),
|
||||||
Error::Trie(ref err) => err.fmt(f),
|
Error::Trie(ref err) => err.fmt(f),
|
||||||
@@ -55,9 +64,9 @@ impl From<::std::io::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<TrieError>> for Error {
|
impl From<TrieError> for Error {
|
||||||
fn from(err: Box<TrieError>) -> Self {
|
fn from(err: TrieError) -> Self {
|
||||||
Error::Trie(*err)
|
Error::Trie(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,4 +74,10 @@ impl From<DecoderError> for Error {
|
|||||||
fn from(err: DecoderError) -> Self {
|
fn from(err: DecoderError) -> Self {
|
||||||
Error::Decoder(err)
|
Error::Decoder(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E> From<Box<E>> for Error where Error: From<E> {
|
||||||
|
fn from(err: Box<E>) -> Self {
|
||||||
|
Error::from(*err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,13 +18,16 @@
|
|||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
|
|
||||||
use account_db::{AccountDB, AccountDBMut};
|
use account_db::{AccountDB, AccountDBMut};
|
||||||
use blockchain::{BlockChain, BlockProvider};
|
use blockchain::{BlockChain, BlockProvider};
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
|
use ids::BlockID;
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
|
use super::state_db::StateDB;
|
||||||
|
|
||||||
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut};
|
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut, BytesConvertable};
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
use util::hash::{FixedHash, H256};
|
use util::hash::{FixedHash, H256};
|
||||||
use util::journaldb::{self, Algorithm, JournalDB};
|
use util::journaldb::{self, Algorithm, JournalDB};
|
||||||
@@ -58,9 +61,39 @@ const PREFERRED_CHUNK_SIZE: usize = 4 * 1024 * 1024;
|
|||||||
// How many blocks to include in a snapshot, starting from the head of the chain.
|
// How many blocks to include in a snapshot, starting from the head of the chain.
|
||||||
const SNAPSHOT_BLOCKS: u64 = 30000;
|
const SNAPSHOT_BLOCKS: u64 = 30000;
|
||||||
|
|
||||||
|
/// A progress indicator for snapshots.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Progress {
|
||||||
|
accounts: AtomicUsize,
|
||||||
|
blocks: AtomicUsize,
|
||||||
|
size: AtomicUsize, // Todo [rob] use Atomicu64 when it stabilizes.
|
||||||
|
done: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Progress {
|
||||||
|
/// Get the number of accounts snapshotted thus far.
|
||||||
|
pub fn accounts(&self) -> usize { self.accounts.load(Ordering::Relaxed) }
|
||||||
|
|
||||||
|
/// Get the number of blocks snapshotted thus far.
|
||||||
|
pub fn blocks(&self) -> usize { self.blocks.load(Ordering::Relaxed) }
|
||||||
|
|
||||||
|
/// Get the written size of the snapshot in bytes.
|
||||||
|
pub fn size(&self) -> usize { self.size.load(Ordering::Relaxed) }
|
||||||
|
|
||||||
|
/// Whether the snapshot is complete.
|
||||||
|
pub fn done(&self) -> bool { self.done.load(Ordering::SeqCst) }
|
||||||
|
|
||||||
|
}
|
||||||
/// Take a snapshot using the given blockchain, starting block hash, and database, writing into the given writer.
|
/// Take a snapshot using the given blockchain, starting block hash, and database, writing into the given writer.
|
||||||
pub fn take_snapshot<W: SnapshotWriter + Send>(chain: &BlockChain, start_block_hash: H256, state_db: &HashDB, writer: W) -> Result<(), Error> {
|
pub fn take_snapshot<W: SnapshotWriter + Send>(
|
||||||
let start_header = try!(chain.block_header(&start_block_hash).ok_or(Error::InvalidStartingBlock(start_block_hash)));
|
chain: &BlockChain,
|
||||||
|
block_at: H256,
|
||||||
|
state_db: &HashDB,
|
||||||
|
writer: W,
|
||||||
|
p: &Progress
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let start_header = try!(chain.block_header(&block_at)
|
||||||
|
.ok_or(Error::InvalidStartingBlock(BlockID::Hash(block_at))));
|
||||||
let state_root = start_header.state_root();
|
let state_root = start_header.state_root();
|
||||||
let number = start_header.number();
|
let number = start_header.number();
|
||||||
|
|
||||||
@@ -68,8 +101,8 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(chain: &BlockChain, start_block_h
|
|||||||
|
|
||||||
let writer = Mutex::new(writer);
|
let writer = Mutex::new(writer);
|
||||||
let (state_hashes, block_hashes) = try!(scope(|scope| {
|
let (state_hashes, block_hashes) = try!(scope(|scope| {
|
||||||
let block_guard = scope.spawn(|| chunk_blocks(chain, (number, start_block_hash), &writer));
|
let block_guard = scope.spawn(|| chunk_blocks(chain, (number, block_at), &writer, p));
|
||||||
let state_res = chunk_state(state_db, state_root, &writer);
|
let state_res = chunk_state(state_db, state_root, &writer, p);
|
||||||
|
|
||||||
state_res.and_then(|state_hashes| {
|
state_res.and_then(|state_hashes| {
|
||||||
block_guard.join().map(|block_hashes| (state_hashes, block_hashes))
|
block_guard.join().map(|block_hashes| (state_hashes, block_hashes))
|
||||||
@@ -83,11 +116,13 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(chain: &BlockChain, start_block_h
|
|||||||
block_hashes: block_hashes,
|
block_hashes: block_hashes,
|
||||||
state_root: *state_root,
|
state_root: *state_root,
|
||||||
block_number: number,
|
block_number: number,
|
||||||
block_hash: start_block_hash,
|
block_hash: block_at,
|
||||||
};
|
};
|
||||||
|
|
||||||
try!(writer.into_inner().finish(manifest_data));
|
try!(writer.into_inner().finish(manifest_data));
|
||||||
|
|
||||||
|
p.done.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +135,7 @@ struct BlockChunker<'a> {
|
|||||||
hashes: Vec<H256>,
|
hashes: Vec<H256>,
|
||||||
snappy_buffer: Vec<u8>,
|
snappy_buffer: Vec<u8>,
|
||||||
writer: &'a Mutex<SnapshotWriter + 'a>,
|
writer: &'a Mutex<SnapshotWriter + 'a>,
|
||||||
|
progress: &'a Progress,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockChunker<'a> {
|
impl<'a> BlockChunker<'a> {
|
||||||
@@ -162,7 +198,8 @@ impl<'a> BlockChunker<'a> {
|
|||||||
|
|
||||||
let parent_total_difficulty = parent_details.total_difficulty;
|
let parent_total_difficulty = parent_details.total_difficulty;
|
||||||
|
|
||||||
let mut rlp_stream = RlpStream::new_list(3 + self.rlps.len());
|
let num_entries = self.rlps.len();
|
||||||
|
let mut rlp_stream = RlpStream::new_list(3 + num_entries);
|
||||||
rlp_stream.append(&parent_number).append(&parent_hash).append(&parent_total_difficulty);
|
rlp_stream.append(&parent_number).append(&parent_hash).append(&parent_total_difficulty);
|
||||||
|
|
||||||
for pair in self.rlps.drain(..) {
|
for pair in self.rlps.drain(..) {
|
||||||
@@ -178,6 +215,9 @@ impl<'a> BlockChunker<'a> {
|
|||||||
try!(self.writer.lock().write_block_chunk(hash, compressed));
|
try!(self.writer.lock().write_block_chunk(hash, compressed));
|
||||||
trace!(target: "snapshot", "wrote block chunk. hash: {}, size: {}, uncompressed size: {}", hash.hex(), size, raw_data.len());
|
trace!(target: "snapshot", "wrote block chunk. hash: {}, size: {}, uncompressed size: {}", hash.hex(), size, raw_data.len());
|
||||||
|
|
||||||
|
self.progress.size.fetch_add(size, Ordering::SeqCst);
|
||||||
|
self.progress.blocks.fetch_add(num_entries, Ordering::SeqCst);
|
||||||
|
|
||||||
self.hashes.push(hash);
|
self.hashes.push(hash);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -189,7 +229,7 @@ impl<'a> BlockChunker<'a> {
|
|||||||
/// The path parameter is the directory to store the block chunks in.
|
/// The path parameter is the directory to store the block chunks in.
|
||||||
/// This function assumes the directory exists already.
|
/// This function assumes the directory exists already.
|
||||||
/// Returns a list of chunk hashes, with the first having the blocks furthest from the genesis.
|
/// Returns a list of chunk hashes, with the first having the blocks furthest from the genesis.
|
||||||
pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), writer: &Mutex<SnapshotWriter + 'a>) -> Result<Vec<H256>, Error> {
|
pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
|
||||||
let (start_number, start_hash) = start_block_info;
|
let (start_number, start_hash) = start_block_info;
|
||||||
|
|
||||||
let first_hash = if start_number < SNAPSHOT_BLOCKS {
|
let first_hash = if start_number < SNAPSHOT_BLOCKS {
|
||||||
@@ -197,8 +237,7 @@ pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), wr
|
|||||||
chain.genesis_hash()
|
chain.genesis_hash()
|
||||||
} else {
|
} else {
|
||||||
let first_num = start_number - SNAPSHOT_BLOCKS;
|
let first_num = start_number - SNAPSHOT_BLOCKS;
|
||||||
chain.block_hash(first_num)
|
try!(chain.block_hash(first_num).ok_or(Error::IncompleteChain))
|
||||||
.expect("number before best block number; whole chain is stored; qed")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut chunker = BlockChunker {
|
let mut chunker = BlockChunker {
|
||||||
@@ -208,6 +247,7 @@ pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), wr
|
|||||||
hashes: Vec::new(),
|
hashes: Vec::new(),
|
||||||
snappy_buffer: vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)],
|
snappy_buffer: vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)],
|
||||||
writer: writer,
|
writer: writer,
|
||||||
|
progress: progress,
|
||||||
};
|
};
|
||||||
|
|
||||||
try!(chunker.chunk_all(first_hash));
|
try!(chunker.chunk_all(first_hash));
|
||||||
@@ -222,6 +262,7 @@ struct StateChunker<'a> {
|
|||||||
cur_size: usize,
|
cur_size: usize,
|
||||||
snappy_buffer: Vec<u8>,
|
snappy_buffer: Vec<u8>,
|
||||||
writer: &'a Mutex<SnapshotWriter + 'a>,
|
writer: &'a Mutex<SnapshotWriter + 'a>,
|
||||||
|
progress: &'a Progress,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StateChunker<'a> {
|
impl<'a> StateChunker<'a> {
|
||||||
@@ -249,7 +290,8 @@ impl<'a> StateChunker<'a> {
|
|||||||
// Write out the buffer to disk, pushing the created chunk's hash to
|
// Write out the buffer to disk, pushing the created chunk's hash to
|
||||||
// the list.
|
// the list.
|
||||||
fn write_chunk(&mut self) -> Result<(), Error> {
|
fn write_chunk(&mut self) -> Result<(), Error> {
|
||||||
let mut stream = RlpStream::new_list(self.rlps.len());
|
let num_entries = self.rlps.len();
|
||||||
|
let mut stream = RlpStream::new_list(num_entries);
|
||||||
for rlp in self.rlps.drain(..) {
|
for rlp in self.rlps.drain(..) {
|
||||||
stream.append_raw(&rlp, 1);
|
stream.append_raw(&rlp, 1);
|
||||||
}
|
}
|
||||||
@@ -263,6 +305,9 @@ impl<'a> StateChunker<'a> {
|
|||||||
try!(self.writer.lock().write_state_chunk(hash, compressed));
|
try!(self.writer.lock().write_state_chunk(hash, compressed));
|
||||||
trace!(target: "snapshot", "wrote state chunk. size: {}, uncompressed size: {}", compressed_size, raw_data.len());
|
trace!(target: "snapshot", "wrote state chunk. size: {}, uncompressed size: {}", compressed_size, raw_data.len());
|
||||||
|
|
||||||
|
self.progress.accounts.fetch_add(num_entries, Ordering::SeqCst);
|
||||||
|
self.progress.size.fetch_add(compressed_size, Ordering::SeqCst);
|
||||||
|
|
||||||
self.hashes.push(hash);
|
self.hashes.push(hash);
|
||||||
self.cur_size = 0;
|
self.cur_size = 0;
|
||||||
|
|
||||||
@@ -275,7 +320,7 @@ impl<'a> StateChunker<'a> {
|
|||||||
///
|
///
|
||||||
/// Returns a list of hashes of chunks created, or any error it may
|
/// Returns a list of hashes of chunks created, or any error it may
|
||||||
/// have encountered.
|
/// have encountered.
|
||||||
pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter + 'a>) -> Result<Vec<H256>, Error> {
|
pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
|
||||||
let account_trie = try!(TrieDB::new(db, &root));
|
let account_trie = try!(TrieDB::new(db, &root));
|
||||||
|
|
||||||
let mut chunker = StateChunker {
|
let mut chunker = StateChunker {
|
||||||
@@ -284,10 +329,9 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
|||||||
cur_size: 0,
|
cur_size: 0,
|
||||||
snappy_buffer: vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)],
|
snappy_buffer: vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)],
|
||||||
writer: writer,
|
writer: writer,
|
||||||
|
progress: progress,
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(target: "snapshot", "beginning state chunking");
|
|
||||||
|
|
||||||
// account_key here is the address' hash.
|
// account_key here is the address' hash.
|
||||||
for (account_key, account_data) in account_trie.iter() {
|
for (account_key, account_data) in account_trie.iter() {
|
||||||
let account = Account::from_thin_rlp(account_data);
|
let account = Account::from_thin_rlp(account_data);
|
||||||
@@ -383,6 +427,7 @@ impl StateRebuilder {
|
|||||||
let chunk_size = account_fat_rlps.len() / ::num_cpus::get() + 1;
|
let chunk_size = account_fat_rlps.len() / ::num_cpus::get() + 1;
|
||||||
|
|
||||||
// build account tries in parallel.
|
// build account tries in parallel.
|
||||||
|
// Todo [rob] keep a thread pool around so we don't do this per-chunk.
|
||||||
try!(scope(|scope| {
|
try!(scope(|scope| {
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
|
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
|
||||||
@@ -409,7 +454,7 @@ impl StateRebuilder {
|
|||||||
Ok::<_, ::error::Error>(())
|
Ok::<_, ::error::Error>(())
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let mut bloom = StateDB::load_bloom(&backing);
|
||||||
// batch trie writes
|
// batch trie writes
|
||||||
{
|
{
|
||||||
let mut account_trie = if self.state_root != SHA3_NULL_RLP {
|
let mut account_trie = if self.state_root != SHA3_NULL_RLP {
|
||||||
@@ -419,11 +464,14 @@ impl StateRebuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (hash, thin_rlp) in pairs {
|
for (hash, thin_rlp) in pairs {
|
||||||
|
bloom.set(hash.as_slice());
|
||||||
try!(account_trie.insert(&hash, &thin_rlp));
|
try!(account_trie.insert(&hash, &thin_rlp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bloom_journal = bloom.drain_journal();
|
||||||
let batch = backing.transaction();
|
let batch = backing.transaction();
|
||||||
|
try!(StateDB::commit_bloom(&batch, bloom_journal));
|
||||||
try!(self.db.inject(&batch));
|
try!(self.db.inject(&batch));
|
||||||
try!(backing.write(batch).map_err(::util::UtilError::SimpleString));
|
try!(backing.write(batch).map_err(::util::UtilError::SimpleString));
|
||||||
trace!(target: "snapshot", "current state root: {:?}", self.state_root);
|
trace!(target: "snapshot", "current state root: {:?}", self.state_root);
|
||||||
@@ -456,12 +504,12 @@ fn rebuild_account_trie(db: &mut HashDB, account_chunk: &[&[u8]], out_chunk: &mu
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Proportion of blocks which we will verify PoW for.
|
/// Proportion of blocks which we will verify `PoW` for.
|
||||||
const POW_VERIFY_RATE: f32 = 0.02;
|
const POW_VERIFY_RATE: f32 = 0.02;
|
||||||
|
|
||||||
/// Rebuilds the blockchain from chunks.
|
/// Rebuilds the blockchain from chunks.
|
||||||
///
|
///
|
||||||
/// Does basic verification for all blocks, but PoW verification for some.
|
/// Does basic verification for all blocks, but `PoW` verification for some.
|
||||||
/// Blocks must be fed in-order.
|
/// Blocks must be fed in-order.
|
||||||
///
|
///
|
||||||
/// The first block in every chunk is disconnected from the last block in the
|
/// The first block in every chunk is disconnected from the last block in the
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ impl Restoration {
|
|||||||
fn new(manifest: &ManifestData, pruning: Algorithm, path: &Path, gb: &[u8]) -> Result<Self, Error> {
|
fn new(manifest: &ManifestData, pruning: Algorithm, path: &Path, gb: &[u8]) -> Result<Self, Error> {
|
||||||
let cfg = DatabaseConfig::with_columns(::client::DB_NO_OF_COLUMNS);
|
let cfg = DatabaseConfig::with_columns(::client::DB_NO_OF_COLUMNS);
|
||||||
let raw_db = Arc::new(try!(Database::open(&cfg, &*path.to_string_lossy())
|
let raw_db = Arc::new(try!(Database::open(&cfg, &*path.to_string_lossy())
|
||||||
.map_err(|s| UtilError::SimpleString(s))));
|
.map_err(UtilError::SimpleString)));
|
||||||
|
|
||||||
let chain = BlockChain::new(Default::default(), gb, raw_db.clone());
|
let chain = BlockChain::new(Default::default(), gb, raw_db.clone());
|
||||||
let blocks = try!(BlockRebuilder::new(chain, manifest.block_number));
|
let blocks = try!(BlockRebuilder::new(chain, manifest.block_number));
|
||||||
@@ -207,23 +207,17 @@ impl Service {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// create the snapshot dir if it doesn't exist.
|
// create the snapshot dir if it doesn't exist.
|
||||||
match fs::create_dir_all(service.snapshot_dir()) {
|
if let Err(e) = fs::create_dir_all(service.snapshot_dir()) {
|
||||||
Err(e) => {
|
if e.kind() != ErrorKind::AlreadyExists {
|
||||||
if e.kind() != ErrorKind::AlreadyExists {
|
return Err(e.into())
|
||||||
return Err(e.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the temporary restoration dir if it does exist.
|
// delete the temporary restoration dir if it does exist.
|
||||||
match fs::remove_dir_all(service.restoration_dir()) {
|
if let Err(e) = fs::remove_dir_all(service.restoration_dir()) {
|
||||||
Err(e) => {
|
if e.kind() != ErrorKind::NotFound {
|
||||||
if e.kind() != ErrorKind::NotFound {
|
return Err(e.into())
|
||||||
return Err(e.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(service)
|
Ok(service)
|
||||||
@@ -434,4 +428,4 @@ impl SnapshotService for Service {
|
|||||||
self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk))
|
self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk))
|
||||||
.expect("snapshot service and io service are kept alive by client service; qed");
|
.expect("snapshot service and io service are kept alive by client service; qed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use devtools::RandomTempPath;
|
|||||||
|
|
||||||
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||||
use blockchain::BlockChain;
|
use blockchain::BlockChain;
|
||||||
use snapshot::{chunk_blocks, BlockRebuilder};
|
use snapshot::{chunk_blocks, BlockRebuilder, Progress};
|
||||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||||
|
|
||||||
use util::{Mutex, snappy};
|
use util::{Mutex, snappy};
|
||||||
@@ -55,7 +55,7 @@ fn chunk_and_restore(amount: u64) {
|
|||||||
|
|
||||||
// snapshot it.
|
// snapshot it.
|
||||||
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
|
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
|
||||||
let block_hashes = chunk_blocks(&bc, (amount, best_hash), &writer).unwrap();
|
let block_hashes = chunk_blocks(&bc, (amount, best_hash), &writer, &Progress::default()).unwrap();
|
||||||
writer.into_inner().finish(::snapshot::ManifestData {
|
writer.into_inner().finish(::snapshot::ManifestData {
|
||||||
state_hashes: Vec::new(),
|
state_hashes: Vec::new(),
|
||||||
block_hashes: block_hashes,
|
block_hashes: block_hashes,
|
||||||
@@ -88,4 +88,4 @@ fn chunk_and_restore(amount: u64) {
|
|||||||
fn chunk_and_restore_500() { chunk_and_restore(500) }
|
fn chunk_and_restore_500() { chunk_and_restore(500) }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chunk_and_restore_40k() { chunk_and_restore(40000) }
|
fn chunk_and_restore_40k() { chunk_and_restore(40000) }
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ pub fn fill_storage(mut db: AccountDBMut, root: &mut H256, seed: &mut H256) {
|
|||||||
pub fn compare_dbs(one: &HashDB, two: &HashDB) {
|
pub fn compare_dbs(one: &HashDB, two: &HashDB) {
|
||||||
let keys = one.keys();
|
let keys = one.keys();
|
||||||
|
|
||||||
for (key, _) in keys {
|
for key in keys.keys() {
|
||||||
assert_eq!(one.get(&key).unwrap(), two.get(&key).unwrap());
|
assert_eq!(one.get(&key).unwrap(), two.get(&key).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! State snapshotting tests.
|
//! State snapshotting tests.
|
||||||
|
|
||||||
use snapshot::{chunk_state, StateRebuilder};
|
use snapshot::{chunk_state, Progress, StateRebuilder};
|
||||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||||
use super::helpers::{compare_dbs, StateProducer};
|
use super::helpers::{compare_dbs, StateProducer};
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ fn snap_and_restore() {
|
|||||||
let state_root = producer.state_root();
|
let state_root = producer.state_root();
|
||||||
let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap());
|
let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap());
|
||||||
|
|
||||||
let state_hashes = chunk_state(&old_db, &state_root, &writer).unwrap();
|
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
|
||||||
|
|
||||||
writer.into_inner().finish(::snapshot::ManifestData {
|
writer.into_inner().finish(::snapshot::ManifestData {
|
||||||
state_hashes: state_hashes,
|
state_hashes: state_hashes,
|
||||||
@@ -79,4 +79,4 @@ fn snap_and_restore() {
|
|||||||
let new_db = journaldb::new(db, Algorithm::Archive, ::client::DB_COL_STATE);
|
let new_db = journaldb::new(db, Algorithm::Archive, ::client::DB_COL_STATE);
|
||||||
|
|
||||||
compare_dbs(&old_db, new_db.as_hashdb());
|
compare_dbs(&old_db, new_db.as_hashdb());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use common::*;
|
|||||||
use engines::{Engine, NullEngine, InstantSeal, BasicAuthority};
|
use engines::{Engine, NullEngine, InstantSeal, BasicAuthority};
|
||||||
use pod_state::*;
|
use pod_state::*;
|
||||||
use account_db::*;
|
use account_db::*;
|
||||||
|
use state_db::StateDB;
|
||||||
use super::genesis::Genesis;
|
use super::genesis::Genesis;
|
||||||
use super::seal::Generic as GenericSeal;
|
use super::seal::Generic as GenericSeal;
|
||||||
use ethereum;
|
use ethereum;
|
||||||
@@ -229,19 +230,20 @@ impl Spec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||||
pub fn ensure_db_good(&self, db: &mut HashDB) -> Result<bool, Box<TrieError>> {
|
pub fn ensure_db_good(&self, db: &mut StateDB) -> Result<bool, Box<TrieError>> {
|
||||||
if !db.contains(&self.state_root()) {
|
if !db.as_hashdb().contains(&self.state_root()) {
|
||||||
let mut root = H256::new();
|
let mut root = H256::new();
|
||||||
{
|
{
|
||||||
let mut t = SecTrieDBMut::new(db, &mut root);
|
let mut t = SecTrieDBMut::new(db.as_hashdb_mut(), &mut root);
|
||||||
for (address, account) in self.genesis_state.get().iter() {
|
for (address, account) in self.genesis_state.get().iter() {
|
||||||
try!(t.insert(address.as_slice(), &account.rlp()));
|
try!(t.insert(address.as_slice(), &account.rlp()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (address, account) in self.genesis_state.get().iter() {
|
for (address, account) in self.genesis_state.get().iter() {
|
||||||
account.insert_additional(&mut AccountDBMut::new(db, address));
|
db.note_account_bloom(address);
|
||||||
|
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address));
|
||||||
}
|
}
|
||||||
assert!(db.contains(&self.state_root()));
|
assert!(db.as_hashdb().contains(&self.state_root()));
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else { Ok(false) }
|
} else { Ok(false) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use trace::FlatTrace;
|
|||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
use pod_state::{self, PodState};
|
use pod_state::{self, PodState};
|
||||||
use types::state_diff::StateDiff;
|
use types::state_diff::StateDiff;
|
||||||
|
use state_db::StateDB;
|
||||||
|
|
||||||
/// Used to return information about an `State::apply` operation.
|
/// Used to return information about an `State::apply` operation.
|
||||||
pub struct ApplyOutcome {
|
pub struct ApplyOutcome {
|
||||||
@@ -37,23 +38,92 @@ pub struct ApplyOutcome {
|
|||||||
/// Result type for the execution ("application") of a transaction.
|
/// Result type for the execution ("application") of a transaction.
|
||||||
pub type ApplyResult = Result<ApplyOutcome, Error>;
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountEntry {
|
||||||
|
fn is_dirty(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
AccountEntry::Cached(ref a) => a.is_dirty(),
|
||||||
|
AccountEntry::Killed => true,
|
||||||
|
AccountEntry::Missing => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone dirty data into new `AccountEntry`.
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Representation of the entire state of all accounts in the system.
|
/// Representation of the entire state of all accounts in the system.
|
||||||
|
///
|
||||||
|
/// `State` can work together with `StateDB` to share account cache.
|
||||||
|
///
|
||||||
|
/// Local cache contains changes made locally and changes accumulated
|
||||||
|
/// locally from previous commits. Global cache reflects the database
|
||||||
|
/// state and never contains any changes.
|
||||||
|
///
|
||||||
|
/// Account data can be in the following cache states:
|
||||||
|
/// * In global but not local - something that was queried from the database,
|
||||||
|
/// but never modified
|
||||||
|
/// * In local but not global - something that was just added (e.g. new account)
|
||||||
|
/// * In both with the same value - something that was changed to a new value,
|
||||||
|
/// but changed back to a previous block in the same block (same State instance)
|
||||||
|
/// * In both with different values - something that was overwritten with a
|
||||||
|
/// new value.
|
||||||
|
///
|
||||||
|
/// All read-only state queries check local cache/modifications first,
|
||||||
|
/// then global state cache. If data is not found in any of the caches
|
||||||
|
/// it is loaded from the DB to the local cache.
|
||||||
|
///
|
||||||
|
/// Upon destruction all the local cache data merged into the global cache.
|
||||||
|
/// The merge might be rejected if current state is non-canonical.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
db: Box<JournalDB>,
|
db: StateDB,
|
||||||
root: H256,
|
root: H256,
|
||||||
cache: RefCell<HashMap<Address, Option<Account>>>,
|
cache: RefCell<HashMap<Address, AccountEntry>>,
|
||||||
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
|
snapshots: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
|
||||||
account_start_nonce: U256,
|
account_start_nonce: U256,
|
||||||
trie_factory: TrieFactory,
|
trie_factory: TrieFactory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum RequireCache {
|
||||||
|
None,
|
||||||
|
CodeSize,
|
||||||
|
Code,
|
||||||
|
}
|
||||||
|
|
||||||
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
|
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
|
||||||
Therefore creating a SecTrieDB with this state's root will not fail.";
|
Therefore creating a SecTrieDB with this state's root will not fail.";
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
/// Creates new state with empty state root
|
/// Creates new state with empty state root
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, trie_factory: TrieFactory) -> State {
|
pub fn new(mut db: StateDB, account_start_nonce: U256, trie_factory: TrieFactory) -> State {
|
||||||
let mut root = H256::new();
|
let mut root = H256::new();
|
||||||
{
|
{
|
||||||
// init trie and reset root too null
|
// init trie and reset root too null
|
||||||
@@ -71,7 +141,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new state with existing state root
|
/// Creates new state with existing state root
|
||||||
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result<State, TrieError> {
|
pub fn from_existing(db: StateDB, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result<State, TrieError> {
|
||||||
if !db.as_hashdb().contains(&root) {
|
if !db.as_hashdb().contains(&root) {
|
||||||
return Err(TrieError::InvalidStateRoot(root));
|
return Err(TrieError::InvalidStateRoot(root));
|
||||||
}
|
}
|
||||||
@@ -115,14 +185,21 @@ impl State {
|
|||||||
self.cache.borrow_mut().insert(k, v);
|
self.cache.borrow_mut().insert(k, v);
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
self.cache.borrow_mut().remove(&k);
|
match self.cache.borrow_mut().entry(k) {
|
||||||
|
::std::collections::hash_map::Entry::Occupied(e) => {
|
||||||
|
if e.get().is_dirty() {
|
||||||
|
e.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_cache(&self, address: &Address, account: Option<Account>) {
|
fn insert_cache(&self, address: &Address, account: AccountEntry) {
|
||||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||||
if !snapshot.contains_key(address) {
|
if !snapshot.contains_key(address) {
|
||||||
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
|
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
|
||||||
@@ -135,13 +212,14 @@ impl State {
|
|||||||
fn note_cache(&self, address: &Address) {
|
fn note_cache(&self, address: &Address) {
|
||||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||||
if !snapshot.contains_key(address) {
|
if !snapshot.contains_key(address) {
|
||||||
snapshot.insert(address.clone(), self.cache.borrow().get(address).cloned());
|
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_for_snapshot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy the current object and return root and database.
|
/// Destroy the current object and return root and database.
|
||||||
pub fn drop(self) -> (H256, Box<JournalDB>) {
|
pub fn drop(mut self) -> (H256, StateDB) {
|
||||||
|
self.commit_cache();
|
||||||
(self.root, self.db)
|
(self.root, self.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,41 +231,95 @@ impl State {
|
|||||||
/// Create a new contract at address `contract`. If there is already an account at the address
|
/// Create a new contract at address `contract`. If there is already an account at the address
|
||||||
/// it will have its code reset, ready for `init_code()`.
|
/// it will have its code reset, ready for `init_code()`.
|
||||||
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
|
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
|
||||||
self.insert_cache(contract, Some(Account::new_contract(balance, self.account_start_nonce)));
|
self.insert_cache(contract, AccountEntry::Cached(Account::new_contract(balance, self.account_start_nonce)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove an existing account.
|
/// Remove an existing account.
|
||||||
pub fn kill_account(&mut self, account: &Address) {
|
pub fn kill_account(&mut self, account: &Address) {
|
||||||
self.insert_cache(account, None);
|
self.insert_cache(account, AccountEntry::Killed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine whether an account exists.
|
/// Determine whether an account exists.
|
||||||
pub fn exists(&self, a: &Address) -> bool {
|
pub fn exists(&self, a: &Address) -> bool {
|
||||||
self.ensure_cached(a, false, |a| a.is_some())
|
self.ensure_cached(a, RequireCache::None, |a| a.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the balance of account `a`.
|
/// Get the balance of account `a`.
|
||||||
pub fn balance(&self, a: &Address) -> U256 {
|
pub fn balance(&self, a: &Address) -> U256 {
|
||||||
self.ensure_cached(a, false,
|
self.ensure_cached(a, RequireCache::None,
|
||||||
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
|
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the nonce of account `a`.
|
/// Get the nonce of account `a`.
|
||||||
pub fn nonce(&self, a: &Address) -> U256 {
|
pub fn nonce(&self, a: &Address) -> U256 {
|
||||||
self.ensure_cached(a, false,
|
self.ensure_cached(a, RequireCache::None,
|
||||||
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
|
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutate storage of account `address` so that it is `value` for `key`.
|
/// Mutate storage of account `address` so that it is `value` for `key`.
|
||||||
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
|
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
|
||||||
self.ensure_cached(address, false,
|
// Storage key search and update works like this:
|
||||||
|a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::new(self.db.as_hashdb(), address), key)))
|
// 1. Check bloom to see if account never used surely
|
||||||
|
// 2. If there's an entry for the account in the local cache check for the key and return it if found.
|
||||||
|
// 3. If there's an entry for the account in the global cache check for the key or load it into that account.
|
||||||
|
// 4. If account is missing in the global cache load it into the local cache and cache the key there.
|
||||||
|
|
||||||
|
// check bloom
|
||||||
|
|
||||||
|
// check local cache first without updating
|
||||||
|
{
|
||||||
|
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) => {
|
||||||
|
if let Some(value) = account.cached_storage_at(key) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
local_account = Some(maybe_acc);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => return H256::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check the global cache and and cache storage key there if found,
|
||||||
|
// otherwise cache the account localy and cache storage key there.
|
||||||
|
if let Some(result) = self.db.get_cached(address, |acc| acc.map_or(H256::new(), |a| a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key))) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if let Some(ref mut acc) = local_account {
|
||||||
|
if let AccountEntry::Cached(ref account) = **acc {
|
||||||
|
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() }
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutate storage of account `a` so that it is `value` for `key`.
|
/// Get accounts' code.
|
||||||
pub fn code(&self, a: &Address) -> Option<Bytes> {
|
pub fn code(&self, a: &Address) -> Option<Bytes> {
|
||||||
self.ensure_cached(a, true,
|
self.ensure_cached(a, RequireCache::Code,
|
||||||
|a| a.as_ref().map_or(None, |a|a.code().map(|x|x.to_vec())))
|
|a| a.as_ref().map_or(None, |a| a.code().map(|x|x.to_vec())))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get accounts' code size.
|
||||||
|
pub fn code_size(&self, a: &Address) -> Option<u64> {
|
||||||
|
self.ensure_cached(a, RequireCache::CodeSize,
|
||||||
|
|a| a.as_ref().and_then(|a| a.code_size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add `incr` to the balance of account `a`.
|
/// Add `incr` to the balance of account `a`.
|
||||||
@@ -241,26 +373,26 @@ impl State {
|
|||||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||||
try!(self.commit());
|
try!(self.commit());
|
||||||
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
||||||
// trace!("Transaction receipt: {:?}", receipt);
|
trace!(target: "state", "Transaction receipt: {:?}", receipt);
|
||||||
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
|
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
|
||||||
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
||||||
pub fn commit_into(
|
fn commit_into(
|
||||||
trie_factory: &TrieFactory,
|
trie_factory: &TrieFactory,
|
||||||
db: &mut HashDB,
|
db: &mut StateDB,
|
||||||
root: &mut H256,
|
root: &mut H256,
|
||||||
accounts: &mut HashMap<Address,
|
accounts: &mut HashMap<Address, AccountEntry>
|
||||||
Option<Account>>
|
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// first, commit the sub trees.
|
// first, commit the sub trees.
|
||||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
// 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() {
|
for (address, ref mut a) in accounts.iter_mut() {
|
||||||
match a {
|
match a {
|
||||||
&mut&mut Some(ref mut account) if account.is_dirty() => {
|
&mut&mut AccountEntry::Cached(ref mut account) if account.is_dirty() => {
|
||||||
let mut account_db = AccountDBMut::new(db, address);
|
db.note_account_bloom(&address);
|
||||||
|
let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), account.address_hash(address));
|
||||||
account.commit_storage(trie_factory, &mut account_db);
|
account.commit_storage(trie_factory, &mut account_db);
|
||||||
account.commit_code(&mut account_db);
|
account.commit_code(&mut account_db);
|
||||||
}
|
}
|
||||||
@@ -269,15 +401,18 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut trie = trie_factory.from_existing(db, root).unwrap();
|
let mut trie = trie_factory.from_existing(db.as_hashdb_mut(), root).unwrap();
|
||||||
for (address, ref mut a) in accounts.iter_mut() {
|
for (address, ref mut a) in accounts.iter_mut() {
|
||||||
match **a {
|
match **a {
|
||||||
Some(ref mut account) if account.is_dirty() => {
|
AccountEntry::Cached(ref mut account) if account.is_dirty() => {
|
||||||
account.set_clean();
|
account.set_clean();
|
||||||
try!(trie.insert(address, &account.rlp()))
|
try!(trie.insert(address, &account.rlp()));
|
||||||
},
|
},
|
||||||
None => try!(trie.remove(address)),
|
AccountEntry::Killed => {
|
||||||
_ => (),
|
try!(trie.remove(address));
|
||||||
|
**a = AccountEntry::Missing;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,10 +420,27 @@ impl State {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn commit_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);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Commits our cached account changes into the trie.
|
/// Commits our cached account changes into the trie.
|
||||||
pub fn commit(&mut self) -> Result<(), Error> {
|
pub fn commit(&mut self) -> Result<(), Error> {
|
||||||
assert!(self.snapshots.borrow().is_empty());
|
assert!(self.snapshots.borrow().is_empty());
|
||||||
Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut())
|
Self::commit_into(&self.trie_factory, &mut self.db, &mut self.root, &mut *self.cache.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear state cache
|
/// Clear state cache
|
||||||
@@ -302,7 +454,8 @@ impl State {
|
|||||||
pub fn populate_from(&mut self, accounts: PodState) {
|
pub fn populate_from(&mut self, accounts: PodState) {
|
||||||
assert!(self.snapshots.borrow().is_empty());
|
assert!(self.snapshots.borrow().is_empty());
|
||||||
for (add, acc) in accounts.drain().into_iter() {
|
for (add, acc) in accounts.drain().into_iter() {
|
||||||
self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc)));
|
self.db.note_account_bloom(&add);
|
||||||
|
self.cache.borrow_mut().insert(add, AccountEntry::Cached(Account::from_pod(acc)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +465,7 @@ impl State {
|
|||||||
// TODO: handle database rather than just the cache.
|
// TODO: handle database rather than just the cache.
|
||||||
// will need fat db.
|
// will need fat db.
|
||||||
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
||||||
if let Some(ref acc) = *opt {
|
if let AccountEntry::Cached(ref acc) = *opt {
|
||||||
m.insert(add.clone(), PodAccount::from_account(acc));
|
m.insert(add.clone(), PodAccount::from_account(acc));
|
||||||
}
|
}
|
||||||
m
|
m
|
||||||
@@ -321,7 +474,7 @@ impl State {
|
|||||||
|
|
||||||
fn query_pod(&mut self, query: &PodState) {
|
fn query_pod(&mut self, query: &PodState) {
|
||||||
for (ref address, ref pod_account) in query.get() {
|
for (ref address, ref pod_account) in query.get() {
|
||||||
self.ensure_cached(address, true, |a| {
|
self.ensure_cached(address, RequireCache::Code, |a| {
|
||||||
if a.is_some() {
|
if a.is_some() {
|
||||||
for key in pod_account.storage.keys() {
|
for key in pod_account.storage.keys() {
|
||||||
self.storage_at(address, key);
|
self.storage_at(address, key);
|
||||||
@@ -340,26 +493,61 @@ impl State {
|
|||||||
pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post)
|
pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure account `a` is in our cache of the trie DB and return a handle for getting it.
|
fn update_account_cache(require: RequireCache, account: &mut Account, address: &Address, db: &HashDB) {
|
||||||
/// `require_code` requires that the code be cached, too.
|
match require {
|
||||||
fn ensure_cached<'a, F, U>(&'a self, a: &'a Address, require_code: bool, f: F) -> U
|
RequireCache::None => {},
|
||||||
where F: FnOnce(&Option<Account>) -> U {
|
RequireCache::Code => {
|
||||||
let have_key = self.cache.borrow().contains_key(a);
|
let address_hash = account.address_hash(address);
|
||||||
if !have_key {
|
account.cache_code(&AccountDB::from_hash(db, address_hash));
|
||||||
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) {
|
RequireCache::CodeSize => {
|
||||||
Ok(acc) => acc.map(Account::from_rlp),
|
let address_hash = account.address_hash(address);
|
||||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
account.cache_code_size(&AccountDB::from_hash(db, address_hash));
|
||||||
};
|
|
||||||
self.insert_cache(a, maybe_acc);
|
|
||||||
}
|
|
||||||
if require_code {
|
|
||||||
if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() {
|
|
||||||
account.cache_code(&AccountDB::new(self.db.as_hashdb(), a));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f(self.cache.borrow().get(a).unwrap())
|
/// 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
|
||||||
|
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 {
|
||||||
|
Self::update_account_cache(require, account, a, self.db.as_hashdb());
|
||||||
|
return f(Some(account));
|
||||||
|
}
|
||||||
|
return f(None);
|
||||||
|
}
|
||||||
|
// check global cache
|
||||||
|
let result = self.db.get_cached(a, |mut acc| {
|
||||||
|
if let Some(ref mut account) = acc {
|
||||||
|
Self::update_account_cache(require, account, a, self.db.as_hashdb());
|
||||||
|
}
|
||||||
|
f(acc.map(|a| &*a))
|
||||||
|
});
|
||||||
|
match result {
|
||||||
|
Some(r) => r,
|
||||||
|
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),
|
||||||
|
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||||
|
};
|
||||||
|
if let Some(ref mut account) = maybe_acc.as_mut() {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too.
|
/// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too.
|
||||||
@@ -374,28 +562,45 @@ impl State {
|
|||||||
{
|
{
|
||||||
let contains_key = self.cache.borrow().contains_key(a);
|
let contains_key = self.cache.borrow().contains_key(a);
|
||||||
if !contains_key {
|
if !contains_key {
|
||||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
match self.db.get_cached_account(a) {
|
||||||
let maybe_acc = match db.get(&a) {
|
Some(Some(acc)) => self.insert_cache(a, AccountEntry::Cached(acc)),
|
||||||
Ok(acc) => acc.map(Account::from_rlp),
|
Some(None) => self.insert_cache(a, AccountEntry::Missing),
|
||||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
None => {
|
||||||
};
|
let maybe_acc = if self.db.check_account_bloom(a) {
|
||||||
|
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||||
self.insert_cache(a, maybe_acc);
|
let maybe_acc = match db.get(a) {
|
||||||
|
Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)),
|
||||||
|
Ok(None) => AccountEntry::Missing,
|
||||||
|
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||||
|
};
|
||||||
|
maybe_acc
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AccountEntry::Missing
|
||||||
|
};
|
||||||
|
self.insert_cache(a, maybe_acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.note_cache(a);
|
self.note_cache(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.cache.borrow_mut().get_mut(a).unwrap() {
|
match self.cache.borrow_mut().get_mut(a).unwrap() {
|
||||||
&mut Some(ref mut acc) => not_default(acc),
|
&mut AccountEntry::Cached(ref mut acc) => not_default(acc),
|
||||||
slot @ &mut None => *slot = Some(default()),
|
slot => *slot = AccountEntry::Cached(default()),
|
||||||
}
|
}
|
||||||
|
|
||||||
RefMut::map(self.cache.borrow_mut(), |c| {
|
RefMut::map(self.cache.borrow_mut(), |c| {
|
||||||
let account = c.get_mut(a).unwrap().as_mut().unwrap();
|
match c.get_mut(a).unwrap() {
|
||||||
if require_code {
|
&mut AccountEntry::Cached(ref mut account) => {
|
||||||
account.cache_code(&AccountDB::new(self.db.as_hashdb(), a));
|
if require_code {
|
||||||
|
let addr_hash = account.address_hash(a);
|
||||||
|
account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash));
|
||||||
|
}
|
||||||
|
account
|
||||||
|
},
|
||||||
|
_ => panic!("Required account must always exist; qed"),
|
||||||
}
|
}
|
||||||
account
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,11 +613,21 @@ impl fmt::Debug for State {
|
|||||||
|
|
||||||
impl Clone for State {
|
impl Clone for State {
|
||||||
fn clone(&self) -> State {
|
fn clone(&self) -> 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() {
|
||||||
|
cache.insert(key.clone(), entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache
|
||||||
|
};
|
||||||
|
|
||||||
State {
|
State {
|
||||||
db: self.db.boxed_clone(),
|
db: self.db.boxed_clone(),
|
||||||
root: self.root.clone(),
|
root: self.root.clone(),
|
||||||
cache: RefCell::new(self.cache.borrow().clone()),
|
cache: RefCell::new(cache),
|
||||||
snapshots: RefCell::new(self.snapshots.borrow().clone()),
|
snapshots: RefCell::new(Vec::new()),
|
||||||
account_start_nonce: self.account_start_nonce.clone(),
|
account_start_nonce: self.account_start_nonce.clone(),
|
||||||
trie_factory: self.trie_factory.clone(),
|
trie_factory: self.trie_factory.clone(),
|
||||||
}
|
}
|
||||||
@@ -630,7 +845,7 @@ fn should_trace_call_transaction_to_builtin() {
|
|||||||
|
|
||||||
let mut info = EnvInfo::default();
|
let mut info = EnvInfo::default();
|
||||||
info.gas_limit = 1_000_000.into();
|
info.gas_limit = 1_000_000.into();
|
||||||
let engine = Spec::new_test().engine;
|
let engine = &*Spec::new_test().engine;
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: 0.into(),
|
nonce: 0.into(),
|
||||||
@@ -642,7 +857,7 @@ fn should_trace_call_transaction_to_builtin() {
|
|||||||
}.sign(&"".sha3());
|
}.sign(&"".sha3());
|
||||||
|
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = vec![FlatTrace {
|
let expected_trace = vec![FlatTrace {
|
||||||
trace_address: Default::default(),
|
trace_address: Default::default(),
|
||||||
@@ -673,7 +888,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
|||||||
|
|
||||||
let mut info = EnvInfo::default();
|
let mut info = EnvInfo::default();
|
||||||
info.gas_limit = 1_000_000.into();
|
info.gas_limit = 1_000_000.into();
|
||||||
let engine = Spec::new_test().engine;
|
let engine = &*Spec::new_test().engine;
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: 0.into(),
|
nonce: 0.into(),
|
||||||
@@ -686,7 +901,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
|||||||
|
|
||||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = vec![FlatTrace {
|
let expected_trace = vec![FlatTrace {
|
||||||
trace_address: Default::default(),
|
trace_address: Default::default(),
|
||||||
@@ -717,7 +932,7 @@ fn should_not_trace_callcode() {
|
|||||||
|
|
||||||
let mut info = EnvInfo::default();
|
let mut info = EnvInfo::default();
|
||||||
info.gas_limit = 1_000_000.into();
|
info.gas_limit = 1_000_000.into();
|
||||||
let engine = Spec::new_test().engine;
|
let engine = &*Spec::new_test().engine;
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: 0.into(),
|
nonce: 0.into(),
|
||||||
@@ -731,7 +946,7 @@ fn should_not_trace_callcode() {
|
|||||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
||||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = vec![FlatTrace {
|
let expected_trace = vec![FlatTrace {
|
||||||
trace_address: Default::default(),
|
trace_address: Default::default(),
|
||||||
@@ -778,7 +993,7 @@ fn should_not_trace_delegatecall() {
|
|||||||
let mut info = EnvInfo::default();
|
let mut info = EnvInfo::default();
|
||||||
info.gas_limit = 1_000_000.into();
|
info.gas_limit = 1_000_000.into();
|
||||||
info.number = 0x789b0;
|
info.number = 0x789b0;
|
||||||
let engine = Spec::new_test().engine;
|
let engine = &*Spec::new_test().engine;
|
||||||
|
|
||||||
println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);
|
println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call);
|
||||||
|
|
||||||
@@ -794,7 +1009,7 @@ fn should_not_trace_delegatecall() {
|
|||||||
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
||||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap();
|
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||||
|
|
||||||
let expected_trace = vec![FlatTrace {
|
let expected_trace = vec![FlatTrace {
|
||||||
trace_address: Default::default(),
|
trace_address: Default::default(),
|
||||||
@@ -1489,4 +1704,4 @@ fn create_empty() {
|
|||||||
assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
|
assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
231
ethcore/src/state_db.rs
Normal file
231
ethcore/src/state_db.rs
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
// 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 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 bloomfilter::{Bloom, BloomJournal};
|
||||||
|
use util::Database;
|
||||||
|
use client::DB_COL_ACCOUNT_BLOOM;
|
||||||
|
use byteorder::{LittleEndian, ByteOrder};
|
||||||
|
|
||||||
|
const STATE_CACHE_ITEMS: usize = 65536;
|
||||||
|
|
||||||
|
struct AccountCache {
|
||||||
|
/// DB Account cache. `None` indicates that account is known to be missing.
|
||||||
|
accounts: LruCache<Address, Option<Account>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State database abstraction.
|
||||||
|
/// Manages shared global state cache.
|
||||||
|
/// A clone of `StateDB` may be created as canonical or not.
|
||||||
|
/// For canonical clones cache changes are accumulated and applied
|
||||||
|
/// on commit.
|
||||||
|
/// For non-canonical clones cache is cleared on commit.
|
||||||
|
pub struct StateDB {
|
||||||
|
db: Box<JournalDB>,
|
||||||
|
account_cache: Arc<Mutex<AccountCache>>,
|
||||||
|
cache_overlay: Vec<(Address, Option<Account>)>,
|
||||||
|
is_canon: bool,
|
||||||
|
account_bloom: Arc<Mutex<Bloom>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;
|
||||||
|
pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;
|
||||||
|
|
||||||
|
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
|
||||||
|
|
||||||
|
impl StateDB {
|
||||||
|
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");
|
||||||
|
|
||||||
|
if hash_count_entry.is_none() {
|
||||||
|
return Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET);
|
||||||
|
}
|
||||||
|
let hash_count_bytes = hash_count_entry.unwrap();
|
||||||
|
assert_eq!(hash_count_bytes.len(), 1);
|
||||||
|
let hash_count = hash_count_bytes[0];
|
||||||
|
|
||||||
|
let mut bloom_parts = vec![0u64; ACCOUNT_BLOOM_SPACE / 8];
|
||||||
|
let mut key = [0u8; 8];
|
||||||
|
for i in 0..ACCOUNT_BLOOM_SPACE / 8 {
|
||||||
|
LittleEndian::write_u64(&mut key, i as u64);
|
||||||
|
bloom_parts[i] = db.get(DB_COL_ACCOUNT_BLOOM, &key).expect("low-level database error")
|
||||||
|
.and_then(|val| Some(LittleEndian::read_u64(&val[..])))
|
||||||
|
.unwrap_or(0u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bloom = Bloom::from_parts(&bloom_parts, hash_count as u32);
|
||||||
|
trace!(target: "account_bloom", "Bloom is {:?} full, hash functions count = {:?}", bloom.how_full(), hash_count);
|
||||||
|
bloom
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance wrapping `JournalDB`
|
||||||
|
pub fn new(db: Box<JournalDB>) -> 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_bloom: Arc::new(Mutex::new(bloom)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_account_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) {
|
||||||
|
trace!(target: "account_bloom", "Note account bloom: {:?}", address);
|
||||||
|
let mut bloom = self.account_bloom.lock();
|
||||||
|
bloom.set(address.sha3().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit_bloom(batch: &DBTransaction, journal: BloomJournal) -> Result<(), UtilError> {
|
||||||
|
assert!(journal.hash_functions <= 255);
|
||||||
|
try!(batch.put(DB_COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &vec![journal.hash_functions as u8]));
|
||||||
|
let mut key = [0u8; 8];
|
||||||
|
let mut val = [0u8; 8];
|
||||||
|
|
||||||
|
for (bloom_part_index, bloom_part_value) in journal.entries {
|
||||||
|
LittleEndian::write_u64(&mut key, bloom_part_index as u64);
|
||||||
|
LittleEndian::write_u64(&mut val, bloom_part_value);
|
||||||
|
try!(batch.put(DB_COL_ACCOUNT_BLOOM, &key, &val));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit all recent insert operations and canonical historical commits' removals from the
|
||||||
|
/// old era to the backing database, reverting any non-canonical historical commit's inserts.
|
||||||
|
pub fn commit(&mut self, batch: &DBTransaction, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
|
{
|
||||||
|
let mut bloom_lock = self.account_bloom.lock();
|
||||||
|
try!(Self::commit_bloom(batch, bloom_lock.drain_journal()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let records = try!(self.db.commit(batch, now, id, end));
|
||||||
|
if self.is_canon {
|
||||||
|
self.commit_cache();
|
||||||
|
} else {
|
||||||
|
self.clear_cache();
|
||||||
|
}
|
||||||
|
Ok(records)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an interface to HashDB.
|
||||||
|
pub fn as_hashdb(&self) -> &HashDB {
|
||||||
|
self.db.as_hashdb()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an interface to mutable HashDB.
|
||||||
|
pub fn as_hashdb_mut(&mut self) -> &mut HashDB {
|
||||||
|
self.db.as_hashdb_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone the database.
|
||||||
|
pub fn boxed_clone(&self) -> StateDB {
|
||||||
|
StateDB {
|
||||||
|
db: self.db.boxed_clone(),
|
||||||
|
account_cache: self.account_cache.clone(),
|
||||||
|
cache_overlay: Vec::new(),
|
||||||
|
is_canon: false,
|
||||||
|
account_bloom: self.account_bloom.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone the database for a canonical state.
|
||||||
|
pub fn boxed_clone_canon(&self) -> StateDB {
|
||||||
|
StateDB {
|
||||||
|
db: self.db.boxed_clone(),
|
||||||
|
account_cache: self.account_cache.clone(),
|
||||||
|
cache_overlay: Vec::new(),
|
||||||
|
is_canon: true,
|
||||||
|
account_bloom: self.account_bloom.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if pruning is enabled on the database.
|
||||||
|
pub fn is_pruned(&self) -> bool {
|
||||||
|
self.db.is_pruned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Heap size used.
|
||||||
|
pub fn mem_used(&self) -> usize {
|
||||||
|
self.db.mem_used() //TODO: + self.account_cache.lock().heap_size_of_children()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns underlying `JournalDB`.
|
||||||
|
pub fn journal_db(&self) -> &JournalDB {
|
||||||
|
&*self.db
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enqueue cache change.
|
||||||
|
pub fn cache_account(&mut self, addr: Address, data: Option<Account>) {
|
||||||
|
self.cache_overlay.push((addr, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub fn get_cached_account(&self, addr: &Address) -> Option<Option<Account>> {
|
||||||
|
if !self.is_canon {
|
||||||
|
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.
|
||||||
|
pub fn get_cached<F, U>(&self, a: &Address, f: F) -> Option<U>
|
||||||
|
where F: FnOnce(Option<&mut Account>) -> U {
|
||||||
|
if !self.is_canon {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut cache = self.account_cache.lock();
|
||||||
|
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use io::IoChannel;
|
use io::IoChannel;
|
||||||
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID};
|
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID};
|
||||||
|
use ethereum;
|
||||||
use block::IsBlock;
|
use block::IsBlock;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use common::*;
|
use common::*;
|
||||||
@@ -31,6 +32,14 @@ fn imports_from_empty() {
|
|||||||
client.flush_queue();
|
client.flush_queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_registrar() {
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
let spec = ethereum::new_morden();
|
||||||
|
let client = Client::new(ClientConfig::default(), &spec, dir.as_path(), Arc::new(Miner::with_spec(&spec)), IoChannel::disconnected()).unwrap();
|
||||||
|
assert_eq!(client.additional_params().get("registrar"), Some(&"8e4e9b13d4b45cb0befc93c3061b1408f67316b2".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_state_root_basic() {
|
fn returns_state_root_basic() {
|
||||||
let client_result = generate_dummy_client(6);
|
let client_result = generate_dummy_client(6);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use io::*;
|
|||||||
use client::{self, BlockChainClient, Client, ClientConfig};
|
use client::{self, BlockChainClient, Client, ClientConfig};
|
||||||
use common::*;
|
use common::*;
|
||||||
use spec::*;
|
use spec::*;
|
||||||
|
use state_db::StateDB;
|
||||||
use block::{OpenBlock, Drain};
|
use block::{OpenBlock, Drain};
|
||||||
use blockchain::{BlockChain, Config as BlockChainConfig};
|
use blockchain::{BlockChain, Config as BlockChainConfig};
|
||||||
use state::*;
|
use state::*;
|
||||||
@@ -134,11 +135,11 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
|||||||
|
|
||||||
let test_spec = get_test_spec();
|
let test_spec = get_test_spec();
|
||||||
let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected()).unwrap();
|
let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected()).unwrap();
|
||||||
let test_engine = &test_spec.engine;
|
let test_engine = &*test_spec.engine;
|
||||||
|
|
||||||
let mut db_result = get_temp_journal_db();
|
let mut db_result = get_temp_state_db();
|
||||||
let mut db = db_result.take();
|
let mut db = db_result.take();
|
||||||
test_spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
|
test_spec.ensure_db_good(&mut db).unwrap();
|
||||||
let vm_factory = Default::default();
|
let vm_factory = Default::default();
|
||||||
let genesis_header = test_spec.genesis_header();
|
let genesis_header = test_spec.genesis_header();
|
||||||
|
|
||||||
@@ -155,7 +156,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
|||||||
|
|
||||||
// forge block.
|
// forge block.
|
||||||
let mut b = OpenBlock::new(
|
let mut b = OpenBlock::new(
|
||||||
test_engine.deref(),
|
test_engine,
|
||||||
&vm_factory,
|
&vm_factory,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
false,
|
false,
|
||||||
@@ -183,7 +184,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
|||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = b.close_and_lock().seal(test_engine.deref(), vec![]).unwrap();
|
let b = b.close_and_lock().seal(test_engine, vec![]).unwrap();
|
||||||
|
|
||||||
if let Err(e) = client.import_block(b.rlp_bytes()) {
|
if let Err(e) = client.import_block(b.rlp_bytes()) {
|
||||||
panic!("error importing block which is valid by definition: {:?}", e);
|
panic!("error importing block which is valid by definition: {:?}", e);
|
||||||
@@ -303,9 +304,9 @@ pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
|
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let journal_db = get_temp_journal_db_in(temp.as_path());
|
let journal_db = get_temp_state_db_in(temp.as_path());
|
||||||
|
|
||||||
GuardedTempResult {
|
GuardedTempResult {
|
||||||
_temp: temp,
|
_temp: temp,
|
||||||
@@ -315,7 +316,7 @@ pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
|
|||||||
|
|
||||||
pub fn get_temp_state() -> GuardedTempResult<State> {
|
pub fn get_temp_state() -> GuardedTempResult<State> {
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let journal_db = get_temp_journal_db_in(temp.as_path());
|
let journal_db = get_temp_state_db_in(temp.as_path());
|
||||||
|
|
||||||
GuardedTempResult {
|
GuardedTempResult {
|
||||||
_temp: temp,
|
_temp: temp,
|
||||||
@@ -323,13 +324,14 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> {
|
pub fn get_temp_state_db_in(path: &Path) -> StateDB {
|
||||||
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
|
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
|
||||||
journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, None)
|
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, client::DB_COL_STATE);
|
||||||
|
StateDB::new(journal_db)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_temp_state_in(path: &Path) -> State {
|
pub fn get_temp_state_in(path: &Path) -> State {
|
||||||
let journal_db = get_temp_journal_db_in(path);
|
let journal_db = get_temp_state_db_in(path);
|
||||||
State::new(journal_db, U256::from(0), Default::default())
|
State::new(journal_db, U256::from(0), Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Trace database.
|
//! Trace database.
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::Deref;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use bloomchain::{Number, Config as BloomConfig};
|
use bloomchain::{Number, Config as BloomConfig};
|
||||||
@@ -119,8 +119,9 @@ pub struct TraceDB<T> where T: DatabaseExtras {
|
|||||||
impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras {
|
impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras {
|
||||||
fn blooms_at(&self, position: &GroupPosition) -> Option<BloomGroup> {
|
fn blooms_at(&self, position: &GroupPosition) -> Option<BloomGroup> {
|
||||||
let position = TraceGroupPosition::from(position.clone());
|
let position = TraceGroupPosition::from(position.clone());
|
||||||
self.note_used(CacheID::Bloom(position.clone()));
|
let result = self.tracesdb.read_with_cache(DB_COL_TRACE, &self.blooms, &position).map(Into::into);
|
||||||
self.tracesdb.read_with_cache(DB_COL_TRACE, &self.blooms, &position).map(Into::into)
|
self.note_used(CacheID::Bloom(position));
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,11 +175,13 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
|||||||
|
|
||||||
/// Ticks our cache system and throws out any old data.
|
/// Ticks our cache system and throws out any old data.
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
let mut cache_manager = self.cache_manager.write();
|
let current_size = self.cache_size();
|
||||||
cache_manager.collect_garbage(|| self.cache_size(), | ids | {
|
|
||||||
let mut traces = self.traces.write();
|
|
||||||
let mut blooms = self.blooms.write();
|
|
||||||
|
|
||||||
|
let mut traces = self.traces.write();
|
||||||
|
let mut blooms = self.blooms.write();
|
||||||
|
let mut cache_manager = self.cache_manager.write();
|
||||||
|
|
||||||
|
cache_manager.collect_garbage(current_size, | ids | {
|
||||||
for id in &ids {
|
for id in &ids {
|
||||||
match *id {
|
match *id {
|
||||||
CacheID::Trace(ref h) => { traces.remove(h); },
|
CacheID::Trace(ref h) => { traces.remove(h); },
|
||||||
@@ -187,13 +190,16 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
|||||||
}
|
}
|
||||||
traces.shrink_to_fit();
|
traces.shrink_to_fit();
|
||||||
blooms.shrink_to_fit();
|
blooms.shrink_to_fit();
|
||||||
|
|
||||||
|
traces.heap_size_of_children() + blooms.heap_size_of_children()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns traces for block with hash.
|
/// Returns traces for block with hash.
|
||||||
fn traces(&self, block_hash: &H256) -> Option<FlatBlockTraces> {
|
fn traces(&self, block_hash: &H256) -> Option<FlatBlockTraces> {
|
||||||
|
let result = self.tracesdb.read_with_cache(DB_COL_TRACE, &self.traces, block_hash);
|
||||||
self.note_used(CacheID::Trace(block_hash.clone()));
|
self.note_used(CacheID::Trace(block_hash.clone()));
|
||||||
self.tracesdb.read_with_cache(DB_COL_TRACE, &self.traces, block_hash)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns vector of transaction traces for given block.
|
/// Returns vector of transaction traces for given block.
|
||||||
@@ -264,12 +270,12 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
|
|
||||||
// at first, let's insert new block traces
|
// at first, let's insert new block traces
|
||||||
{
|
{
|
||||||
// note_used must be called before locking traces to avoid cache/traces deadlock on garbage collection
|
|
||||||
self.note_used(CacheID::Trace(request.block_hash.clone()));
|
|
||||||
let mut traces = self.traces.write();
|
let mut traces = self.traces.write();
|
||||||
// it's important to use overwrite here,
|
// it's important to use overwrite here,
|
||||||
// cause this value might be queried by hash later
|
// cause this value might be queried by hash later
|
||||||
batch.write_with_cache(DB_COL_TRACE, traces.deref_mut(), request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
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
|
// now let's rebuild the blooms
|
||||||
@@ -294,12 +300,13 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
.map(|p| (From::from(p.0), From::from(p.1)))
|
.map(|p| (From::from(p.0), From::from(p.1)))
|
||||||
.collect::<HashMap<TraceGroupPosition, blooms::BloomGroup>>();
|
.collect::<HashMap<TraceGroupPosition, blooms::BloomGroup>>();
|
||||||
|
|
||||||
// note_used must be called before locking blooms to avoid cache/traces deadlock on garbage collection
|
let blooms_keys: Vec<_> = blooms_to_insert.keys().cloned().collect();
|
||||||
for key in blooms_to_insert.keys() {
|
|
||||||
self.note_used(CacheID::Bloom(key.clone()));
|
|
||||||
}
|
|
||||||
let mut blooms = self.blooms.write();
|
let mut blooms = self.blooms.write();
|
||||||
batch.extend_with_cache(DB_COL_TRACE, blooms.deref_mut(), blooms_to_insert, CacheUpdatePolicy::Remove);
|
batch.extend_with_cache(DB_COL_TRACE, &mut *blooms, blooms_to_insert, CacheUpdatePolicy::Remove);
|
||||||
|
// note_used must be called after locking blooms to avoid cache/traces deadlock on garbage collection
|
||||||
|
for key in blooms_keys.into_iter() {
|
||||||
|
self.note_used(CacheID::Bloom(key));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ fn update_trace_address(traces: Vec<FlatTrace>) -> Vec<FlatTrace> {
|
|||||||
let mut subtrace_subtraces_left = 0;
|
let mut subtrace_subtraces_left = 0;
|
||||||
traces.into_iter().map(|mut trace| {
|
traces.into_iter().map(|mut trace| {
|
||||||
let is_top_subtrace = trace.trace_address.is_empty();
|
let is_top_subtrace = trace.trace_address.is_empty();
|
||||||
|
let is_subtrace = trace.trace_address.len() == 1;
|
||||||
trace.trace_address.push_front(top_subtrace_index);
|
trace.trace_address.push_front(top_subtrace_index);
|
||||||
|
|
||||||
if is_top_subtrace {
|
if is_top_subtrace {
|
||||||
subtrace_subtraces_left = trace.subtraces;
|
subtrace_subtraces_left = trace.subtraces;
|
||||||
} else {
|
} else if is_subtrace {
|
||||||
subtrace_subtraces_left -= 1;
|
subtrace_subtraces_left -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,23 @@ impl HeapSizeOf for Receipt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receipt with additional info.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||||
|
pub struct RichReceipt {
|
||||||
|
/// Transaction hash.
|
||||||
|
pub transaction_hash: H256,
|
||||||
|
/// Transaction index.
|
||||||
|
pub transaction_index: usize,
|
||||||
|
/// The total gas used in the block following execution of the transaction.
|
||||||
|
pub cumulative_gas_used: U256,
|
||||||
|
/// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`.
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Contract address.
|
||||||
|
pub contract_address: Option<Address>,
|
||||||
|
/// Logs
|
||||||
|
pub logs: Vec<LogEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Receipt with additional info.
|
/// Receipt with additional info.
|
||||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||||
pub struct LocalizedReceipt {
|
pub struct LocalizedReceipt {
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
|
|||||||
try!(verify_header(&header, engine));
|
try!(verify_header(&header, engine));
|
||||||
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
||||||
try!(engine.verify_block_basic(&header, Some(bytes)));
|
try!(engine.verify_block_basic(&header, Some(bytes)));
|
||||||
for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
for u in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
|
let u = try!(u);
|
||||||
try!(verify_header(&u, engine));
|
try!(verify_header(&u, engine));
|
||||||
try!(engine.verify_block_basic(&u, None));
|
try!(engine.verify_block_basic(&u, None));
|
||||||
}
|
}
|
||||||
@@ -58,8 +59,8 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
|
|||||||
/// Returns a `PreverifiedBlock` structure populated with transactions
|
/// Returns a `PreverifiedBlock` structure populated with transactions
|
||||||
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreverifiedBlock, Error> {
|
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreverifiedBlock, Error> {
|
||||||
try!(engine.verify_block_unordered(&header, Some(&bytes)));
|
try!(engine.verify_block_unordered(&header, Some(&bytes)));
|
||||||
for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
for u in try!(UntrustedRlp::new(&bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
try!(engine.verify_block_unordered(&u, None));
|
try!(engine.verify_block_unordered(&try!(u), None));
|
||||||
}
|
}
|
||||||
// Verify transactions.
|
// Verify transactions.
|
||||||
let mut transactions = Vec::new();
|
let mut transactions = Vec::new();
|
||||||
@@ -84,7 +85,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
|||||||
try!(verify_parent(&header, &parent));
|
try!(verify_parent(&header, &parent));
|
||||||
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
|
try!(engine.verify_block_family(&header, &parent, Some(bytes)));
|
||||||
|
|
||||||
let num_uncles = Rlp::new(bytes).at(2).item_count();
|
let num_uncles = try!(UntrustedRlp::new(bytes).at(2)).item_count();
|
||||||
if num_uncles != 0 {
|
if num_uncles != 0 {
|
||||||
if num_uncles > engine.maximum_uncle_count() {
|
if num_uncles > engine.maximum_uncle_count() {
|
||||||
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles })));
|
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles })));
|
||||||
@@ -106,7 +107,8 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
for uncle in try!(UntrustedRlp::new(bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
|
let uncle = try!(uncle);
|
||||||
if excluded.contains(&uncle.hash()) {
|
if excluded.contains(&uncle.hash()) {
|
||||||
return Err(From::from(BlockError::UncleInChain(uncle.hash())))
|
return Err(From::from(BlockError::UncleInChain(uncle.hash())))
|
||||||
}
|
}
|
||||||
@@ -210,13 +212,13 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
|
|||||||
|
|
||||||
/// Verify block data against header: transactions root and uncles hash.
|
/// Verify block data against header: transactions root and uncles hash.
|
||||||
fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> {
|
fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> {
|
||||||
let block = Rlp::new(block);
|
let block = UntrustedRlp::new(block);
|
||||||
let tx = block.at(1);
|
let tx = try!(block.at(1));
|
||||||
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here
|
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here
|
||||||
if expected_root != transactions_root {
|
if expected_root != transactions_root {
|
||||||
return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() })))
|
return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() })))
|
||||||
}
|
}
|
||||||
let expected_uncles = &block.at(2).as_raw().sha3();
|
let expected_uncles = &try!(block.at(2)).as_raw().sha3();
|
||||||
if expected_uncles != uncles_hash {
|
if expected_uncles != uncles_hash {
|
||||||
return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() })))
|
return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() })))
|
||||||
}
|
}
|
||||||
@@ -342,7 +344,7 @@ mod tests {
|
|||||||
// Test against morden
|
// Test against morden
|
||||||
let mut good = Header::new();
|
let mut good = Header::new();
|
||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
let engine = &spec.engine;
|
let engine = &*spec.engine;
|
||||||
|
|
||||||
let min_gas_limit = engine.params().min_gas_limit;
|
let min_gas_limit = engine.params().min_gas_limit;
|
||||||
good.gas_limit = min_gas_limit;
|
good.gas_limit = min_gas_limit;
|
||||||
@@ -423,69 +425,69 @@ mod tests {
|
|||||||
bc.insert(create_test_block(&parent7));
|
bc.insert(create_test_block(&parent7));
|
||||||
bc.insert(create_test_block(&parent8));
|
bc.insert(create_test_block(&parent8));
|
||||||
|
|
||||||
check_ok(basic_test(&create_test_block(&good), engine.deref()));
|
check_ok(basic_test(&create_test_block(&good), engine));
|
||||||
|
|
||||||
let mut header = good.clone();
|
let mut header = good.clone();
|
||||||
header.transactions_root = good_transactions_root.clone();
|
header.transactions_root = good_transactions_root.clone();
|
||||||
header.uncles_hash = good_uncles_hash.clone();
|
header.uncles_hash = good_uncles_hash.clone();
|
||||||
check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()));
|
check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine));
|
||||||
|
|
||||||
header.gas_limit = min_gas_limit - From::from(1);
|
header.gas_limit = min_gas_limit - From::from(1);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }));
|
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.number = BlockNumber::max_value();
|
header.number = BlockNumber::max_value();
|
||||||
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
|
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.gas_used = header.gas_limit + From::from(1);
|
header.gas_used = header.gas_limit + From::from(1);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }));
|
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
|
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
|
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
|
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
|
||||||
check_fail(basic_test(&create_test_block(&header), engine.deref()),
|
check_fail(basic_test(&create_test_block(&header), engine),
|
||||||
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
|
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.uncles_hash = good_uncles_hash.clone();
|
header.uncles_hash = good_uncles_hash.clone();
|
||||||
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()),
|
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
|
||||||
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root }));
|
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.transactions_root = good_transactions_root.clone();
|
header.transactions_root = good_transactions_root.clone();
|
||||||
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()),
|
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine),
|
||||||
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
|
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
|
||||||
|
|
||||||
check_ok(family_test(&create_test_block(&good), engine.deref(), &bc));
|
check_ok(family_test(&create_test_block(&good), engine, &bc));
|
||||||
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine.deref(), &bc));
|
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine, &bc));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.parent_hash = H256::random();
|
header.parent_hash = H256::random();
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
||||||
UnknownParent(header.parent_hash));
|
UnknownParent(header.parent_hash));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.timestamp = 10;
|
header.timestamp = 10;
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
||||||
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }));
|
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
header.number = 9;
|
header.number = 9;
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc),
|
||||||
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
|
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
|
||||||
|
|
||||||
header = good.clone();
|
header = good.clone();
|
||||||
let mut bad_uncles = good_uncles.clone();
|
let mut bad_uncles = good_uncles.clone();
|
||||||
bad_uncles.push(good_uncle1.clone());
|
bad_uncles.push(good_uncle1.clone());
|
||||||
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine.deref(), &bc),
|
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc),
|
||||||
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() }));
|
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() }));
|
||||||
|
|
||||||
// TODO: some additional uncle checks
|
// TODO: some additional uncle checks
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ pub fn verify_public(public: &Public, signature: &Signature, message: &Message)
|
|||||||
|
|
||||||
let pdata: [u8; 65] = {
|
let pdata: [u8; 65] = {
|
||||||
let mut temp = [4u8; 65];
|
let mut temp = [4u8; 65];
|
||||||
temp[1..65].copy_from_slice(public.deref());
|
temp[1..65].copy_from_slice(&**public);
|
||||||
temp
|
temp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use ethkey::{KeyPair, sign, Address, Secret, Signature, Message};
|
use ethkey::{KeyPair, sign, Address, Secret, Signature, Message};
|
||||||
use {json, Error, crypto};
|
use {json, Error, crypto};
|
||||||
use crypto::Keccak256;
|
use crypto::Keccak256;
|
||||||
@@ -87,7 +86,7 @@ impl Crypto {
|
|||||||
let mut ciphertext = [0u8; 32];
|
let mut ciphertext = [0u8; 32];
|
||||||
|
|
||||||
// aes-128-ctr with initial vector of iv
|
// aes-128-ctr with initial vector of iv
|
||||||
crypto::aes::encrypt(&derived_left_bits, &iv, secret.deref(), &mut ciphertext);
|
crypto::aes::encrypt(&derived_left_bits, &iv, &**secret, &mut ciphertext);
|
||||||
|
|
||||||
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
|
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
|
||||||
let mac = crypto::derive_mac(&derived_right_bits, &ciphertext).keccak256();
|
let mac = crypto::derive_mac(&derived_right_bits, &ciphertext).keccak256();
|
||||||
@@ -123,7 +122,7 @@ impl Crypto {
|
|||||||
|
|
||||||
match self.cipher {
|
match self.cipher {
|
||||||
Cipher::Aes128Ctr(ref params) => {
|
Cipher::Aes128Ctr(ref params) => {
|
||||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, secret.deref_mut())
|
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut *secret)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
} else if args.cmd_import {
|
} else if args.cmd_import {
|
||||||
let src = try!(key_dir(&args.flag_src));
|
let src = try!(key_dir(&args.flag_src));
|
||||||
let dst = try!(key_dir(&args.flag_dir));
|
let dst = try!(key_dir(&args.flag_dir));
|
||||||
let accounts = try!(import_accounts(src.deref(), dst.deref()));
|
let accounts = try!(import_accounts(*src, *dst));
|
||||||
Ok(format_accounts(&accounts))
|
Ok(format_accounts(&accounts))
|
||||||
} else if args.cmd_import_wallet {
|
} else if args.cmd_import_wallet {
|
||||||
let wallet = try!(PresaleWallet::open(&args.arg_path));
|
let wallet = try!(PresaleWallet::open(&args.arg_path));
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ use ethkey::Address;
|
|||||||
use {json, SafeAccount, Error};
|
use {json, SafeAccount, Error};
|
||||||
use super::KeyDirectory;
|
use super::KeyDirectory;
|
||||||
|
|
||||||
|
const IGNORED_FILES: &'static [&'static str] = &["thumbs.db"];
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> {
|
fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> {
|
||||||
use std::ffi;
|
use std::ffi;
|
||||||
@@ -62,7 +64,14 @@ impl DiskDirectory {
|
|||||||
.flat_map(Result::ok)
|
.flat_map(Result::ok)
|
||||||
.filter(|entry| {
|
.filter(|entry| {
|
||||||
let metadata = entry.metadata();
|
let metadata = entry.metadata();
|
||||||
metadata.is_ok() && !metadata.unwrap().is_dir()
|
let file_name = entry.file_name();
|
||||||
|
let name = file_name.to_str().unwrap();
|
||||||
|
// filter directories
|
||||||
|
metadata.is_ok() && !metadata.unwrap().is_dir() &&
|
||||||
|
// hidden files
|
||||||
|
!name.starts_with(".") &&
|
||||||
|
// other ignored files
|
||||||
|
!IGNORED_FILES.contains(&name)
|
||||||
})
|
})
|
||||||
.map(|entry| entry.path())
|
.map(|entry| entry.path())
|
||||||
.collect::<Vec<PathBuf>>();
|
.collect::<Vec<PathBuf>>();
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ use std::sync::*;
|
|||||||
use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut};
|
use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
const POLL_TIMEOUT: isize = 100;
|
const POLL_TIMEOUT: isize = 200;
|
||||||
const CLIENT_CONNECTION_TIMEOUT: isize = 2500;
|
const CLIENT_CONNECTION_TIMEOUT: isize = 15000;
|
||||||
|
|
||||||
/// Generic worker to handle service (binded) sockets
|
/// Generic worker to handle service (binded) sockets
|
||||||
pub struct Worker<S: ?Sized> where S: IpcInterface {
|
pub struct Worker<S: ?Sized> where S: IpcInterface {
|
||||||
@@ -68,7 +68,6 @@ pub fn init_duplex_client<S>(socket_addr: &str) -> Result<GuardedSocket<S>, Sock
|
|||||||
SocketError::DuplexLink
|
SocketError::DuplexLink
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 2500 ms default timeout
|
|
||||||
socket.set_receive_timeout(CLIENT_CONNECTION_TIMEOUT).unwrap();
|
socket.set_receive_timeout(CLIENT_CONNECTION_TIMEOUT).unwrap();
|
||||||
|
|
||||||
let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
|
let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
|
||||||
@@ -91,7 +90,6 @@ pub fn init_client<S>(socket_addr: &str) -> Result<GuardedSocket<S>, SocketError
|
|||||||
SocketError::RequestLink
|
SocketError::RequestLink
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 2500 ms default timeout
|
|
||||||
socket.set_receive_timeout(CLIENT_CONNECTION_TIMEOUT).unwrap();
|
socket.set_receive_timeout(CLIENT_CONNECTION_TIMEOUT).unwrap();
|
||||||
|
|
||||||
let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
|
let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ macro_rules! impl_hash {
|
|||||||
0 => $inner::from(0),
|
0 => $inner::from(0),
|
||||||
2 if value == "0x" => $inner::from(0),
|
2 if value == "0x" => $inner::from(0),
|
||||||
_ if value.starts_with("0x") => try!($inner::from_str(&value[2..]).map_err(|_| {
|
_ if value.starts_with("0x") => try!($inner::from_str(&value[2..]).map_err(|_| {
|
||||||
Error::custom(format!("Invalid hex value {}.", value).as_ref())
|
Error::custom(format!("Invalid hex value {}.", value).as_str())
|
||||||
})),
|
})),
|
||||||
_ => try!($inner::from_str(value).map_err(|_| {
|
_ => try!($inner::from_str(value).map_err(|_| {
|
||||||
Error::custom(format!("Invalid hex value {}.", value).as_ref())
|
Error::custom(format!("Invalid hex value {}.", value).as_str())
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ impl Visitor for UintVisitor {
|
|||||||
0 => U256::from(0),
|
0 => U256::from(0),
|
||||||
2 if value.starts_with("0x") => U256::from(0),
|
2 if value.starts_with("0x") => U256::from(0),
|
||||||
_ if value.starts_with("0x") => try!(U256::from_str(&value[2..]).map_err(|_| {
|
_ if value.starts_with("0x") => try!(U256::from_str(&value[2..]).map_err(|_| {
|
||||||
Error::custom(format!("Invalid hex value {}.", value).as_ref())
|
Error::custom(format!("Invalid hex value {}.", value).as_str())
|
||||||
})),
|
})),
|
||||||
_ => try!(U256::from_dec_str(value).map_err(|_| {
|
_ => try!(U256::from_dec_str(value).map_err(|_| {
|
||||||
Error::custom(format!("Invalid decimal value {}.", value).as_ref())
|
Error::custom(format!("Invalid decimal value {}.", value).as_str())
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -89,10 +89,10 @@ pub fn setup_log(config: &Config) -> Result<Arc<RotatingLogger>, String> {
|
|||||||
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
|
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
|
||||||
|
|
||||||
let with_color = if max_log_level() <= LogLevelFilter::Info {
|
let with_color = if max_log_level() <= LogLevelFilter::Info {
|
||||||
format!("{}{}", Colour::Black.bold().paint(timestamp), record.args())
|
format!("{} {}", Colour::Black.bold().paint(timestamp), record.args())
|
||||||
} else {
|
} else {
|
||||||
let name = thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x)));
|
let name = thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x)));
|
||||||
format!("{}{} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args())
|
format!("{} {} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args())
|
||||||
};
|
};
|
||||||
|
|
||||||
let removed_color = kill_color(with_color.as_ref());
|
let removed_color = kill_color(with_color.as_ref());
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||||
!define VERSIONMAJOR 1
|
!define VERSIONMAJOR 1
|
||||||
!define VERSIONMINOR 3
|
!define VERSIONMINOR 3
|
||||||
!define VERSIONBUILD 0
|
!define VERSIONBUILD 2
|
||||||
|
|
||||||
!addplugindir .\
|
!addplugindir .\
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ API and Console Options:
|
|||||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||||
server, IP should be an interface's IP address,
|
server, IP should be an interface's IP address,
|
||||||
or local [default: local].
|
or local [default: local].
|
||||||
|
--dapps-hosts HOSTS List of allowed Host header values. This option will
|
||||||
|
validate the Host header sent by the browser, it
|
||||||
|
is additional security against some attack
|
||||||
|
vectors. Special options: "all", "none",
|
||||||
|
[default: none].
|
||||||
--dapps-user USERNAME Specify username for Dapps server. It will be
|
--dapps-user USERNAME Specify username for Dapps server. It will be
|
||||||
used in HTTP Basic Authentication Scheme.
|
used in HTTP Basic Authentication Scheme.
|
||||||
If --dapps-pass is not specified you will be
|
If --dapps-pass is not specified you will be
|
||||||
@@ -231,6 +236,12 @@ Import/Export Options:
|
|||||||
--format FORMAT For import/export in given format. FORMAT must be
|
--format FORMAT For import/export in given format. FORMAT must be
|
||||||
one of 'hex' and 'binary'.
|
one of 'hex' and 'binary'.
|
||||||
|
|
||||||
|
Snapshot Options:
|
||||||
|
--at BLOCK Take a snapshot at the given block, which may be an
|
||||||
|
index, hash, or 'latest'. Note that taking snapshots at
|
||||||
|
non-recent blocks will only work with --pruning archive
|
||||||
|
[default: latest]
|
||||||
|
|
||||||
Virtual Machine Options:
|
Virtual Machine Options:
|
||||||
--jitvm Enable the JIT VM.
|
--jitvm Enable the JIT VM.
|
||||||
|
|
||||||
@@ -337,6 +348,7 @@ pub struct Args {
|
|||||||
pub flag_no_dapps: bool,
|
pub flag_no_dapps: bool,
|
||||||
pub flag_dapps_port: u16,
|
pub flag_dapps_port: u16,
|
||||||
pub flag_dapps_interface: String,
|
pub flag_dapps_interface: String,
|
||||||
|
pub flag_dapps_hosts: String,
|
||||||
pub flag_dapps_user: Option<String>,
|
pub flag_dapps_user: Option<String>,
|
||||||
pub flag_dapps_pass: Option<String>,
|
pub flag_dapps_pass: Option<String>,
|
||||||
pub flag_dapps_path: String,
|
pub flag_dapps_path: String,
|
||||||
@@ -365,6 +377,7 @@ pub struct Args {
|
|||||||
pub flag_version: bool,
|
pub flag_version: bool,
|
||||||
pub flag_from: String,
|
pub flag_from: String,
|
||||||
pub flag_to: String,
|
pub flag_to: String,
|
||||||
|
pub flag_at: String,
|
||||||
pub flag_format: Option<String>,
|
pub flag_format: Option<String>,
|
||||||
pub flag_jitvm: bool,
|
pub flag_jitvm: bool,
|
||||||
pub flag_log_file: Option<String>,
|
pub flag_log_file: Option<String>,
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ impl Configuration {
|
|||||||
file_path: self.args.arg_file.clone(),
|
file_path: self.args.arg_file.clone(),
|
||||||
wal: wal,
|
wal: wal,
|
||||||
kind: snapshot::Kind::Take,
|
kind: snapshot::Kind::Take,
|
||||||
|
block_at: try!(to_block_id(&self.args.flag_at)),
|
||||||
};
|
};
|
||||||
Cmd::Snapshot(snapshot_cmd)
|
Cmd::Snapshot(snapshot_cmd)
|
||||||
} else if self.args.cmd_restore {
|
} else if self.args.cmd_restore {
|
||||||
@@ -186,6 +187,7 @@ impl Configuration {
|
|||||||
file_path: self.args.arg_file.clone(),
|
file_path: self.args.arg_file.clone(),
|
||||||
wal: wal,
|
wal: wal,
|
||||||
kind: snapshot::Kind::Restore,
|
kind: snapshot::Kind::Restore,
|
||||||
|
block_at: try!(to_block_id("latest")), // unimportant.
|
||||||
};
|
};
|
||||||
Cmd::Snapshot(restore_cmd)
|
Cmd::Snapshot(restore_cmd)
|
||||||
} else {
|
} else {
|
||||||
@@ -353,6 +355,7 @@ impl Configuration {
|
|||||||
enabled: self.dapps_enabled(),
|
enabled: self.dapps_enabled(),
|
||||||
interface: self.dapps_interface(),
|
interface: self.dapps_interface(),
|
||||||
port: self.args.flag_dapps_port,
|
port: self.args.flag_dapps_port,
|
||||||
|
hosts: self.dapps_hosts(),
|
||||||
user: self.args.flag_dapps_user.clone(),
|
user: self.args.flag_dapps_user.clone(),
|
||||||
pass: self.args.flag_dapps_pass.clone(),
|
pass: self.args.flag_dapps_pass.clone(),
|
||||||
dapps_path: self.directories().dapps,
|
dapps_path: self.directories().dapps,
|
||||||
@@ -472,6 +475,16 @@ impl Configuration {
|
|||||||
Some(hosts)
|
Some(hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dapps_hosts(&self) -> Option<Vec<String>> {
|
||||||
|
match self.args.flag_dapps_hosts.as_ref() {
|
||||||
|
"none" => return Some(Vec::new()),
|
||||||
|
"all" => return None,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let hosts = self.args.flag_dapps_hosts.split(',').map(|h| h.into()).collect();
|
||||||
|
Some(hosts)
|
||||||
|
}
|
||||||
|
|
||||||
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
||||||
let conf = IpcConfiguration {
|
let conf = IpcConfiguration {
|
||||||
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
||||||
@@ -499,8 +512,6 @@ impl Configuration {
|
|||||||
NetworkSettings {
|
NetworkSettings {
|
||||||
name: self.args.flag_identity.clone(),
|
name: self.args.flag_identity.clone(),
|
||||||
chain: self.chain(),
|
chain: self.chain(),
|
||||||
max_peers: self.max_peers(),
|
|
||||||
min_peers: self.min_peers(),
|
|
||||||
network_port: self.args.flag_port,
|
network_port: self.args.flag_port,
|
||||||
rpc_enabled: !self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc,
|
rpc_enabled: !self.args.flag_jsonrpc_off && !self.args.flag_no_jsonrpc,
|
||||||
rpc_interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()),
|
rpc_interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()),
|
||||||
@@ -509,6 +520,8 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn directories(&self) -> Directories {
|
fn directories(&self) -> Directories {
|
||||||
|
use util::path;
|
||||||
|
|
||||||
let db_path = replace_home(self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path));
|
let db_path = replace_home(self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path));
|
||||||
|
|
||||||
let keys_path = replace_home(
|
let keys_path = replace_home(
|
||||||
@@ -522,6 +535,12 @@ impl Configuration {
|
|||||||
let dapps_path = replace_home(&self.args.flag_dapps_path);
|
let dapps_path = replace_home(&self.args.flag_dapps_path);
|
||||||
let signer_path = replace_home(&self.args.flag_signer_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));
|
||||||
|
}
|
||||||
|
|
||||||
Directories {
|
Directories {
|
||||||
keys: keys_path,
|
keys: keys_path,
|
||||||
db: db_path,
|
db: db_path,
|
||||||
@@ -769,8 +788,6 @@ mod tests {
|
|||||||
assert_eq!(conf.network_settings(), NetworkSettings {
|
assert_eq!(conf.network_settings(), NetworkSettings {
|
||||||
name: "testname".to_owned(),
|
name: "testname".to_owned(),
|
||||||
chain: "morden".to_owned(),
|
chain: "morden".to_owned(),
|
||||||
max_peers: 50,
|
|
||||||
min_peers: 25,
|
|
||||||
network_port: 30303,
|
network_port: 30303,
|
||||||
rpc_enabled: true,
|
rpc_enabled: true,
|
||||||
rpc_interface: "local".to_owned(),
|
rpc_interface: "local".to_owned(),
|
||||||
@@ -826,6 +843,23 @@ mod tests {
|
|||||||
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_dapps_hosts() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--dapps-hosts", "none"]);
|
||||||
|
let conf2 = parse(&["parity", "--dapps-hosts", "all"]);
|
||||||
|
let conf3 = parse(&["parity", "--dapps-hosts", "ethcore.io,something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.dapps_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf1.dapps_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf2.dapps_hosts(), None);
|
||||||
|
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_disable_signer_in_geth_compat() {
|
fn should_disable_signer_in_geth_compat() {
|
||||||
// given
|
// given
|
||||||
|
|||||||
104
parity/dapps.rs
104
parity/dapps.rs
@@ -15,21 +15,16 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use io::PanicHandler;
|
use io::PanicHandler;
|
||||||
use rpc_apis;
|
use rpc_apis;
|
||||||
use helpers::replace_home;
|
use helpers::replace_home;
|
||||||
|
|
||||||
#[cfg(feature = "dapps")]
|
|
||||||
pub use ethcore_dapps::Server as WebappServer;
|
|
||||||
#[cfg(not(feature = "dapps"))]
|
|
||||||
pub struct WebappServer;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub interface: String,
|
pub interface: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub hosts: Option<Vec<String>>,
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub pass: Option<String>,
|
pub pass: Option<String>,
|
||||||
pub dapps_path: String,
|
pub dapps_path: String,
|
||||||
@@ -41,6 +36,7 @@ impl Default for Configuration {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
hosts: Some(Vec::new()),
|
||||||
user: None,
|
user: None,
|
||||||
pass: None,
|
pass: None,
|
||||||
dapps_path: replace_home("$HOME/.parity/dapps"),
|
dapps_path: replace_home("$HOME/.parity/dapps"),
|
||||||
@@ -72,48 +68,68 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
|
|||||||
(username.to_owned(), password)
|
(username.to_owned(), password)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, auth))))
|
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, configuration.hosts, auth))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use self::server::setup_dapps_server;
|
||||||
|
pub use self::server::WebappServer;
|
||||||
|
|
||||||
#[cfg(not(feature = "dapps"))]
|
#[cfg(not(feature = "dapps"))]
|
||||||
pub fn setup_dapps_server(
|
mod server {
|
||||||
_deps: Dependencies,
|
use super::Dependencies;
|
||||||
_dapps_path: String,
|
use std::net::SocketAddr;
|
||||||
_url: &SocketAddr,
|
|
||||||
_auth: Option<(String, String)>,
|
|
||||||
) -> Result<WebappServer, String> {
|
|
||||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dapps")]
|
pub struct WebappServer;
|
||||||
pub fn setup_dapps_server(
|
pub fn setup_dapps_server(
|
||||||
deps: Dependencies,
|
_deps: Dependencies,
|
||||||
dapps_path: String,
|
_dapps_path: String,
|
||||||
url: &SocketAddr,
|
_url: &SocketAddr,
|
||||||
auth: Option<(String, String)>
|
_allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Result<WebappServer, String> {
|
_auth: Option<(String, String)>,
|
||||||
use ethcore_dapps as dapps;
|
) -> Result<WebappServer, String> {
|
||||||
|
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||||
let server = dapps::ServerBuilder::new(dapps_path);
|
}
|
||||||
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
}
|
||||||
let start_result = match auth {
|
|
||||||
None => {
|
#[cfg(feature = "dapps")]
|
||||||
server.start_unsecure_http(url)
|
mod server {
|
||||||
},
|
use super::Dependencies;
|
||||||
Some((username, password)) => {
|
use std::net::SocketAddr;
|
||||||
server.start_basic_auth_http(url, &username, &password)
|
|
||||||
},
|
use rpc_apis;
|
||||||
};
|
|
||||||
|
pub use ethcore_dapps::Server as WebappServer;
|
||||||
match start_result {
|
|
||||||
Err(dapps::ServerError::IoError(err)) => Err(format!("WebApps io error: {}", err)),
|
pub fn setup_dapps_server(
|
||||||
Err(e) => Err(format!("WebApps error: {:?}", e)),
|
deps: Dependencies,
|
||||||
Ok(server) => {
|
dapps_path: String,
|
||||||
server.set_panic_handler(move || {
|
url: &SocketAddr,
|
||||||
deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned());
|
allowed_hosts: Option<Vec<String>>,
|
||||||
});
|
auth: Option<(String, String)>
|
||||||
Ok(server)
|
) -> Result<WebappServer, String> {
|
||||||
},
|
use ethcore_dapps as dapps;
|
||||||
|
|
||||||
|
let server = dapps::ServerBuilder::new(dapps_path);
|
||||||
|
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
||||||
|
let start_result = match auth {
|
||||||
|
None => {
|
||||||
|
server.start_unsecured_http(url, allowed_hosts)
|
||||||
|
},
|
||||||
|
Some((username, password)) => {
|
||||||
|
server.start_basic_auth_http(url, allowed_hosts, &username, &password)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match start_result {
|
||||||
|
Err(dapps::ServerError::IoError(err)) => Err(format!("WebApps io error: {}", err)),
|
||||||
|
Err(e) => Err(format!("WebApps error: {:?}", e)),
|
||||||
|
Ok(server) => {
|
||||||
|
server.set_panic_handler(move || {
|
||||||
|
deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned());
|
||||||
|
});
|
||||||
|
Ok(server)
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use self::ansi_term::Style;
|
|||||||
use std::sync::{Arc};
|
use std::sync::{Arc};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use isatty::{stdout_isatty};
|
use isatty::{stdout_isatty};
|
||||||
use ethsync::{SyncProvider, ManageNetwork};
|
use ethsync::{SyncProvider, ManageNetwork};
|
||||||
use util::{Uint, RwLock, Mutex, H256, Colour};
|
use util::{Uint, RwLock, Mutex, H256, Colour};
|
||||||
@@ -112,7 +111,7 @@ impl Informant {
|
|||||||
paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))),
|
paint(White.bold(), format!("{:>8}", format!("#{}", chain_info.best_block_number))),
|
||||||
paint(White.bold(), format!("{}", chain_info.best_block_hash)),
|
paint(White.bold(), format!("{}", chain_info.best_block_hash)),
|
||||||
{
|
{
|
||||||
let last_report = match write_report.deref() { &Some(ref last_report) => last_report.clone(), _ => ClientReport::default() };
|
let last_report = match *write_report { Some(ref last_report) => last_report.clone(), _ => ClientReport::default() };
|
||||||
format!("{} blk/s {} tx/s {} Mgas/s",
|
format!("{} blk/s {} tx/s {} Mgas/s",
|
||||||
paint(Yellow.bold(), format!("{:4}", ((report.blocks_imported - last_report.blocks_imported) * 1000) as u64 / elapsed.as_milliseconds())),
|
paint(Yellow.bold(), format!("{:4}", ((report.blocks_imported - last_report.blocks_imported) * 1000) as u64 / elapsed.as_milliseconds())),
|
||||||
paint(Yellow.bold(), format!("{:4}", ((report.transactions_applied - last_report.transactions_applied) * 1000) as u64 / elapsed.as_milliseconds())),
|
paint(Yellow.bold(), format!("{:4}", ((report.transactions_applied - last_report.transactions_applied) * 1000) as u64 / elapsed.as_milliseconds())),
|
||||||
@@ -132,7 +131,7 @@ impl Informant {
|
|||||||
},
|
},
|
||||||
paint(Cyan.bold(), format!("{:2}", sync_info.num_active_peers)),
|
paint(Cyan.bold(), format!("{:2}", sync_info.num_active_peers)),
|
||||||
paint(Cyan.bold(), format!("{:2}", sync_info.num_peers)),
|
paint(Cyan.bold(), format!("{:2}", sync_info.num_peers)),
|
||||||
paint(Cyan.bold(), format!("{:2}", if sync_info.num_peers as u32 > net_config.min_peers { net_config.max_peers} else { net_config.min_peers} ))
|
paint(Cyan.bold(), format!("{:2}", sync_info.current_max_peers(net_config.min_peers, net_config.max_peers))),
|
||||||
),
|
),
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
},
|
},
|
||||||
@@ -147,9 +146,9 @@ impl Informant {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
*self.chain_info.write().deref_mut() = Some(chain_info);
|
*self.chain_info.write() = Some(chain_info);
|
||||||
*self.cache_info.write().deref_mut() = Some(cache_info);
|
*self.cache_info.write() = Some(cache_info);
|
||||||
*write_report.deref_mut() = Some(report);
|
*write_report = Some(report);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use ethcore::migrations::Extract;
|
|||||||
/// Database is assumed to be at default version, when no version file is found.
|
/// Database is assumed to be at default version, when no version file is found.
|
||||||
const DEFAULT_VERSION: u32 = 5;
|
const DEFAULT_VERSION: u32 = 5;
|
||||||
/// Current version of database models.
|
/// Current version of database models.
|
||||||
const CURRENT_VERSION: u32 = 9;
|
const CURRENT_VERSION: u32 = 10;
|
||||||
/// First version of the consolidated database.
|
/// First version of the consolidated database.
|
||||||
const CONSOLIDATION_VERSION: u32 = 9;
|
const CONSOLIDATION_VERSION: u32 = 9;
|
||||||
/// Defines how many items are migrated to the new version of database at once.
|
/// Defines how many items are migrated to the new version of database at once.
|
||||||
@@ -140,7 +140,12 @@ pub fn default_migration_settings(compaction_profile: &CompactionProfile) -> Mig
|
|||||||
|
|
||||||
/// Migrations on the consolidated database.
|
/// Migrations on the consolidated database.
|
||||||
fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> Result<MigrationManager, Error> {
|
fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> Result<MigrationManager, Error> {
|
||||||
let manager = MigrationManager::new(default_migration_settings(compaction_profile));
|
let mut manager = MigrationManager::new(default_migration_settings(compaction_profile));
|
||||||
|
// this won't ever fire, because version will be already 9
|
||||||
|
// added because migration api should know that it should open database with 5 columns
|
||||||
|
try!(manager.add_migration(migrations::ToV9::new(Some(5), migrations::Extract::All)).map_err(|_| Error::MigrationImpossible));
|
||||||
|
// this will
|
||||||
|
try!(manager.add_migration(migrations::ToV10).map_err(|_| Error::MigrationImpossible));
|
||||||
Ok(manager)
|
Ok(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +185,6 @@ fn consolidate_database(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Migrates database at given position with given migration rules.
|
/// Migrates database at given position with given migration rules.
|
||||||
fn migrate_database(version: u32, db_path: PathBuf, mut migrations: MigrationManager) -> Result<(), Error> {
|
fn migrate_database(version: u32, db_path: PathBuf, mut migrations: MigrationManager) -> Result<(), Error> {
|
||||||
// check if migration is needed
|
// check if migration is needed
|
||||||
@@ -215,6 +219,12 @@ fn exists(path: &Path) -> bool {
|
|||||||
fs::metadata(path).is_ok()
|
fs::metadata(path).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in-place upgrades that do nothing when called repeatedly
|
||||||
|
fn run_inplace_upgrades(path: &Path) -> Result<(), Error> {
|
||||||
|
try!(migrations::upgrade_account_bloom(path));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Migrates the database.
|
/// Migrates the database.
|
||||||
pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionProfile) -> Result<(), Error> {
|
pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionProfile) -> Result<(), Error> {
|
||||||
// read version file.
|
// read version file.
|
||||||
@@ -228,6 +238,7 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
|||||||
|
|
||||||
// We are in the latest version, yay!
|
// We are in the latest version, yay!
|
||||||
if version == CURRENT_VERSION {
|
if version == CURRENT_VERSION {
|
||||||
|
try!(run_inplace_upgrades(consolidated_database_path(path).as_path()));
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +270,8 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
|||||||
println!("Migration finished");
|
println!("Migration finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try!(run_inplace_upgrades(consolidated_database_path(path).as_path()));
|
||||||
|
|
||||||
// update version file.
|
// update version file.
|
||||||
update_version(path)
|
update_version(path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,10 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::fs;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use util::{contents, Database, DatabaseConfig, journaldb, H256, Address, U256, version_data};
|
use util::{contents, H256, Address, U256, version_data};
|
||||||
use util::journaldb::Algorithm;
|
use util::journaldb::Algorithm;
|
||||||
use ethcore::client;
|
|
||||||
use ethcore::spec::Spec;
|
use ethcore::spec::Spec;
|
||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
use ethcore::miner::{GasPricer, GasPriceCalibratorOptions};
|
use ethcore::miner::{GasPricer, GasPriceCalibratorOptions};
|
||||||
@@ -100,20 +99,13 @@ impl Pruning {
|
|||||||
|
|
||||||
fn find_best_db(dirs: &Directories, genesis_hash: H256, fork_name: Option<&String>) -> Algorithm {
|
fn find_best_db(dirs: &Directories, genesis_hash: H256, fork_name: Option<&String>) -> Algorithm {
|
||||||
let mut algo_types = Algorithm::all_types();
|
let mut algo_types = Algorithm::all_types();
|
||||||
|
// if all dbs have the same modification time, the last element is the default one
|
||||||
// if all dbs have the same latest era, the last element is the default one
|
|
||||||
algo_types.push(Algorithm::default());
|
algo_types.push(Algorithm::default());
|
||||||
|
|
||||||
algo_types.into_iter().max_by_key(|i| {
|
algo_types.into_iter().max_by_key(|i| {
|
||||||
let client_path = dirs.client_path(genesis_hash, fork_name, *i);
|
let mut client_path = dirs.client_path(genesis_hash, fork_name, *i);
|
||||||
let config = DatabaseConfig::with_columns(client::DB_NO_OF_COLUMNS);
|
client_path.push("CURRENT");
|
||||||
let db = match Database::open(&config, client_path.to_str().unwrap()) {
|
fs::metadata(&client_path).and_then(|m| m.modified()).ok()
|
||||||
Ok(db) => db,
|
|
||||||
Err(_) => return 0,
|
|
||||||
};
|
|
||||||
let db = journaldb::new(Arc::new(db), *i, client::DB_COL_STATE);
|
|
||||||
trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era());
|
|
||||||
db.latest_era().unwrap_or(0)
|
|
||||||
}).unwrap()
|
}).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
|
|||||||
},
|
},
|
||||||
Api::Ethcore => {
|
Api::Ethcore => {
|
||||||
let queue = deps.signer_port.map(|_| deps.signer_queue.clone());
|
let queue = deps.signer_port.map(|_| deps.signer_queue.clone());
|
||||||
server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone(), queue).to_delegate())
|
server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, &deps.sync, &deps.net_service, deps.logger.clone(), deps.settings.clone(), queue).to_delegate())
|
||||||
},
|
},
|
||||||
Api::EthcoreSet => {
|
Api::EthcoreSet => {
|
||||||
server.add_delegate(EthcoreSetClient::new(&deps.client, &deps.miner, &deps.net_service).to_delegate())
|
server.add_delegate(EthcoreSetClient::new(&deps.client, &deps.miner, &deps.net_service).to_delegate())
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ use ethcore::account_provider::AccountProvider;
|
|||||||
use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions};
|
use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions};
|
||||||
use ethsync::SyncConfig;
|
use ethsync::SyncConfig;
|
||||||
use informant::Informant;
|
use informant::Informant;
|
||||||
#[cfg(feature="ipc")]
|
|
||||||
use ethcore::client::ChainNotify;
|
|
||||||
|
|
||||||
use rpc::{HttpServer, IpcServer, HttpConfiguration, IpcConfiguration};
|
use rpc::{HttpServer, IpcServer, HttpConfiguration, IpcConfiguration};
|
||||||
use signer::SignerServer;
|
use signer::SignerServer;
|
||||||
@@ -82,9 +80,6 @@ pub struct RunCmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
||||||
// create supervisor
|
|
||||||
let mut hypervisor = modules::hypervisor();
|
|
||||||
|
|
||||||
// increase max number of open files
|
// increase max number of open files
|
||||||
raise_fd_limit();
|
raise_fd_limit();
|
||||||
|
|
||||||
@@ -167,6 +162,9 @@ pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
|||||||
net_conf.boot_nodes = spec.nodes.clone();
|
net_conf.boot_nodes = spec.nodes.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create supervisor
|
||||||
|
let mut hypervisor = modules::hypervisor();
|
||||||
|
|
||||||
// create client service.
|
// create client service.
|
||||||
let service = try!(ClientService::start(
|
let service = try!(ClientService::start(
|
||||||
client_config,
|
client_config,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ fn codes_path(path: String) -> PathBuf {
|
|||||||
|
|
||||||
pub fn new_token(path: String) -> Result<String, String> {
|
pub fn new_token(path: String) -> Result<String, String> {
|
||||||
generate_new_token(path)
|
generate_new_token(path)
|
||||||
.map(|code| format!("This key code will authorise your System Signer UI: {}", Colour::White.bold().paint(code)))
|
.map(|code| format!("This key code will authorise your System Signer UI: {}", code))
|
||||||
.map_err(|err| format!("Error generating token: {:?}", err))
|
.map_err(|err| format!("Error generating token: {:?}", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,15 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ethcore_logger::{setup_log, Config as LogConfig};
|
use ethcore_logger::{setup_log, Config as LogConfig};
|
||||||
use ethcore::snapshot::{RestorationStatus, SnapshotService};
|
use ethcore::snapshot::{Progress, RestorationStatus, SnapshotService};
|
||||||
use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter};
|
use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter};
|
||||||
use ethcore::service::ClientService;
|
use ethcore::service::ClientService;
|
||||||
use ethcore::client::{Mode, DatabaseCompactionProfile, Switch, VMType};
|
use ethcore::client::{Mode, DatabaseCompactionProfile, Switch, VMType};
|
||||||
use ethcore::miner::Miner;
|
use ethcore::miner::Miner;
|
||||||
|
use ethcore::ids::BlockID;
|
||||||
|
|
||||||
use cache::CacheConfig;
|
use cache::CacheConfig;
|
||||||
use params::{SpecType, Pruning};
|
use params::{SpecType, Pruning};
|
||||||
use helpers::{to_client_config, execute_upgrades};
|
use helpers::{to_client_config, execute_upgrades};
|
||||||
@@ -56,6 +59,7 @@ pub struct SnapshotCommand {
|
|||||||
pub file_path: Option<String>,
|
pub file_path: Option<String>,
|
||||||
pub wal: bool,
|
pub wal: bool,
|
||||||
pub kind: Kind,
|
pub kind: Kind,
|
||||||
|
pub block_at: BlockID,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SnapshotCommand {
|
impl SnapshotCommand {
|
||||||
@@ -105,7 +109,7 @@ impl SnapshotCommand {
|
|||||||
warn!("Snapshot restoration is experimental and the format may be subject to change.");
|
warn!("Snapshot restoration is experimental and the format may be subject to change.");
|
||||||
|
|
||||||
let snapshot = service.snapshot_service();
|
let snapshot = service.snapshot_service();
|
||||||
let reader = PackedReader::new(&Path::new(&file))
|
let reader = PackedReader::new(Path::new(&file))
|
||||||
.map_err(|e| format!("Couldn't open snapshot file: {}", e))
|
.map_err(|e| format!("Couldn't open snapshot file: {}", e))
|
||||||
.and_then(|x| x.ok_or("Snapshot file has invalid format.".into()));
|
.and_then(|x| x.ok_or("Snapshot file has invalid format.".into()));
|
||||||
|
|
||||||
@@ -168,6 +172,7 @@ impl SnapshotCommand {
|
|||||||
pub fn take_snapshot(self) -> Result<(), String> {
|
pub fn take_snapshot(self) -> Result<(), String> {
|
||||||
let file_path = try!(self.file_path.clone().ok_or("No file path provided.".to_owned()));
|
let file_path = try!(self.file_path.clone().ok_or("No file path provided.".to_owned()));
|
||||||
let file_path: PathBuf = file_path.into();
|
let file_path: PathBuf = file_path.into();
|
||||||
|
let block_at = self.block_at;
|
||||||
let (service, _panic_handler) = try!(self.start_service());
|
let (service, _panic_handler) = try!(self.start_service());
|
||||||
|
|
||||||
warn!("Snapshots are currently experimental. File formats may be subject to change.");
|
warn!("Snapshots are currently experimental. File formats may be subject to change.");
|
||||||
@@ -175,11 +180,35 @@ impl SnapshotCommand {
|
|||||||
let writer = try!(PackedWriter::new(&file_path)
|
let writer = try!(PackedWriter::new(&file_path)
|
||||||
.map_err(|e| format!("Failed to open snapshot writer: {}", e)));
|
.map_err(|e| format!("Failed to open snapshot writer: {}", e)));
|
||||||
|
|
||||||
if let Err(e) = service.client().take_snapshot(writer) {
|
let progress = Arc::new(Progress::default());
|
||||||
|
let p = progress.clone();
|
||||||
|
let informant_handle = ::std::thread::spawn(move || {
|
||||||
|
::std::thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
|
let mut last_size = 0;
|
||||||
|
while !p.done() {
|
||||||
|
let cur_size = p.size();
|
||||||
|
if cur_size != last_size {
|
||||||
|
last_size = cur_size;
|
||||||
|
info!("Snapshot: {} accounts {} blocks {} bytes", p.accounts(), p.blocks(), p.size());
|
||||||
|
} else {
|
||||||
|
info!("Snapshot: No progress since last update.");
|
||||||
|
}
|
||||||
|
|
||||||
|
::std::thread::sleep(Duration::from_secs(5));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = service.client().take_snapshot(writer, block_at, &*progress) {
|
||||||
let _ = ::std::fs::remove_file(&file_path);
|
let _ = ::std::fs::remove_file(&file_path);
|
||||||
return Err(format!("Encountered fatal error while creating snapshot: {}", e));
|
return Err(format!("Encountered fatal error while creating snapshot: {}", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("snapshot creation complete");
|
||||||
|
|
||||||
|
assert!(progress.done());
|
||||||
|
try!(informant_handle.join().map_err(|_| "failed to join logger thread"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,4 +221,4 @@ pub fn execute(cmd: SnapshotCommand) -> Result<String, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ build = "build.rs"
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
serde_json = "0.7.0"
|
serde_json = "0.7.0"
|
||||||
jsonrpc-core = "2.0"
|
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" }
|
||||||
ethcore-io = { path = "../util/io" }
|
ethcore-io = { path = "../util/io" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
|
|||||||
96
rpc/src/v1/helpers/dispatch.rs
Normal file
96
rpc/src/v1/helpers/dispatch.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// 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 util::numbers::*;
|
||||||
|
use util::rlp::encode;
|
||||||
|
use util::bytes::ToPretty;
|
||||||
|
use ethcore::miner::MinerService;
|
||||||
|
use ethcore::client::MiningBlockChainClient;
|
||||||
|
use ethcore::transaction::{Action, SignedTransaction, Transaction};
|
||||||
|
use ethcore::account_provider::AccountProvider;
|
||||||
|
use jsonrpc_core::{Error, Value, to_value};
|
||||||
|
use v1::helpers::TransactionRequest;
|
||||||
|
use v1::types::{H256 as RpcH256, H520 as RpcH520};
|
||||||
|
use v1::helpers::errors;
|
||||||
|
|
||||||
|
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
Transaction {
|
||||||
|
nonce: request.nonce
|
||||||
|
.or_else(|| miner
|
||||||
|
.last_nonce(&request.from)
|
||||||
|
.map(|nonce| nonce + U256::one()))
|
||||||
|
.unwrap_or_else(|| client.latest_nonce(&request.from)),
|
||||||
|
|
||||||
|
action: request.to.map_or(Action::Create, Action::Call),
|
||||||
|
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
|
||||||
|
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
|
||||||
|
value: request.value.unwrap_or_else(U256::zero),
|
||||||
|
data: request.data.map_or_else(Vec::new, |b| b.to_vec()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
||||||
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
let hash = RpcH256::from(signed_transaction.hash());
|
||||||
|
|
||||||
|
let import = miner.import_own_transaction(client, signed_transaction);
|
||||||
|
|
||||||
|
import
|
||||||
|
.map_err(errors::from_transaction_error)
|
||||||
|
.and_then(|_| to_value(&hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result<Value, Error> {
|
||||||
|
accounts.sign_with_password(address, pass, hash)
|
||||||
|
.map_err(errors::from_password_error)
|
||||||
|
.and_then(|hash| to_value(&RpcH520::from(hash)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
|
||||||
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
|
let address = request.from;
|
||||||
|
let signed_transaction = {
|
||||||
|
let t = prepare_transaction(client, miner, request);
|
||||||
|
let hash = t.hash();
|
||||||
|
let signature = try!(account_provider.sign_with_password(address, password, hash).map_err(errors::from_password_error));
|
||||||
|
t.with_signature(signature)
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
||||||
|
dispatch_transaction(&*client, &*miner, signed_transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result<Value, Error>
|
||||||
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
|
let signed_transaction = {
|
||||||
|
let t = prepare_transaction(client, miner, request);
|
||||||
|
let hash = t.hash();
|
||||||
|
let signature = try!(account_provider.sign(address, hash).map_err(errors::from_signing_error));
|
||||||
|
t.with_signature(signature)
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
||||||
|
dispatch_transaction(&*client, &*miner, signed_transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
client
|
||||||
|
.gas_price_statistics(100, 8)
|
||||||
|
.map(|x| x[4])
|
||||||
|
.unwrap_or_else(|_| miner.sensible_gas_price())
|
||||||
|
}
|
||||||
188
rpc/src/v1/helpers/errors.rs
Normal file
188
rpc/src/v1/helpers/errors.rs
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! RPC Error codes and error objects
|
||||||
|
|
||||||
|
macro_rules! rpc_unimplemented {
|
||||||
|
() => (Err(::v1::helpers::errors::unimplemented()))
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use ethcore::error::Error as EthcoreError;
|
||||||
|
use ethcore::account_provider::{Error as AccountError};
|
||||||
|
use jsonrpc_core::{Error, ErrorCode, Value};
|
||||||
|
|
||||||
|
mod codes {
|
||||||
|
// NOTE [ToDr] Codes from [-32099, -32000]
|
||||||
|
pub const UNSUPPORTED_REQUEST: i64 = -32000;
|
||||||
|
pub const NO_WORK: i64 = -32001;
|
||||||
|
pub const NO_AUTHOR: i64 = -32002;
|
||||||
|
pub const UNKNOWN_ERROR: i64 = -32009;
|
||||||
|
pub const TRANSACTION_ERROR: i64 = -32010;
|
||||||
|
pub const ACCOUNT_LOCKED: i64 = -32020;
|
||||||
|
pub const PASSWORD_INVALID: i64 = -32021;
|
||||||
|
pub const ACCOUNT_ERROR: i64 = -32023;
|
||||||
|
pub const SIGNER_DISABLED: i64 = -32030;
|
||||||
|
pub const REQUEST_REJECTED: i64 = -32040;
|
||||||
|
pub const REQUEST_NOT_FOUND: i64 = -32041;
|
||||||
|
pub const COMPILATION_ERROR: i64 = -32050;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unimplemented() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
||||||
|
message: "This request is not implemented yet. Please create an issue on Github repo.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_not_found() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND),
|
||||||
|
message: "Request not found.".into(),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_rejected() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::REQUEST_REJECTED),
|
||||||
|
message: "Request has been rejected.".into(),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn account<T: fmt::Debug>(error: &str, details: T) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::ACCOUNT_ERROR),
|
||||||
|
message: error.into(),
|
||||||
|
data: Some(Value::String(format!("{:?}", details))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compilation<T: fmt::Debug>(error: T) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::COMPILATION_ERROR),
|
||||||
|
message: "Error while compiling code.".into(),
|
||||||
|
data: Some(Value::String(format!("{:?}", error))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn internal<T: fmt::Debug>(error: &str, data: T) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::InternalError,
|
||||||
|
message: format!("Internal error occurred: {}", error),
|
||||||
|
data: Some(Value::String(format!("{:?}", data))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_params<T: fmt::Debug>(param: &str, details: T) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::InvalidParams,
|
||||||
|
message: format!("Couldn't parse parameters: {}", param),
|
||||||
|
data: Some(Value::String(format!("{:?}", details))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state_pruned() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
||||||
|
message: "This request is not supported because your node is running with state pruning. Run with --pruning=archive.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_work() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::NO_WORK),
|
||||||
|
message: "Still syncing.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_author() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::NO_AUTHOR),
|
||||||
|
message: "Author not configured. Run Parity with --author to configure.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn signer_disabled() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::SIGNER_DISABLED),
|
||||||
|
message: "Trusted Signer is disabled. This API is not available.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_signing_error(error: AccountError) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::ACCOUNT_LOCKED),
|
||||||
|
message: "Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.".into(),
|
||||||
|
data: Some(Value::String(format!("{:?}", error))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_password_error(error: AccountError) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::PASSWORD_INVALID),
|
||||||
|
message: "Account password is invalid or account does not exist.".into(),
|
||||||
|
data: Some(Value::String(format!("{:?}", error))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_transaction_error(error: EthcoreError) -> Error {
|
||||||
|
use ethcore::error::TransactionError::*;
|
||||||
|
|
||||||
|
if let EthcoreError::Transaction(e) = error {
|
||||||
|
let msg = match e {
|
||||||
|
AlreadyImported => "Transaction with the same hash was already imported.".into(),
|
||||||
|
Old => "Transaction nonce is too low. Try incrementing the nonce.".into(),
|
||||||
|
TooCheapToReplace => {
|
||||||
|
"Transaction fee is too low. There is another transaction with same nonce in the queue. Try increasing the fee or incrementing the nonce.".into()
|
||||||
|
},
|
||||||
|
LimitReached => {
|
||||||
|
"There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
|
||||||
|
},
|
||||||
|
InsufficientGasPrice { minimal, got } => {
|
||||||
|
format!("Transaction fee is too low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got)
|
||||||
|
},
|
||||||
|
InsufficientBalance { balance, cost } => {
|
||||||
|
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)
|
||||||
|
},
|
||||||
|
GasLimitExceeded { limit, got } => {
|
||||||
|
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
|
||||||
|
},
|
||||||
|
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
|
||||||
|
};
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::TRANSACTION_ERROR),
|
||||||
|
message: msg,
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::UNKNOWN_ERROR),
|
||||||
|
message: "Unknown error when sending transaction.".into(),
|
||||||
|
data: Some(Value::String(format!("{:?}", error))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -14,6 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod errors;
|
||||||
|
pub mod dispatch;
|
||||||
|
pub mod params;
|
||||||
mod poll_manager;
|
mod poll_manager;
|
||||||
mod poll_filter;
|
mod poll_filter;
|
||||||
mod requests;
|
mod requests;
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ pub struct NetworkSettings {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
/// Name of the chain we are connected to
|
/// Name of the chain we are connected to
|
||||||
pub chain: String,
|
pub chain: String,
|
||||||
/// Min number of peers
|
|
||||||
pub min_peers: u32,
|
|
||||||
/// Max number of peers
|
|
||||||
pub max_peers: u32,
|
|
||||||
/// Networking port
|
/// Networking port
|
||||||
pub network_port: u16,
|
pub network_port: u16,
|
||||||
/// Is JSON-RPC server enabled?
|
/// Is JSON-RPC server enabled?
|
||||||
@@ -41,8 +37,6 @@ impl Default for NetworkSettings {
|
|||||||
NetworkSettings {
|
NetworkSettings {
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
chain: "homestead".into(),
|
chain: "homestead".into(),
|
||||||
min_peers: 25,
|
|
||||||
max_peers: 50,
|
|
||||||
network_port: 30303,
|
network_port: 30303,
|
||||||
rpc_enabled: true,
|
rpc_enabled: true,
|
||||||
rpc_interface: "local".into(),
|
rpc_interface: "local".into(),
|
||||||
|
|||||||
53
rpc/src/v1/helpers/params.rs
Normal file
53
rpc/src/v1/helpers/params.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Parameters parsing helpers
|
||||||
|
|
||||||
|
use serde;
|
||||||
|
use jsonrpc_core::{Error, Params, from_params};
|
||||||
|
use v1::types::BlockNumber;
|
||||||
|
use v1::helpers::errors;
|
||||||
|
|
||||||
|
pub fn expect_no_params(params: Params) -> Result<(), Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => Ok(()),
|
||||||
|
p => Err(errors::invalid_params("No parameters were expected", p)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn params_len(params: &Params) -> usize {
|
||||||
|
match params {
|
||||||
|
&Params::Array(ref vec) => vec.len(),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize request parameters with optional second parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
|
||||||
|
pub fn from_params_default_second<F>(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize {
|
||||||
|
match params_len(¶ms) {
|
||||||
|
1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)),
|
||||||
|
_ => from_params::<(F, BlockNumber)>(params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize request parameters with optional third parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
|
||||||
|
pub fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize {
|
||||||
|
match params_len(¶ms) {
|
||||||
|
2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)),
|
||||||
|
_ => from_params::<(F1, F2, BlockNumber)>(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,6 @@ use std::process::{Command, Stdio};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::ops::Deref;
|
|
||||||
use ethsync::{SyncProvider, SyncState};
|
use ethsync::{SyncProvider, SyncState};
|
||||||
use ethcore::miner::{MinerService, ExternalMinerService};
|
use ethcore::miner::{MinerService, ExternalMinerService};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
@@ -43,8 +42,9 @@ use ethcore::filter::Filter as EthcoreFilter;
|
|||||||
use self::ethash::SeedHashCompute;
|
use self::ethash::SeedHashCompute;
|
||||||
use v1::traits::Eth;
|
use v1::traits::Eth;
|
||||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256};
|
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256};
|
||||||
use v1::helpers::CallRequest as CRequest;
|
use v1::helpers::{CallRequest as CRequest, errors};
|
||||||
use v1::impls::{default_gas_price, dispatch_transaction, error_codes, from_params_default_second, from_params_default_third};
|
use v1::helpers::dispatch::{default_gas_price, dispatch_transaction};
|
||||||
|
use v1::helpers::params::{expect_no_params, from_params_default_second, from_params_default_third};
|
||||||
|
|
||||||
/// Eth RPC options
|
/// Eth RPC options
|
||||||
pub struct EthClientOptions {
|
pub struct EthClientOptions {
|
||||||
@@ -107,7 +107,7 @@ impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
|||||||
let view = block_view.header_view();
|
let view = block_view.header_view();
|
||||||
let block = Block {
|
let block = Block {
|
||||||
hash: Some(view.sha3().into()),
|
hash: Some(view.sha3().into()),
|
||||||
size: Some(bytes.len()),
|
size: Some(bytes.len().into()),
|
||||||
parent_hash: view.parent_hash().into(),
|
parent_hash: view.parent_hash().into(),
|
||||||
uncles_hash: view.uncles_hash().into(),
|
uncles_hash: view.uncles_hash().into(),
|
||||||
author: view.author().into(),
|
author: view.author().into(),
|
||||||
@@ -214,30 +214,6 @@ pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: M
|
|||||||
|
|
||||||
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
|
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
|
||||||
|
|
||||||
fn make_unsupported_err() -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::UNSUPPORTED_REQUEST_CODE),
|
|
||||||
message: "Unsupported request.".into(),
|
|
||||||
data: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn no_work_err() -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::NO_WORK_CODE),
|
|
||||||
message: "Still syncing.".into(),
|
|
||||||
data: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn no_author_err() -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::NO_AUTHOR_CODE),
|
|
||||||
message: "Author not configured. Run parity with --author to configure.".into(),
|
|
||||||
data: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
||||||
C: MiningBlockChainClient + 'static,
|
C: MiningBlockChainClient + 'static,
|
||||||
S: SyncProvider + 'static,
|
S: SyncProvider + 'static,
|
||||||
@@ -265,94 +241,80 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
|
|
||||||
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())),
|
|
||||||
_ => Err(Error::invalid_params())
|
Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syncing(&self, params: Params) -> Result<Value, Error> {
|
fn syncing(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let status = take_weak!(self.sync).status();
|
|
||||||
let res = match status.state {
|
|
||||||
SyncState::Idle => SyncStatus::None,
|
|
||||||
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => {
|
|
||||||
let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number);
|
|
||||||
let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number));
|
|
||||||
|
|
||||||
if highest_block > current_block + U256::from(6) {
|
let status = take_weak!(self.sync).status();
|
||||||
let info = SyncInfo {
|
let res = match status.state {
|
||||||
starting_block: status.start_block_number.into(),
|
SyncState::Idle => SyncStatus::None,
|
||||||
current_block: current_block.into(),
|
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => {
|
||||||
highest_block: highest_block.into(),
|
let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number);
|
||||||
};
|
let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number));
|
||||||
SyncStatus::Info(info)
|
|
||||||
} else {
|
if highest_block > current_block + U256::from(6) {
|
||||||
SyncStatus::None
|
let info = SyncInfo {
|
||||||
}
|
starting_block: status.start_block_number.into(),
|
||||||
}
|
current_block: current_block.into(),
|
||||||
};
|
highest_block: highest_block.into(),
|
||||||
to_value(&res)
|
};
|
||||||
|
SyncStatus::Info(info)
|
||||||
|
} else {
|
||||||
|
SyncStatus::None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => Err(Error::invalid_params()),
|
};
|
||||||
}
|
to_value(&res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn author(&self, params: Params) -> Result<Value, Error> {
|
fn author(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => to_value(&RpcH160::from(take_weak!(self.miner).author())),
|
|
||||||
_ => Err(Error::invalid_params()),
|
to_value(&RpcH160::from(take_weak!(self.miner).author()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => to_value(&(take_weak!(self.miner).is_sealing())),
|
|
||||||
_ => Err(Error::invalid_params())
|
to_value(&(take_weak!(self.miner).is_sealing()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hashrate(&self, params: Params) -> Result<Value, Error> {
|
fn hashrate(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => to_value(&RpcU256::from(self.external_miner.hashrate())),
|
|
||||||
_ => Err(Error::invalid_params())
|
to_value(&RpcU256::from(self.external_miner.hashrate()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gas_price(&self, params: Params) -> Result<Value, Error> {
|
fn gas_price(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
||||||
to_value(&RpcU256::from(default_gas_price(&*client, &*miner)))
|
to_value(&RpcU256::from(default_gas_price(&*client, &*miner)))
|
||||||
}
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accounts(&self, params: Params) -> Result<Value, Error> {
|
fn accounts(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
let accounts = try!(store.accounts().map_err(|_| Error::internal_error()));
|
let accounts = try!(store.accounts().map_err(|e| errors::internal("Could not fetch accounts.", e)));
|
||||||
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
|
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
|
||||||
},
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_number(&self, params: Params) -> Result<Value, Error> {
|
fn block_number(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number)),
|
|
||||||
_ => Err(Error::invalid_params())
|
to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, params: Params) -> Result<Value, Error> {
|
fn balance(&self, params: Params) -> Result<Value, Error> {
|
||||||
@@ -361,8 +323,11 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
.and_then(|(address, block_number,)| {
|
.and_then(|(address, block_number,)| {
|
||||||
let address: Address = RpcH160::into(address);
|
let address: Address = RpcH160::into(address);
|
||||||
match block_number {
|
match block_number {
|
||||||
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address))),
|
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).balance(&*take_weak!(self.client), &address))),
|
||||||
id => to_value(&RpcU256::from(try!(take_weak!(self.client).balance(&address, id.into()).ok_or_else(make_unsupported_err)))),
|
id => match take_weak!(self.client).balance(&address, id.into()) {
|
||||||
|
Some(balance) => to_value(&RpcU256::from(balance)),
|
||||||
|
None => Err(errors::state_pruned()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -377,7 +342,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))),
|
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))),
|
||||||
id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) {
|
id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) {
|
||||||
Some(s) => to_value(&RpcH256::from(s)),
|
Some(s) => to_value(&RpcH256::from(s)),
|
||||||
None => Err(make_unsupported_err()), // None is only returned on unsupported requests.
|
None => Err(errors::state_pruned()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -390,8 +355,11 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
.and_then(|(address, block_number,)| {
|
.and_then(|(address, block_number,)| {
|
||||||
let address: Address = RpcH160::into(address);
|
let address: Address = RpcH160::into(address);
|
||||||
match block_number {
|
match block_number {
|
||||||
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address))),
|
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).nonce(&*take_weak!(self.client), &address))),
|
||||||
id => to_value(&take_weak!(self.client).nonce(&address, id.into()).map(RpcU256::from)),
|
id => match take_weak!(self.client).nonce(&address, id.into()) {
|
||||||
|
Some(nonce) => to_value(&RpcU256::from(nonce)),
|
||||||
|
None => Err(errors::state_pruned()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -440,9 +408,11 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
.and_then(|(address, block_number,)| {
|
.and_then(|(address, block_number,)| {
|
||||||
let address: Address = RpcH160::into(address);
|
let address: Address = RpcH160::into(address);
|
||||||
match block_number {
|
match block_number {
|
||||||
BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)),
|
BlockNumber::Pending => to_value(&take_weak!(self.miner).code(&*take_weak!(self.client), &address).map_or_else(Bytes::default, Bytes::new)),
|
||||||
BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)),
|
_ => match take_weak!(self.client).code(&address, block_number.into()) {
|
||||||
_ => Err(Error::invalid_params()),
|
Some(code) => to_value(&code.map_or_else(Bytes::default, Bytes::new)),
|
||||||
|
None => Err(errors::state_pruned()),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -463,11 +433,14 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
try!(self.active());
|
try!(self.active());
|
||||||
from_params::<(RpcH256,)>(params)
|
from_params::<(RpcH256,)>(params)
|
||||||
.and_then(|(hash,)| {
|
.and_then(|(hash,)| {
|
||||||
let miner = take_weak!(self.miner);
|
|
||||||
let hash: H256 = hash.into();
|
let hash: H256 = hash.into();
|
||||||
match miner.transaction(&hash) {
|
let miner = take_weak!(self.miner);
|
||||||
Some(pending_tx) => to_value(&Transaction::from(pending_tx)),
|
let client = take_weak!(self.client);
|
||||||
None => self.transaction(TransactionID::Hash(hash))
|
let maybe_tx = client.transaction(TransactionID::Hash(hash)).map(Transaction::from)
|
||||||
|
.or_else(|| miner.transaction(&hash).map(Transaction::from));
|
||||||
|
match maybe_tx {
|
||||||
|
Some(t) => to_value(&t),
|
||||||
|
None => Ok(Value::Null),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -490,8 +463,8 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
.and_then(|(hash,)| {
|
.and_then(|(hash,)| {
|
||||||
let miner = take_weak!(self.miner);
|
let miner = take_weak!(self.miner);
|
||||||
let hash: H256 = hash.into();
|
let hash: H256 = hash.into();
|
||||||
match miner.pending_receipts().get(&hash) {
|
match (miner.pending_receipt(&hash), self.options.allow_pending_receipt_query) {
|
||||||
Some(receipt) if self.options.allow_pending_receipt_query => to_value(&Receipt::from(receipt.clone())),
|
(Some(receipt), true) => to_value(&Receipt::from(receipt)),
|
||||||
_ => {
|
_ => {
|
||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
let receipt = client.transaction_receipt(TransactionID::Hash(hash));
|
let receipt = client.transaction_receipt(TransactionID::Hash(hash));
|
||||||
@@ -515,16 +488,13 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
|
|
||||||
fn compilers(&self, params: Params) -> Result<Value, Error> {
|
fn compilers(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let mut compilers = vec![];
|
let mut compilers = vec![];
|
||||||
if Command::new(SOLC).output().is_ok() {
|
if Command::new(SOLC).output().is_ok() {
|
||||||
compilers.push("solidity".to_owned())
|
compilers.push("solidity".to_owned())
|
||||||
}
|
|
||||||
to_value(&compilers)
|
|
||||||
}
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
}
|
||||||
|
to_value(&compilers)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logs(&self, params: Params) -> Result<Value, Error> {
|
fn logs(&self, params: Params) -> Result<Value, Error> {
|
||||||
@@ -539,7 +509,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
.collect::<Vec<Log>>();
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
if include_pending {
|
if include_pending {
|
||||||
let pending = pending_logs(take_weak!(self.miner).deref(), &filter);
|
let pending = pending_logs(&*take_weak!(self.miner), &filter);
|
||||||
logs.extend(pending);
|
logs.extend(pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,45 +519,42 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
|
|
||||||
fn work(&self, params: Params) -> Result<Value, Error> {
|
fn work(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let client = take_weak!(self.client);
|
|
||||||
// check if we're still syncing and return empty strings in that case
|
|
||||||
{
|
|
||||||
//TODO: check if initial sync is complete here
|
|
||||||
//let sync = take_weak!(self.sync);
|
|
||||||
if /*sync.status().state != SyncState::Idle ||*/ client.queue_info().total_queue_size() > MAX_QUEUE_SIZE_TO_MINE_ON {
|
|
||||||
trace!(target: "miner", "Syncing. Cannot give any work.");
|
|
||||||
return Err(no_work_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise spin until our submitted block has been included.
|
let client = take_weak!(self.client);
|
||||||
let timeout = Instant::now() + Duration::from_millis(1000);
|
// check if we're still syncing and return empty strings in that case
|
||||||
while Instant::now() < timeout && client.queue_info().total_queue_size() > 0 {
|
{
|
||||||
thread::sleep(Duration::from_millis(1));
|
//TODO: check if initial sync is complete here
|
||||||
}
|
//let sync = take_weak!(self.sync);
|
||||||
}
|
if /*sync.status().state != SyncState::Idle ||*/ client.queue_info().total_queue_size() > MAX_QUEUE_SIZE_TO_MINE_ON {
|
||||||
|
trace!(target: "miner", "Syncing. Cannot give any work.");
|
||||||
|
return Err(errors::no_work());
|
||||||
|
}
|
||||||
|
|
||||||
let miner = take_weak!(self.miner);
|
// Otherwise spin until our submitted block has been included.
|
||||||
if miner.author().is_zero() {
|
let timeout = Instant::now() + Duration::from_millis(1000);
|
||||||
warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!");
|
while Instant::now() < timeout && client.queue_info().total_queue_size() > 0 {
|
||||||
return Err(no_author_err())
|
thread::sleep(Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
miner.map_sealing_work(client.deref(), |b| {
|
|
||||||
let pow_hash = b.hash();
|
|
||||||
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
|
||||||
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
|
|
||||||
|
|
||||||
if self.options.send_block_number_in_get_work {
|
|
||||||
let block_number = RpcU256::from(b.block().header().number());
|
|
||||||
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
|
|
||||||
} else {
|
|
||||||
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target)))
|
|
||||||
}
|
|
||||||
}).unwrap_or(Err(Error::internal_error())) // no work found.
|
|
||||||
},
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let miner = take_weak!(self.miner);
|
||||||
|
if miner.author().is_zero() {
|
||||||
|
warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!");
|
||||||
|
return Err(errors::no_author())
|
||||||
|
}
|
||||||
|
miner.map_sealing_work(&*client, |b| {
|
||||||
|
let pow_hash = b.hash();
|
||||||
|
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
||||||
|
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
|
||||||
|
|
||||||
|
if self.options.send_block_number_in_get_work {
|
||||||
|
let block_number = RpcU256::from(b.block().header().number());
|
||||||
|
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
|
||||||
|
} else {
|
||||||
|
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target)))
|
||||||
|
}
|
||||||
|
}).unwrap_or(Err(Error::internal_error())) // no work found.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_work(&self, params: Params) -> Result<Value, Error> {
|
fn submit_work(&self, params: Params) -> Result<Value, Error> {
|
||||||
@@ -600,7 +567,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
let miner = take_weak!(self.miner);
|
let miner = take_weak!(self.miner);
|
||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
|
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
|
||||||
let r = miner.submit_seal(client.deref(), pow_hash, seal);
|
let r = miner.submit_seal(&*client, pow_hash, seal);
|
||||||
to_value(&r.is_ok())
|
to_value(&r.is_ok())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -627,13 +594,12 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
|
|
||||||
fn call(&self, params: Params) -> Result<Value, Error> {
|
fn call(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
trace!(target: "jsonrpc", "call: {:?}", params);
|
|
||||||
from_params_default_second(params)
|
from_params_default_second(params)
|
||||||
.and_then(|(request, block_number,)| {
|
.and_then(|(request, block_number,)| {
|
||||||
let request = CallRequest::into(request);
|
let request = CallRequest::into(request);
|
||||||
let signed = try!(self.sign_call(request));
|
let signed = try!(self.sign_call(request));
|
||||||
let r = match block_number {
|
let r = match block_number {
|
||||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()),
|
BlockNumber::Pending => take_weak!(self.miner).call(&*take_weak!(self.client), &signed, Default::default()),
|
||||||
block_number => take_weak!(self.client).call(&signed, block_number.into(), Default::default()),
|
block_number => take_weak!(self.client).call(&signed, block_number.into(), Default::default()),
|
||||||
};
|
};
|
||||||
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
|
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
|
||||||
@@ -647,7 +613,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
let request = CallRequest::into(request);
|
let request = CallRequest::into(request);
|
||||||
let signed = try!(self.sign_call(request));
|
let signed = try!(self.sign_call(request));
|
||||||
let r = match block_number {
|
let r = match block_number {
|
||||||
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()),
|
BlockNumber::Pending => take_weak!(self.miner).call(&*take_weak!(self.client), &signed, Default::default()),
|
||||||
block => take_weak!(self.client).call(&signed, block.into(), Default::default()),
|
block => take_weak!(self.client).call(&signed, block.into(), Default::default()),
|
||||||
};
|
};
|
||||||
to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))))
|
to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))))
|
||||||
@@ -675,17 +641,23 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.spawn();
|
.spawn();
|
||||||
if let Ok(mut child) = maybe_child {
|
|
||||||
if let Ok(_) = child.stdin.as_mut().expect("we called child.stdin(Stdio::piped()) before spawn; qed").write_all(code.as_bytes()) {
|
maybe_child
|
||||||
if let Ok(output) = child.wait_with_output() {
|
.map_err(errors::compilation)
|
||||||
let s = String::from_utf8_lossy(&output.stdout);
|
.and_then(|mut child| {
|
||||||
if let Some(hex) = s.lines().skip_while(|ref l| !l.contains("Binary")).skip(1).next() {
|
try!(child.stdin.as_mut()
|
||||||
return to_value(&Bytes::new(hex.from_hex().unwrap_or(vec![])));
|
.expect("we called child.stdin(Stdio::piped()) before spawn; qed")
|
||||||
}
|
.write_all(code.as_bytes())
|
||||||
|
.map_err(errors::compilation));
|
||||||
|
let output = try!(child.wait_with_output().map_err(errors::compilation));
|
||||||
|
|
||||||
|
let s = String::from_utf8_lossy(&output.stdout);
|
||||||
|
if let Some(hex) = s.lines().skip_while(|ref l| !l.contains("Binary")).skip(1).next() {
|
||||||
|
to_value(&Bytes::new(hex.from_hex().unwrap_or(vec![])))
|
||||||
|
} else {
|
||||||
|
Err(errors::compilation("Unexpected output."))
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
|
||||||
Err(Error::invalid_params())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
//! Eth Filter RPC implementation
|
//! Eth Filter RPC implementation
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
@@ -27,6 +26,7 @@ use util::Mutex;
|
|||||||
use v1::traits::EthFilter;
|
use v1::traits::EthFilter;
|
||||||
use v1::types::{BlockNumber, Index, Filter, Log, H256 as RpcH256, U256 as RpcU256};
|
use v1::types::{BlockNumber, Index, Filter, Log, H256 as RpcH256, U256 as RpcU256};
|
||||||
use v1::helpers::{PollFilter, PollManager};
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
|
use v1::helpers::params::expect_no_params;
|
||||||
use v1::impls::eth::pending_logs;
|
use v1::impls::eth::pending_logs;
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
/// Eth filter rpc implementation.
|
||||||
@@ -76,28 +76,22 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
|||||||
|
|
||||||
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let mut polls = self.polls.lock();
|
let mut polls = self.polls.lock();
|
||||||
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
||||||
to_value(&RpcU256::from(id))
|
to_value(&RpcU256::from(id))
|
||||||
},
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let mut polls = self.polls.lock();
|
|
||||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
|
||||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
|
||||||
|
|
||||||
to_value(&RpcU256::from(id))
|
let mut polls = self.polls.lock();
|
||||||
},
|
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||||
_ => Err(Error::invalid_params())
|
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||||
}
|
|
||||||
|
to_value(&RpcU256::from(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
fn filter_changes(&self, params: Params) -> Result<Value, Error> {
|
||||||
@@ -165,7 +159,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
|||||||
|
|
||||||
// additionally retrieve pending logs
|
// additionally retrieve pending logs
|
||||||
if include_pending {
|
if include_pending {
|
||||||
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
let pending_logs = pending_logs(&*take_weak!(self.miner), &filter);
|
||||||
|
|
||||||
// remove logs about which client was already notified about
|
// remove logs about which client was already notified about
|
||||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||||
@@ -206,7 +200,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
|||||||
.collect::<Vec<Log>>();
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
if include_pending {
|
if include_pending {
|
||||||
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
logs.extend(pending_logs(&*take_weak!(self.miner), &filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
to_value(&logs)
|
to_value(&logs)
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ use ethcore::client::MiningBlockChainClient;
|
|||||||
use util::{U256, Address, H256, Mutex};
|
use util::{U256, Address, H256, Mutex};
|
||||||
use transient_hashmap::TransientHashMap;
|
use transient_hashmap::TransientHashMap;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest};
|
use v1::helpers::{errors, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest};
|
||||||
|
use v1::helpers::dispatch::{default_gas_price, sign_and_dispatch};
|
||||||
use v1::traits::EthSigning;
|
use v1::traits::EthSigning;
|
||||||
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256};
|
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256};
|
||||||
use v1::impls::{default_gas_price, sign_and_dispatch, request_rejected_error, request_not_found_error, signer_disabled_error};
|
|
||||||
|
|
||||||
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
|
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
@@ -151,10 +151,10 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
|
|||||||
let res = match pending.get(&id) {
|
let res = match pending.get(&id) {
|
||||||
Some(ref promise) => match promise.result() {
|
Some(ref promise) => match promise.result() {
|
||||||
ConfirmationResult::Waiting => { return Ok(Value::Null); }
|
ConfirmationResult::Waiting => { return Ok(Value::Null); }
|
||||||
ConfirmationResult::Rejected => Err(request_rejected_error()),
|
ConfirmationResult::Rejected => Err(errors::request_rejected()),
|
||||||
ConfirmationResult::Confirmed(rpc_response) => rpc_response,
|
ConfirmationResult::Confirmed(rpc_response) => rpc_response,
|
||||||
},
|
},
|
||||||
_ => { return Err(request_not_found_error()); }
|
_ => { return Err(errors::request_not_found()); }
|
||||||
};
|
};
|
||||||
pending.remove(&id);
|
pending.remove(&id);
|
||||||
res
|
res
|
||||||
@@ -217,16 +217,16 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
|
|||||||
|
|
||||||
fn post_sign(&self, _: Params) -> Result<Value, Error> {
|
fn post_sign(&self, _: Params) -> Result<Value, Error> {
|
||||||
// We don't support this in non-signer mode.
|
// We don't support this in non-signer mode.
|
||||||
Err(signer_disabled_error())
|
Err(errors::signer_disabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
|
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
|
||||||
// We don't support this in non-signer mode.
|
// We don't support this in non-signer mode.
|
||||||
Err(signer_disabled_error())
|
Err(errors::signer_disabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_request(&self, _: Params) -> Result<Value, Error> {
|
fn check_request(&self, _: Params) -> Result<Value, Error> {
|
||||||
// We don't support this in non-signer mode.
|
// We don't support this in non-signer mode.
|
||||||
Err(signer_disabled_error())
|
Err(errors::signer_disabled())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,37 +15,52 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Ethcore-specific rpc implementation.
|
//! Ethcore-specific rpc implementation.
|
||||||
use util::{RotatingLogger};
|
|
||||||
use util::misc::version_data;
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::ops::Deref;
|
use std::str::FromStr;
|
||||||
use std::collections::{BTreeMap};
|
use std::collections::{BTreeMap};
|
||||||
use ethcore::client::{MiningBlockChainClient};
|
use util::{RotatingLogger, Address};
|
||||||
use jsonrpc_core::*;
|
use util::misc::version_data;
|
||||||
|
use ethsync::{SyncProvider, ManageNetwork};
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
|
use ethcore::client::{MiningBlockChainClient};
|
||||||
|
|
||||||
|
use jsonrpc_core::*;
|
||||||
use v1::traits::Ethcore;
|
use v1::traits::Ethcore;
|
||||||
use v1::types::{Bytes, U256};
|
use v1::types::{Bytes, U256, Peers, H160, Transaction};
|
||||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, NetworkSettings};
|
use v1::helpers::{errors, SigningQueue, ConfirmationsQueue, NetworkSettings};
|
||||||
use v1::impls::signer_disabled_error;
|
use v1::helpers::params::expect_no_params;
|
||||||
|
|
||||||
/// Ethcore implementation.
|
/// Ethcore implementation.
|
||||||
pub struct EthcoreClient<C, M> where
|
pub struct EthcoreClient<C, M, S: ?Sized> where
|
||||||
C: MiningBlockChainClient,
|
C: MiningBlockChainClient,
|
||||||
M: MinerService {
|
M: MinerService,
|
||||||
|
S: SyncProvider {
|
||||||
|
|
||||||
client: Weak<C>,
|
client: Weak<C>,
|
||||||
miner: Weak<M>,
|
miner: Weak<M>,
|
||||||
|
sync: Weak<S>,
|
||||||
|
net: Weak<ManageNetwork>,
|
||||||
logger: Arc<RotatingLogger>,
|
logger: Arc<RotatingLogger>,
|
||||||
settings: Arc<NetworkSettings>,
|
settings: Arc<NetworkSettings>,
|
||||||
confirmations_queue: Option<Arc<ConfirmationsQueue>>,
|
confirmations_queue: Option<Arc<ConfirmationsQueue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, M> EthcoreClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
impl<C, M, S: ?Sized> EthcoreClient<C, M, S> where C: MiningBlockChainClient, M: MinerService, S: SyncProvider {
|
||||||
/// Creates new `EthcoreClient`.
|
/// Creates new `EthcoreClient`.
|
||||||
pub fn new(client: &Arc<C>, miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>, queue: Option<Arc<ConfirmationsQueue>>) -> Self {
|
pub fn new(
|
||||||
|
client: &Arc<C>,
|
||||||
|
miner: &Arc<M>,
|
||||||
|
sync: &Arc<S>,
|
||||||
|
net: &Arc<ManageNetwork>,
|
||||||
|
logger: Arc<RotatingLogger>,
|
||||||
|
settings: Arc<NetworkSettings>,
|
||||||
|
queue: Option<Arc<ConfirmationsQueue>>
|
||||||
|
) -> Self {
|
||||||
EthcoreClient {
|
EthcoreClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
miner: Arc::downgrade(miner),
|
miner: Arc::downgrade(miner),
|
||||||
|
sync: Arc::downgrade(sync),
|
||||||
|
net: Arc::downgrade(net),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
confirmations_queue: queue,
|
confirmations_queue: queue,
|
||||||
@@ -59,66 +74,97 @@ impl<C, M> EthcoreClient<C, M> where C: MiningBlockChainClient, M: MinerService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: MiningBlockChainClient + 'static {
|
impl<C, M, S: ?Sized> Ethcore for EthcoreClient<C, M, S> where M: MinerService + 'static, C: MiningBlockChainClient + 'static, S: SyncProvider + 'static {
|
||||||
|
|
||||||
fn transactions_limit(&self, _: Params) -> Result<Value, Error> {
|
fn transactions_limit(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&take_weak!(self.miner).transactions_limit())
|
to_value(&take_weak!(self.miner).transactions_limit())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min_gas_price(&self, _: Params) -> Result<Value, Error> {
|
fn min_gas_price(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&U256::from(take_weak!(self.miner).minimal_gas_price()))
|
to_value(&U256::from(take_weak!(self.miner).minimal_gas_price()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_data(&self, _: Params) -> Result<Value, Error> {
|
fn extra_data(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&Bytes::new(take_weak!(self.miner).extra_data()))
|
to_value(&Bytes::new(take_weak!(self.miner).extra_data()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gas_floor_target(&self, _: Params) -> Result<Value, Error> {
|
fn gas_floor_target(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&U256::from(take_weak!(self.miner).gas_floor_target()))
|
to_value(&U256::from(take_weak!(self.miner).gas_floor_target()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gas_ceil_target(&self, _: Params) -> Result<Value, Error> {
|
fn gas_ceil_target(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&U256::from(take_weak!(self.miner).gas_ceil_target()))
|
to_value(&U256::from(take_weak!(self.miner).gas_ceil_target()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dev_logs(&self, _params: Params) -> Result<Value, Error> {
|
fn dev_logs(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
let logs = self.logger.logs();
|
let logs = self.logger.logs();
|
||||||
to_value(&logs.deref().as_slice())
|
to_value(&logs.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dev_logs_levels(&self, _params: Params) -> Result<Value, Error> {
|
fn dev_logs_levels(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&self.logger.levels())
|
to_value(&self.logger.levels())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn net_chain(&self, _params: Params) -> Result<Value, Error> {
|
fn net_chain(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&self.settings.chain)
|
to_value(&self.settings.chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn net_max_peers(&self, _params: Params) -> Result<Value, Error> {
|
fn net_peers(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
to_value(&self.settings.max_peers)
|
try!(expect_no_params(params));
|
||||||
|
|
||||||
|
let sync_status = take_weak!(self.sync).status();
|
||||||
|
let net_config = take_weak!(self.net).network_config();
|
||||||
|
|
||||||
|
to_value(&Peers {
|
||||||
|
active: sync_status.num_active_peers,
|
||||||
|
connected: sync_status.num_peers,
|
||||||
|
max: sync_status.current_max_peers(net_config.min_peers, net_config.max_peers),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn net_port(&self, _params: Params) -> Result<Value, Error> {
|
fn net_port(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&self.settings.network_port)
|
to_value(&self.settings.network_port)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_name(&self, _params: Params) -> Result<Value, Error> {
|
fn node_name(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
to_value(&self.settings.name)
|
to_value(&self.settings.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rpc_settings(&self, _params: Params) -> Result<Value, Error> {
|
fn registry_address(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
|
let r = take_weak!(self.client)
|
||||||
|
.additional_params()
|
||||||
|
.get("registrar")
|
||||||
|
.and_then(|s| Address::from_str(s).ok())
|
||||||
|
.map(|s| H160::from(s));
|
||||||
|
to_value(&r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rpc_settings(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled));
|
map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled));
|
||||||
map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone()));
|
map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone()));
|
||||||
@@ -128,31 +174,37 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
|
|||||||
|
|
||||||
fn default_extra_data(&self, params: Params) -> Result<Value, Error> {
|
fn default_extra_data(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => to_value(&Bytes::new(version_data())),
|
to_value(&Bytes::new(version_data()))
|
||||||
_ => Err(Error::invalid_params()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gas_price_statistics(&self, params: Params) -> Result<Value, Error> {
|
fn gas_price_statistics(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) {
|
|
||||||
Ok(stats) => to_value(&stats
|
match take_weak!(self.client).gas_price_statistics(100, 8) {
|
||||||
.into_iter()
|
Ok(stats) => to_value(&stats
|
||||||
.map(|x| to_value(&U256::from(x)).expect("x must be U256; qed"))
|
.into_iter()
|
||||||
.collect::<Vec<_>>()),
|
.map(|x| to_value(&U256::from(x)).expect("x must be U256; qed"))
|
||||||
_ => Err(Error::internal_error()),
|
.collect::<Vec<_>>()),
|
||||||
},
|
_ => Err(Error::internal_error()),
|
||||||
_ => Err(Error::invalid_params()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsigned_transactions_count(&self, _params: Params) -> Result<Value, Error> {
|
fn unsigned_transactions_count(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
|
|
||||||
match self.confirmations_queue {
|
match self.confirmations_queue {
|
||||||
None => Err(signer_disabled_error()),
|
None => Err(errors::signer_disabled()),
|
||||||
Some(ref queue) => to_value(&queue.len()),
|
Some(ref queue) => to_value(&queue.len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pending_transactions(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
|
|
||||||
|
to_value(&take_weak!(self.miner).all_transactions().into_iter().map(Into::into).collect::<Vec<Transaction>>())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ use jsonrpc_core::*;
|
|||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
use ethsync::ManageNetwork;
|
use ethsync::ManageNetwork;
|
||||||
|
use v1::helpers::errors;
|
||||||
|
use v1::helpers::params::expect_no_params;
|
||||||
use v1::traits::EthcoreSet;
|
use v1::traits::EthcoreSet;
|
||||||
use v1::types::{Bytes, H160, U256};
|
use v1::types::{Bytes, H160, U256};
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ impl<C, M> EthcoreSet for EthcoreSetClient<C, M> where
|
|||||||
from_params::<(String,)>(params).and_then(|(peer,)| {
|
from_params::<(String,)>(params).and_then(|(peer,)| {
|
||||||
match take_weak!(self.net).add_reserved_peer(peer) {
|
match take_weak!(self.net).add_reserved_peer(peer) {
|
||||||
Ok(()) => to_value(&true),
|
Ok(()) => to_value(&true),
|
||||||
Err(_) => Err(Error::invalid_params()),
|
Err(e) => Err(errors::invalid_params("Peer address", e)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -127,29 +129,33 @@ impl<C, M> EthcoreSet for EthcoreSetClient<C, M> where
|
|||||||
from_params::<(String,)>(params).and_then(|(peer,)| {
|
from_params::<(String,)>(params).and_then(|(peer,)| {
|
||||||
match take_weak!(self.net).remove_reserved_peer(peer) {
|
match take_weak!(self.net).remove_reserved_peer(peer) {
|
||||||
Ok(()) => to_value(&true),
|
Ok(()) => to_value(&true),
|
||||||
Err(_) => Err(Error::invalid_params()),
|
Err(e) => Err(errors::invalid_params("Peer address", e)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_non_reserved_peers(&self, _: Params) -> Result<Value, Error> {
|
fn drop_non_reserved_peers(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
take_weak!(self.net).deny_unreserved_peers();
|
take_weak!(self.net).deny_unreserved_peers();
|
||||||
to_value(&true)
|
to_value(&true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accept_non_reserved_peers(&self, _: Params) -> Result<Value, Error> {
|
fn accept_non_reserved_peers(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
take_weak!(self.net).accept_unreserved_peers();
|
take_weak!(self.net).accept_unreserved_peers();
|
||||||
to_value(&true)
|
to_value(&true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_network(&self, _: Params) -> Result<Value, Error> {
|
fn start_network(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
take_weak!(self.net).start_network();
|
take_weak!(self.net).start_network();
|
||||||
Ok(Value::Bool(true))
|
Ok(Value::Bool(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_network(&self, _: Params) -> Result<Value, Error> {
|
fn stop_network(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
take_weak!(self.net).stop_network();
|
take_weak!(self.net).stop_network();
|
||||||
Ok(Value::Bool(true))
|
Ok(Value::Bool(true))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,21 +25,17 @@ macro_rules! take_weak {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! rpc_unimplemented {
|
|
||||||
() => (Err(Error::internal_error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
mod web3;
|
|
||||||
mod eth;
|
mod eth;
|
||||||
mod eth_filter;
|
mod eth_filter;
|
||||||
mod eth_signing;
|
mod eth_signing;
|
||||||
|
mod ethcore;
|
||||||
|
mod ethcore_set;
|
||||||
mod net;
|
mod net;
|
||||||
mod personal;
|
mod personal;
|
||||||
mod personal_signer;
|
mod personal_signer;
|
||||||
mod ethcore;
|
|
||||||
mod ethcore_set;
|
|
||||||
mod traces;
|
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
mod traces;
|
||||||
|
mod web3;
|
||||||
|
|
||||||
pub use self::web3::Web3Client;
|
pub use self::web3::Web3Client;
|
||||||
pub use self::eth::{EthClient, EthClientOptions};
|
pub use self::eth::{EthClient, EthClientOptions};
|
||||||
@@ -53,202 +49,3 @@ pub use self::ethcore_set::EthcoreSetClient;
|
|||||||
pub use self::traces::TracesClient;
|
pub use self::traces::TracesClient;
|
||||||
pub use self::rpc::RpcClient;
|
pub use self::rpc::RpcClient;
|
||||||
|
|
||||||
use serde;
|
|
||||||
use v1::helpers::TransactionRequest;
|
|
||||||
use v1::types::{H256 as RpcH256, H520 as RpcH520, BlockNumber};
|
|
||||||
use ethcore::error::Error as EthcoreError;
|
|
||||||
use ethcore::miner::MinerService;
|
|
||||||
use ethcore::client::MiningBlockChainClient;
|
|
||||||
use ethcore::transaction::{Action, SignedTransaction, Transaction};
|
|
||||||
use ethcore::account_provider::{AccountProvider, Error as AccountError};
|
|
||||||
use util::numbers::*;
|
|
||||||
use util::rlp::encode;
|
|
||||||
use util::bytes::ToPretty;
|
|
||||||
use jsonrpc_core::{Error, ErrorCode, Value, to_value, from_params, Params};
|
|
||||||
|
|
||||||
mod error_codes {
|
|
||||||
// NOTE [ToDr] Codes from [-32099, -32000]
|
|
||||||
pub const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
|
|
||||||
pub const NO_WORK_CODE: i64 = -32001;
|
|
||||||
pub const NO_AUTHOR_CODE: i64 = -32002;
|
|
||||||
pub const UNKNOWN_ERROR: i64 = -32009;
|
|
||||||
pub const TRANSACTION_ERROR: i64 = -32010;
|
|
||||||
pub const ACCOUNT_LOCKED: i64 = -32020;
|
|
||||||
pub const PASSWORD_INVALID: i64 = -32021;
|
|
||||||
pub const SIGNER_DISABLED: i64 = -32030;
|
|
||||||
pub const REQUEST_REJECTED: i64 = -32040;
|
|
||||||
pub const REQUEST_NOT_FOUND: i64 = -32041;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn params_len(params: &Params) -> usize {
|
|
||||||
match params {
|
|
||||||
&Params::Array(ref vec) => vec.len(),
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize request parameters with optional second parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
|
|
||||||
pub fn from_params_default_second<F>(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize {
|
|
||||||
match params_len(¶ms) {
|
|
||||||
1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)),
|
|
||||||
_ => from_params::<(F, BlockNumber)>(params),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize request parameters with optional third parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
|
|
||||||
pub fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize {
|
|
||||||
match params_len(¶ms) {
|
|
||||||
2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)),
|
|
||||||
_ => from_params::<(F1, F2, BlockNumber)>(params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
|
||||||
let hash = RpcH256::from(signed_transaction.hash());
|
|
||||||
|
|
||||||
let import = miner.import_own_transaction(client, signed_transaction);
|
|
||||||
|
|
||||||
import
|
|
||||||
.map_err(transaction_error)
|
|
||||||
.and_then(|_| to_value(&hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result<Value, Error> {
|
|
||||||
accounts.sign_with_password(address, pass, hash)
|
|
||||||
.map_err(password_error)
|
|
||||||
.and_then(|hash| to_value(&RpcH520::from(hash)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
|
||||||
Transaction {
|
|
||||||
nonce: request.nonce
|
|
||||||
.or_else(|| miner
|
|
||||||
.last_nonce(&request.from)
|
|
||||||
.map(|nonce| nonce + U256::one()))
|
|
||||||
.unwrap_or_else(|| client.latest_nonce(&request.from)),
|
|
||||||
|
|
||||||
action: request.to.map_or(Action::Create, Action::Call),
|
|
||||||
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
|
|
||||||
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
|
|
||||||
value: request.value.unwrap_or_else(U256::zero),
|
|
||||||
data: request.data.map_or_else(Vec::new, |b| b.to_vec()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
|
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
|
||||||
|
|
||||||
let address = request.from;
|
|
||||||
let signed_transaction = {
|
|
||||||
let t = prepare_transaction(client, miner, request);
|
|
||||||
let hash = t.hash();
|
|
||||||
let signature = try!(account_provider.sign_with_password(address, password, hash).map_err(password_error));
|
|
||||||
t.with_signature(signature)
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
|
||||||
dispatch_transaction(&*client, &*miner, signed_transaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result<Value, Error>
|
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
|
||||||
|
|
||||||
let signed_transaction = {
|
|
||||||
let t = prepare_transaction(client, miner, request);
|
|
||||||
let hash = t.hash();
|
|
||||||
let signature = try!(account_provider.sign(address, hash).map_err(signing_error));
|
|
||||||
t.with_signature(signature)
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
|
||||||
dispatch_transaction(&*client, &*miner, signed_transaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
|
|
||||||
client
|
|
||||||
.gas_price_statistics(100, 8)
|
|
||||||
.map(|x| x[4])
|
|
||||||
.unwrap_or_else(|_| miner.sensible_gas_price())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signer_disabled_error() -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),
|
|
||||||
message: "Trusted Signer is disabled. This API is not available.".into(),
|
|
||||||
data: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signing_error(error: AccountError) -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED),
|
|
||||||
message: "Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.".into(),
|
|
||||||
data: Some(Value::String(format!("{:?}", error))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn password_error(error: AccountError) -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::PASSWORD_INVALID),
|
|
||||||
message: "Account password is invalid or account does not exist.".into(),
|
|
||||||
data: Some(Value::String(format!("{:?}", error))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error returned when request is rejected (in Trusted Signer).
|
|
||||||
pub fn request_rejected_error() -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::REQUEST_REJECTED),
|
|
||||||
message: "Request has been rejected.".into(),
|
|
||||||
data: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error returned when request is not found in queue.
|
|
||||||
pub fn request_not_found_error() -> Error {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::REQUEST_NOT_FOUND),
|
|
||||||
message: "Request not found.".into(),
|
|
||||||
data: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transaction_error(error: EthcoreError) -> Error {
|
|
||||||
use ethcore::error::TransactionError::*;
|
|
||||||
|
|
||||||
if let EthcoreError::Transaction(e) = error {
|
|
||||||
let msg = match e {
|
|
||||||
AlreadyImported => "Transaction with the same hash was already imported.".into(),
|
|
||||||
Old => "Transaction nonce is too low. Try incrementing the nonce.".into(),
|
|
||||||
TooCheapToReplace => {
|
|
||||||
"Transaction fee is too low. There is another transaction with same nonce in the queue. Try increasing the fee or incrementing the nonce.".into()
|
|
||||||
},
|
|
||||||
LimitReached => {
|
|
||||||
"There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
|
|
||||||
},
|
|
||||||
InsufficientGasPrice { minimal, got } => {
|
|
||||||
format!("Transaction fee is too low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got)
|
|
||||||
},
|
|
||||||
InsufficientBalance { balance, cost } => {
|
|
||||||
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)
|
|
||||||
},
|
|
||||||
GasLimitExceeded { limit, got } => {
|
|
||||||
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
|
|
||||||
},
|
|
||||||
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
|
|
||||||
};
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::TRANSACTION_ERROR),
|
|
||||||
message: msg,
|
|
||||||
data: None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(error_codes::UNKNOWN_ERROR),
|
|
||||||
message: "Unknown error when sending transaction.".into(),
|
|
||||||
data: Some(Value::String(format!("{:?}", error))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use std::sync::{Arc, Weak};
|
|||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use ethsync::SyncProvider;
|
use ethsync::SyncProvider;
|
||||||
use v1::traits::Net;
|
use v1::traits::Net;
|
||||||
|
use v1::helpers::params::expect_no_params;
|
||||||
|
|
||||||
/// Net rpc implementation.
|
/// Net rpc implementation.
|
||||||
pub struct NetClient<S: ?Sized> where S: SyncProvider {
|
pub struct NetClient<S: ?Sized> where S: SyncProvider {
|
||||||
@@ -35,15 +36,18 @@ impl<S: ?Sized> NetClient<S> where S: SyncProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ?Sized> Net for NetClient<S> where S: SyncProvider + 'static {
|
impl<S: ?Sized> Net for NetClient<S> where S: SyncProvider + 'static {
|
||||||
fn version(&self, _: Params) -> Result<Value, Error> {
|
fn version(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
Ok(Value::String(format!("{}", take_weak!(self.sync).status().network_id).to_owned()))
|
Ok(Value::String(format!("{}", take_weak!(self.sync).status().network_id).to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peer_count(&self, _params: Params) -> Result<Value, Error> {
|
fn peer_count(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
Ok(Value::String(format!("0x{:x}", take_weak!(self.sync).status().num_peers as u64).to_owned()))
|
Ok(Value::String(format!("0x{:x}", take_weak!(self.sync).status().num_peers as u64).to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_listening(&self, _: Params) -> Result<Value, Error> {
|
fn is_listening(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
// right now (11 march 2016), we are always listening for incoming connections
|
// right now (11 march 2016), we are always listening for incoming connections
|
||||||
Ok(Value::Bool(true))
|
Ok(Value::Bool(true))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ use std::collections::{BTreeMap};
|
|||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use v1::traits::Personal;
|
use v1::traits::Personal;
|
||||||
use v1::types::{H160 as RpcH160, TransactionRequest};
|
use v1::types::{H160 as RpcH160, TransactionRequest};
|
||||||
use v1::impls::unlock_sign_and_dispatch;
|
use v1::helpers::{errors, TransactionRequest as TRequest};
|
||||||
use v1::helpers::{TransactionRequest as TRequest};
|
use v1::helpers::params::expect_no_params;
|
||||||
|
use v1::helpers::dispatch::unlock_sign_and_dispatch;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use util::Address;
|
use util::Address;
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
@@ -57,8 +58,10 @@ impl<C, M> PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService
|
|||||||
|
|
||||||
impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
fn signer_enabled(&self, _: Params) -> Result<Value, Error> {
|
fn signer_enabled(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
|
|
||||||
self.signer_port
|
self.signer_port
|
||||||
.map(|v| to_value(&v))
|
.map(|v| to_value(&v))
|
||||||
.unwrap_or_else(|| to_value(&false))
|
.unwrap_or_else(|| to_value(&false))
|
||||||
@@ -66,14 +69,11 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
|
|
||||||
fn accounts(&self, params: Params) -> Result<Value, Error> {
|
fn accounts(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => {
|
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
let accounts = try!(store.accounts().map_err(|_| Error::internal_error()));
|
let accounts = try!(store.accounts().map_err(|e| errors::internal("Could not fetch accounts.", e)));
|
||||||
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
|
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
|
||||||
},
|
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_account(&self, params: Params) -> Result<Value, Error> {
|
fn new_account(&self, params: Params) -> Result<Value, Error> {
|
||||||
@@ -83,7 +83,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
match store.new_account(&pass) {
|
match store.new_account(&pass) {
|
||||||
Ok(address) => to_value(&RpcH160::from(address)),
|
Ok(address) => to_value(&RpcH160::from(address)),
|
||||||
Err(_) => Err(Error::internal_error())
|
Err(e) => Err(errors::account("Could not create account.", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -124,7 +124,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
from_params::<(RpcH160, _)>(params).and_then(|(addr, name)| {
|
from_params::<(RpcH160, _)>(params).and_then(|(addr, name)| {
|
||||||
let addr: Address = addr.into();
|
let addr: Address = addr.into();
|
||||||
store.set_account_name(addr, name).map_err(|_| Error::invalid_params()).map(|_| Value::Null)
|
store.set_account_name(addr, name).map_err(|e| errors::account("Could not set account name.", e)).map(|_| Value::Null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,14 +133,16 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
from_params::<(RpcH160, _)>(params).and_then(|(addr, meta)| {
|
from_params::<(RpcH160, _)>(params).and_then(|(addr, meta)| {
|
||||||
let addr: Address = addr.into();
|
let addr: Address = addr.into();
|
||||||
store.set_account_meta(addr, meta).map_err(|_| Error::invalid_params()).map(|_| Value::Null)
|
store.set_account_meta(addr, meta).map_err(|e| errors::account("Could not set account meta.", e)).map(|_| Value::Null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accounts_info(&self, _: Params) -> Result<Value, Error> {
|
fn accounts_info(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
Ok(Value::Object(try!(store.accounts_info().map_err(|_| Error::invalid_params())).into_iter().map(|(a, v)| {
|
let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e)));
|
||||||
|
Ok(Value::Object(info.into_iter().map(|(a, v)| {
|
||||||
let m = map![
|
let m = map![
|
||||||
"name".to_owned() => to_value(&v.name).unwrap(),
|
"name".to_owned() => to_value(&v.name).unwrap(),
|
||||||
"meta".to_owned() => to_value(&v.meta).unwrap(),
|
"meta".to_owned() => to_value(&v.meta).unwrap(),
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ use ethcore::client::MiningBlockChainClient;
|
|||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use v1::traits::PersonalSigner;
|
use v1::traits::PersonalSigner;
|
||||||
use v1::types::{TransactionModification, ConfirmationRequest, U256};
|
use v1::types::{TransactionModification, ConfirmationRequest, U256};
|
||||||
use v1::impls::{unlock_sign_and_dispatch, signature_with_password};
|
use v1::helpers::{errors, SigningQueue, ConfirmationsQueue, ConfirmationPayload};
|
||||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, ConfirmationPayload};
|
use v1::helpers::params::expect_no_params;
|
||||||
|
use v1::helpers::dispatch::{unlock_sign_and_dispatch, signature_with_password};
|
||||||
|
|
||||||
/// Transactions confirmation (personal) rpc implementation.
|
/// Transactions confirmation (personal) rpc implementation.
|
||||||
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||||
@@ -55,8 +56,9 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
|
|||||||
|
|
||||||
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
fn requests_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
fn requests_to_confirm(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
let queue = take_weak!(self.queue);
|
let queue = take_weak!(self.queue);
|
||||||
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<ConfirmationRequest>>())
|
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<ConfirmationRequest>>())
|
||||||
}
|
}
|
||||||
@@ -91,7 +93,7 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
|
|||||||
queue.request_confirmed(id, Ok(response.clone()));
|
queue.request_confirmed(id, Ok(response.clone()));
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}).unwrap_or_else(|| Err(Error::invalid_params()))
|
}).unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id)))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use v1::traits::Rpc;
|
use v1::traits::Rpc;
|
||||||
|
use v1::helpers::params::expect_no_params;
|
||||||
|
|
||||||
/// RPC generic methods implementation.
|
/// RPC generic methods implementation.
|
||||||
pub struct RpcClient {
|
pub struct RpcClient {
|
||||||
@@ -39,7 +40,8 @@ impl RpcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rpc for RpcClient {
|
impl Rpc for RpcClient {
|
||||||
fn rpc_modules(&self, _: Params) -> Result<Value, Error> {
|
fn rpc_modules(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
let modules = self.modules.iter()
|
let modules = self.modules.iter()
|
||||||
.fold(BTreeMap::new(), |mut map, (k, v)| {
|
.fold(BTreeMap::new(), |mut map, (k, v)| {
|
||||||
map.insert(k.to_owned(), Value::String(v.to_owned()));
|
map.insert(k.to_owned(), Value::String(v.to_owned()));
|
||||||
@@ -48,7 +50,8 @@ impl Rpc for RpcClient {
|
|||||||
Ok(Value::Object(modules))
|
Ok(Value::Object(modules))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modules(&self, _: Params) -> Result<Value, Error> {
|
fn modules(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(expect_no_params(params));
|
||||||
let modules = self.modules.iter()
|
let modules = self.modules.iter()
|
||||||
.filter(|&(k, _v)| {
|
.filter(|&(k, _v)| {
|
||||||
self.valid_apis.contains(k)
|
self.valid_apis.contains(k)
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
|
|||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
||||||
use v1::traits::Traces;
|
use v1::traits::Traces;
|
||||||
use v1::helpers::CallRequest as CRequest;
|
use v1::helpers::{errors, CallRequest as CRequest};
|
||||||
|
use v1::helpers::params::from_params_default_third;
|
||||||
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
|
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
|
||||||
use v1::impls::from_params_default_third;
|
|
||||||
|
|
||||||
fn to_call_analytics(flags: Vec<String>) -> CallAnalytics {
|
fn to_call_analytics(flags: Vec<String>) -> CallAnalytics {
|
||||||
CallAnalytics {
|
CallAnalytics {
|
||||||
@@ -144,7 +144,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
|
|||||||
Ok(e) => to_value(&TraceResults::from(e)),
|
Ok(e) => to_value(&TraceResults::from(e)),
|
||||||
_ => Ok(Value::Null),
|
_ => Ok(Value::Null),
|
||||||
},
|
},
|
||||||
Err(_) => Err(Error::invalid_params()),
|
Err(e) => Err(errors::invalid_params("Transaction is not valid RLP", e)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use jsonrpc_core::*;
|
|||||||
use util::version;
|
use util::version;
|
||||||
use v1::traits::Web3;
|
use v1::traits::Web3;
|
||||||
use v1::types::{H256, Bytes};
|
use v1::types::{H256, Bytes};
|
||||||
|
use v1::helpers::params::expect_no_params;
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
|
|
||||||
/// Web3 rpc implementation.
|
/// Web3 rpc implementation.
|
||||||
@@ -31,10 +32,8 @@ impl Web3Client {
|
|||||||
|
|
||||||
impl Web3 for Web3Client {
|
impl Web3 for Web3Client {
|
||||||
fn client_version(&self, params: Params) -> Result<Value, Error> {
|
fn client_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
try!(expect_no_params(params));
|
||||||
Params::None => Ok(Value::String(version().to_owned().replace("Parity/", "Parity//"))),
|
Ok(Value::String(version().to_owned().replace("Parity/", "Parity//")))
|
||||||
_ => Err(Error::invalid_params())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sha3(&self, params: Params) -> Result<Value, Error> {
|
fn sha3(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user