Compare commits

..

50 Commits

Author SHA1 Message Date
Arkadiy Paronyan
2cf4549d01 DAO hard-fork (#1483) (#1636)
* DAO hard-fork (#1483)

* Minor additions to allow resetting of code.

* Add test.

* Provisional DAO hard-fork proposal.

* Change to reflect latest HF spec.

* Include extradata restrictions and overrides.

* Introduce new tests.

* Update tests to new spec format.

* Allow JSON chain spec fields to be optional.

* Remove superfluous definitions. Fix overflow risk.

* Fix build.

* Add missing file.

* Remove old flag.

* Update to latest address set.

* Update tests and test spec to latest.

Change the mining default to release only on own transactions.

* Updated tests submodule
2016-07-16 15:04:02 +02:00
Arkadiy Paronyan
68dfae8f06 Backports for beta (#1628)
* Remove soft-fork stuff.

* Fix tests.

* Fix "pending" parameter on RPC block requests (#1602)

* Initial commit.

* Pending blocks work.

* Address grumbles.

* Fix up for new API.

* Fixed test
2016-07-15 16:44:27 +02:00
Arkadiy Paronyan
b7caa24c2e don't batch best block for branches (#1623) (#1626) 2016-07-15 10:13:12 +02:00
Arkadiy Paronyan
ed5d797662 Merge bugfixes from master to beta (#1605)
* Attempt to fix blochchain DB sync

* Fix bloomchain on blockchain repair

* Make sure reserved peers are in the node table

* fixed #1606 (#1615)
2016-07-14 12:52:07 +02:00
Nikolay Volf
69847e3b8b (BETA) using block options cache instead of general cache for rocksdb (#1613)
* using block options cache instead of general cache for rocksdb

* remove previous cache setup
2016-07-14 10:25:09 +02:00
Arkadiy Paronyan
ef124fa3ef Backport sealing fixes to beta (#1583)
* Update sealing just once when  externally importing many blocks (#1541)

Fixes Issue #1372

* Fixing deadlock in miner (#1569)

* Fixing deadlock in miner
* Adding more comments
2016-07-12 09:52:46 +02:00
Arkadiy Paronyan
aece120e77 v1.2.2 in beta (#1581)
* v1.2.2

* Fixed warning
2016-07-12 09:48:52 +02:00
Gav Wood
cc127eed15 Fix the reseal mechanism. 2016-07-06 12:37:45 +02:00
Gav Wood
acfabe5431 Skipping transactions with invalid nonces when pushing to block. (#1545) (#1547)
* Changing some logging levels

* Skipping invalid nonce errors
2016-07-06 10:41:29 +02:00
arkpar
a600b1ac80 Merge remote-tracking branch 'origin/work-notify' into beta 2016-07-01 13:10:22 +02:00
arkpar
1ce3fc24cf Save the block reference in the queue on notification 2016-07-01 11:37:31 +02:00
arkpar
df04c95f9a Merge branch 'fixmining' of github.com:ethcore/parity into beta 2016-06-30 23:20:28 +02:00
arkpar
6e7b003e78 Merge branch 'fixmining' of github.com:ethcore/parity into beta 2016-06-30 23:20:12 +02:00
arkpar
0de297adf7 Merge branch 'work-notify' into beta 2016-06-30 22:44:09 +02:00
arkpar
5a7fd628db Added comment 2016-06-30 22:34:54 +02:00
arkpar
3921e10af0 Merge branch 'work-notify' of github.com:ethcore/parity into beta 2016-06-30 18:21:12 +02:00
arkpar
4ba8b3c1e0 Merge remote-tracking branch 'origin/master' into beta 2016-06-30 18:20:06 +02:00
goldylucks
a7c332ecea status page bump 2016-06-30 18:07:38 +02:00
arkpar
070c1b170f Save the block reference in the queue on notification 2016-06-30 17:33:21 +02:00
arkpar
c38d15ad4d Merge branch 'master' of github.com:ethcore/parity into beta 2016-06-30 16:11:56 +02:00
Arkadiy Paronyan
34155730ff Merge pull request #1492 from ethcore/v1.2.1
v1.2.1 in beta
2016-06-30 08:13:17 +02:00
arkpar
2df737bebf v1.2.1 2016-06-29 22:27:49 +02:00
arkpar
9885bdcf0a Merge remote-tracking branch 'origin/master' into beta 2016-06-29 22:14:42 +02:00
arkpar
09e1970bbf Merge with master 2016-06-28 20:04:00 +02:00
Tomasz Drwięga
e3ca87c4d1 Updating WS version 2016-06-28 19:40:23 +02:00
Tomasz Drwięga
1fcf5c7cc2 Fixing HTTP file serving on ws-rs 2016-06-28 19:40:22 +02:00
Tomasz Drwięga
dd0e681847 Using stable version of ws-rs 2016-06-28 19:40:22 +02:00
arkpar
c006f446a4 Reduced IO messages; removed panics on IO notifications 2016-06-28 19:39:59 +02:00
Gav Wood
9a16e593e2 Update configuration.rs 2016-06-28 19:39:31 +02:00
Gav Wood
5ef767f7f2 Update cli.rs
[ci:skip]
2016-06-28 19:39:31 +02:00
Nikolay Volf
071da2eec5 fix tests 2016-06-28 19:39:31 +02:00
Nikolay Volf
a3f165cf48 cli config 2016-06-28 19:39:31 +02:00
Nikolay Volf
93facbf854 ethcore client config 2016-06-28 19:39:31 +02:00
Nikolay Volf
42f5d7f897 jdb to new settings config 2016-06-28 19:39:31 +02:00
Nikolay Volf
97e553f1bf extra helpers for prefix 2016-06-28 19:38:48 +02:00
Nikolay Volf
8caa859111 compaction struct and helpers 2016-06-28 19:38:48 +02:00
Tomasz Drwięga
ac9e6f2649 Handle errors when starting parity (#1451) 2016-06-27 17:24:47 +02:00
Arkadiy Paronyan
33dfb819f0 Fixed losing queued blocks on ancient block error (#1453) 2016-06-27 17:24:36 +02:00
arkpar
2e99bfafc8 Updated to latest hyper with patched mio 2016-06-27 17:22:38 +02:00
Gav Wood
a2c4d550d0 Retweak BASE and MULTIPLIER in rocksdb config. (#1445) 2016-06-27 17:21:25 +02:00
Gav Wood
bc8ba10184 More conservative settings for rocksdb. (#1440) 2016-06-27 17:09:18 +02:00
Gav Wood
cdc34957db Don't mine without --author (#1436)
Requires --author to be set before mining is allowed to happen.
2016-06-26 22:28:55 +02:00
Gav Wood
8a644e7185 Revert the rescuedao extradata. 2016-06-26 22:28:27 +02:00
Arkadiy Paronyan
879bee994d Merge pull request #1420 from ethcore/pdb-exe-artifact
(BETA) add artifacts
2016-06-24 16:53:13 +02:00
NikVolf
a6f7957042 add artifacts 2016-06-24 17:48:42 +03:00
arkpar
2785d61e75 Merge branch 'newblocknumber' of github.com:ethcore/parity into beta 2016-06-24 16:34:26 +02:00
arkpar
8b49b315d9 Merge branch 'master' of github.com:ethcore/parity into beta 2016-06-24 16:34:17 +02:00
arkpar
84ded6f43c Merge branch 'master' of github.com:ethcore/parity into beta 2016-06-24 09:16:49 +02:00
arkpar
eafc1b153d Merge branch 'master' of github.com:ethcore/parity into beta 2016-06-22 16:01:16 +02:00
arkpar
53b0862096 Set version to beta 2016-06-22 12:19:49 +02:00
455 changed files with 13728 additions and 25977 deletions

View File

@@ -9,8 +9,3 @@ trim_trailing_whitespace=true
max_line_length=120
insert_final_newline=true
[.travis.yml]
indent_style=space
indent_size=2
tab_width=8
end_of_line=lf

View File

@@ -1,166 +0,0 @@
stages:
- build
- deploy
variables:
GIT_DEPTH: "3"
SIMPLECOV: "true"
RUST_BACKTRACE: "1"
cache:
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
untracked: true
linux-beta:
stage: build
image: ethcore/rust:beta
script:
- cargo build --release --verbose
- strip target/release/parity
tags:
- rust
- rust-beta
artifacts:
paths:
- target/release/parity
name: "${CI_BUILD_NAME}_parity"
linux-stable:
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:
stage: build
image: ethcore/rust:nightly
script:
- cargo build --release --verbose
- strip target/release/parity
tags:
- rust
- rust-nightly
artifacts:
paths:
- target/release/parity
name: "${CI_BUILD_NAME}_parity"
linux-centos:
stage: build
image: ethcore/rust-centos:latest
script:
- export CXX="g++"
- export CC="gcc"
- cargo build --release --verbose
- strip target/release/parity
tags:
- rust
- rust-centos
artifacts:
paths:
- target/release/parity
name: "${CI_BUILD_NAME}_parity"
linux-armv7:
stage: build
image: ethcore/rust-arm:latest
script:
- export CXX=arm-linux-gnueabihf-g++
- export CC=arm-linux-gnueabihf-gcc
- mkdir -p .cargo
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
tags:
- rust
- rust-arm
artifacts:
paths:
- target/armv7-unknown-linux-gnueabihf/release/parity
name: "${CI_BUILD_NAME}_parity"
linux-arm:
stage: build
image: ethcore/rust-arm:latest
script:
- export CXX=arm-linux-gnueabihf-g++
- export CC=arm-linux-gnueabihf-gcc
- mkdir -p .cargo
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
tags:
- rust
- rust-arm
artifacts:
paths:
- target/arm-unknown-linux-gnueabihf/release/parity
name: "${CI_BUILD_NAME}_parity"
linux-armv6:
stage: build
image: ethcore/rust-arm:latest
script:
- export CXX=arm-linux-gnueabi-g++
- export CC=arm-linux-gnueabi-gcc
- mkdir -p .cargo
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
tags:
- rust
- rust-arm
artifacts:
paths:
- target/arm-unknown-linux-gnueabi/release/parity
name: "${CI_BUILD_NAME}_parity"
linux-aarch64:
stage: build
image: ethcore/rust-arm:latest
script:
- export CXX=aarch64-linux-gnu-g++
- export CC=aarch64-linux-gnu-gcc
- mkdir -p .cargo
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
tags:
- rust
- rust-arm
artifacts:
paths:
- target/aarch64-unknown-linux-gnu/release/parity
name: "${CI_BUILD_NAME}_parity"
darwin:
stage: build
script:
- cargo build --release --verbose
tags:
- osx
artifacts:
paths:
- target/release/parity
name: "${CI_BUILD_NAME}_parity"
windows:
stage: build
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 LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
- set RUST_BACKTRACE=1
- SET
- rustup default stable-x86_64-pc-windows-msvc
- cargo build --release --verbose
tags:
- rust-windows
artifacts:
paths:
- target/release/parity.exe
- target/release/parity.pdb
name: "${CI_BUILD_NAME}_parity"

View File

@@ -1,7 +1,6 @@
sudo: required
dist: trusty
language: rust
branches:
only:
- master
@@ -9,69 +8,101 @@ branches:
- /^stable-.*$/
- /^beta$/
- /^stable$/
git:
depth: 3
matrix:
fast_finish: true
allow_failures:
- rust: nightly
include:
- rust: stable
env: RUN_TESTS="true"
- rust: beta
env: RUN_COVERAGE="true"
env: FEATURES="--features travis-beta" RUN_TESTS="true"
# - rust: beta
# env: FEATURES="--features travis-beta" RUN_TESTS="true"
- rust: stable
env: RUN_DOCS="true"
env: FEATURES="--features travis-beta" RUN_BUILD="true"
- rust: beta
env: FEATURES="--features travis-beta" RUN_BUILD="true"
- rust: stable
env: FEATURES="--features travis-beta" RUN_COVERAGE="true"
# - rust: nightly
# env: FEATURES="--features travis-nightly" RUN_BENCHES="true"
- rust: nightly
env: FEATURES="--features travis-nightly" RUN_TESTS="true"
env:
global:
- CXX="g++-4.8"
- CC="gcc-4.8"
- RUST_BACKTRACE="1"
# GH_TOKEN
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
- TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES=""
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests target/kcov"
- RUN_TESTS="false"
- RUN_COVERAGE="false"
- RUN_DOCS="false"
# GH_TOKEN for documentation
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov"
- RUN_BUILD="false"
- RUN_BENCHES="false"
- RUST_BACKTRACE="1"
cache:
apt: true
directories:
- $TRAVIS_BUILD_DIR/target
- $TRAVIS_BUILD_DIR/kcov-master
- $HOME/.cargo
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
- gcc-4.8
- g++-4.8
install:
- ([ "$RUN_COVERAGE" = "false" ]) || (test -x $KCOV_CMD) || (
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
tar xzf master.tar.gz &&
mkdir -p kcov-master/build &&
cd kcov-master/build &&
cmake .. &&
make && make install DESTDIR=../tmp &&
cd
)
script:
- if [ "$RUN_TESTS" = "true" ]; then ./test.sh --verbose; fi
- if [ "$RUN_COVERAGE" = "true" ]; then ./scripts/cov.sh "$KCOV_CMD"; fi
- if [ "$RUN_TESTS" = "true" ]; then cargo test --release --verbose ${FEATURES} ${TARGETS}; fi
- if [ "$RUN_BENCHES" = "true" ]; then cargo bench --no-run ${FEATURES} ${TARGETS}; fi
- if [ "$RUN_BUILD" = "true" ]; then cargo build --release --verbose ${FEATURES}; fi
- if [ "$RUN_BUILD" = "true" ]; then tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity; fi
after_success: |
[ "$RUN_COVERAGE" = "true" ] &&
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
cargo test --no-run ${KCOV_FEATURES} ${TARGETS} &&
$KCOV_CMD target/debug/deps/ethkey-* &&
$KCOV_CMD target/debug/deps/ethstore-* &&
$KCOV_CMD target/debug/deps/ethcore_util-* &&
$KCOV_CMD target/debug/deps/ethash-* &&
$KCOV_CMD target/debug/deps/ethcore-* &&
$KCOV_CMD target/debug/deps/ethsync-* &&
$KCOV_CMD target/debug/deps/ethcore_rpc-* &&
$KCOV_CMD target/debug/deps/ethcore_dapps-* &&
$KCOV_CMD target/debug/deps/ethcore_signer-* &&
$KCOV_CMD target/debug/deps/ethjson-* &&
$KCOV_CMD target/debug/parity-* &&
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
[ "$RUN_DOCS" = "true" ] &&
./scripts/doc.sh &&
[ $TRAVIS_RUST_VERSION = stable ] &&
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
echo '<meta http-equiv=refresh content=0;url=ethcore/index.html>' > target/doc/index.html &&
pip install --user ghp-import &&
/home/travis/.local/bin/ghp-import -n target/doc &&
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
deploy:
provider: releases
api_key:
secure: "t+oGT/4lsy7IScw5s86Dpntl5Nyck4qG6nhHwMScc6FYzwLldgwgJaafL8Ej+HG+b7nFLriN+Snoa4YQ5o74X5ZlSWubVREOYQlL/fq7vcPB0DwAZ0Jufq1QW2R1M+3SwwF1eAwTv2W3G7A2K7dxjCVvENcy/gdxnZ36NeUPsqaCC9UcI2Yc7+4jyQwvx6ZfBvQeu+HbKENA0eUNs2ZQOID/1IPy0LJBvSyxAQYsysXdjTzGdNu4+Iba20E8uWYe4fAbgz+gwGarXg1L6D6gKyMlWkViqWjvXWBuDJJqMQZ3rw41AwZOoh3mKd2Lc0l6l4oZcEqPuob0yKTNjz1tuJy9xKTC2F2bDzsvUgk1IRfMK5ukXXXS09ZCZWuA9/GtnsqJ1xGTiwX+DhQzpVBHaBiseSNlYE1YN/3jNyGY+iSts1qut+1BwE7swmcTLsAPoAy8Ue+f7ErNoCg1lm71vq7VO2DLn7x2NqHyHUEuJ+7olDHSdE84G7d9otDRu/+TfMOw7GXwTaha6yJRInuNsnj4CFMLNVvYACzCC2idB7f7nUZoSFi9jf18S9fCMPVmazMrFj4g95HWrVHkjpV5zRTeUdTWw6DJl6pC9HFqORHdCvLv4Rc4dm5r3CmOcAQ0ZuiccV2oKzw4/Wic96daae8M5f5KSQ/WTr+h0wXZKp0="
skip_cleanup: true
file: parity${ARCHIVE_SUFFIX}.tar.gz
on:
tags: true
notifications:
webhooks:
urls:
- https://hooks.slack.com/services/${SLACK_WEBHOOK}
on_success: always
on_failure: always
on_start: never
notifications:
slack:
rooms:
- ethcore:4EGxt9WP6AS7uX4JKXSfR9vi#chatops

716
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[package]
description = "Ethcore client."
name = "parity"
version = "1.3.0"
version = "1.2.2"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
@@ -10,7 +10,6 @@ build = "build.rs"
rustc_version = "0.1"
syntex = "*"
ethcore-ipc-codegen = { path = "ipc/codegen" }
ethcore-ipc-tests = { path = "ipc/tests" }
[dependencies]
log = "0.3"
@@ -18,30 +17,24 @@ env_logger = "0.3"
rustc-serialize = "0.3"
docopt = "0.6"
time = "0.1"
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
fdlimit = { path = "util/fdlimit" }
num_cpus = "0.2"
number_prefix = "0.2"
rpassword = "0.2.1"
semver = "0.2"
ansi_term = "0.7"
lazy_static = "0.2"
regex = "0.1"
isatty = "0.1"
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
fdlimit = { path = "util/fdlimit" }
clippy = { version = "0.0.77", optional = true}
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethsync = { path = "sync" }
ethcore-io = { path = "util/io" }
ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc" }
ethcore-signer = { path = "signer" }
ethcore-rpc = { path = "rpc", optional = true }
ethcore-signer = { path = "signer", optional = true }
ethcore-dapps = { path = "dapps", optional = true }
semver = "0.2"
ethcore-ipc-nano = { path = "ipc/nano" }
ethcore-ipc = { path = "ipc/rpc" }
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
ethcore-logger = { path = "logger" }
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
ethcore-dapps = { path = "dapps", optional = true }
clippy = { version = "0.0.80", optional = true}
ansi_term = "0.7"
[target.'cfg(windows)'.dependencies]
winapi = "0.2"
@@ -54,13 +47,13 @@ version = "0.8"
default-features = false
[features]
default = ["ui", "use-precompiled-js"]
ui = ["dapps", "ethcore-signer/ui"]
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
default = ["rpc", "dapps", "ethcore-signer"]
rpc = ["ethcore-rpc"]
dapps = ["ethcore-dapps"]
ipc = ["ethcore/ipc"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
json-tests = ["ethcore/json-tests"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev",
"ethcore-dapps/dev", "ethcore-signer/dev"]
travis-beta = ["ethcore/json-tests"]
travis-nightly = ["ethcore/json-tests", "dev"]
[[bin]]
path = "parity/main.rs"
@@ -69,4 +62,3 @@ name = "parity"
[profile.release]
debug = true
lto = false

View File

@@ -1,11 +1,7 @@
# [Parity](https://ethcore.io/parity.html)
### Fast, light, and robust Ethereum implementation
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
[Internal Documentation][doc-url]
Be sure to check out [our wiki][wiki-url] for more information.
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
[travis-url]: https://travis-ci.org/ethcore/parity
@@ -15,8 +11,8 @@ Be sure to check out [our wiki][wiki-url] for more information.
[gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
[doc-url]: http://ethcore.github.io/parity/ethcore/index.html
[wiki-url]: https://github.com/ethcore/parity/wiki
[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html)
----
@@ -33,7 +29,7 @@ This includes a few useful Dapps, including Ethereum Wallet, Maker OTC, and a no
In a near-future release, it will be easy to install Dapps and use them through this web interface.
If you run into an issue while using parity, feel free to file one in this repository
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
or hop on our [gitter chat room]([gitter-url]) to ask a question. We are glad to help!
Parity's current release is 1.2. You can download it at https://ethcore.io/parity.html or follow the instructions
below to build from source.

View File

@@ -17,16 +17,13 @@ branches:
install:
- git submodule update --init --recursive
- ps: Install-Product node 6
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.10.0-x86_64-pc-windows-msvc.exe"
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-x86_64-pc-windows-msvc.exe"
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe
- rust-1.10.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- rust-1.9.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
- rustc -V
- cargo -V
- node -v
- npm -v
build: off
@@ -43,6 +40,10 @@ after_test:
artifacts:
- path: nsis\installer.exe
name: Windows Installer (x86_64)
- path: target\release\parity.exe
name: Windows Executable (x86_64)
- path: target\release\parity.pdb
name: Windows Executable Debug Symbols (x86_64)
cache:
- target

View File

@@ -15,11 +15,35 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate rustc_version;
extern crate syntex;
extern crate ethcore_ipc_codegen as codegen;
use std::env;
use std::path::Path;
use rustc_version::{version_meta, Channel};
fn main() {
if let Channel::Nightly = version_meta().channel {
println!("cargo:rustc-cfg=nightly");
}
let out_dir = env::var_os("OUT_DIR").unwrap();
// ipc pass
{
let src = Path::new("parity/hypervisor/service.rs.in");
let dst = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
// serialization pass
{
let src = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
let dst = Path::new(&out_dir).join("hypervisor_service_cg.rs");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
}

43
cov.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/bin/sh
# Installing KCOV under ubuntu
# https://users.rust-lang.org/t/tutorial-how-to-collect-test-coverages-for-rust-project/650#
### Install deps
# $ sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev
#
### Compile kcov
# $ wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xf master.tar.gz
# $ cd kcov-master && mkdir build && cd build
# $ cmake .. && make && sudo make install
### Running coverage
if ! type kcov > /dev/null; then
echo "Install kcov first (details inside this file). Aborting."
exit 1
fi
cargo test \
-p ethkey \
-p ethstore \
-p ethash \
-p ethcore-util \
-p ethcore \
-p ethsync \
-p ethcore-rpc \
-p parity \
-p ethcore-signer \
-p ethcore-dapps \
--no-run || exit $?
rm -rf target/coverage
mkdir -p target/coverage
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests"
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethkey-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethstore-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethash-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_signer-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_dapps-*
xdg-open target/coverage/index.html

View File

@@ -1,7 +1,7 @@
[package]
description = "Parity Dapps crate"
name = "ethcore-dapps"
version = "1.3.0"
version = "1.2.0"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io"]
build = "build.rs"
@@ -10,10 +10,9 @@ build = "build.rs"
[dependencies]
log = "0.3"
jsonrpc-core = "2.1"
jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"
url = "1.0"
rustc-serialize = "0.3"
serde = "0.7.0"
@@ -21,13 +20,15 @@ serde_json = "0.7.0"
serde_macros = { version = "0.7.0", optional = true }
ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" }
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
parity-dapps = { git = "https://github.com/ethcore/parity-dapps-rs.git", version = "0.3" }
# List of apps
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true }
parity-dapps-status = { git = "https://github.com/ethcore/parity-dapps-status-rs.git", version = "0.5.1" }
parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtins-rs.git", version = "0.5.2" }
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.6.0", optional = true }
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true }
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true }
mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.80", optional = true}
clippy = { version = "0.0.77", optional = true}
[build-dependencies]
serde_codegen = { version = "0.7.0", optional = true }
@@ -38,9 +39,3 @@ default = ["serde_codegen", "extra-dapps"]
extra-dapps = ["parity-dapps-wallet"]
nightly = ["serde_macros"]
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
use-precompiled-js = [
"parity-dapps-status/use-precompiled-js",
"parity-dapps-home/use-precompiled-js",
"parity-dapps-wallet/use-precompiled-js"
]

View File

@@ -25,8 +25,8 @@ mod inner {
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("./src/api/types.rs.in");
let dst = Path::new(&out_dir).join("types.rs");
let src = Path::new("./src/api/mod.rs.in");
let dst = Path::new(&out_dir).join("mod.rs");
let mut registry = syntex::Registry::new();

View File

@@ -15,23 +15,42 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use hyper::{server, net, Decoder, Encoder, Next};
use api::types::{App, ApiError};
use api::response::{as_json, as_json_error, ping_response};
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
use api::response::as_json;
#[derive(Clone)]
pub struct RestApi {
local_domain: String,
endpoints: Arc<Endpoints>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
impl RestApi {
pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
Box::new(RestApi {
local_domain: local_domain,
endpoints: endpoints,
endpoints: endpoints
})
}
@@ -44,58 +63,7 @@ impl RestApi {
impl Endpoint for RestApi {
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
Box::new(RestApiRouter {
api: self.clone(),
handler: as_json_error(&ApiError {
code: "404".into(),
title: "Not Found".into(),
detail: "Resource you requested has not been found.".into(),
}),
})
as_json(&self.list_apps())
}
}
struct RestApiRouter {
api: RestApi,
handler: Box<Handler>,
}
impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
let url = extract_url(&request);
if url.is_none() {
// Just return 404 if we can't parse URL
return Next::write();
}
let url = url.expect("Check for None is above; qed");
let endpoint = url.path.get(1).map(|v| v.as_str());
let handler = endpoint.and_then(|v| match v {
"apps" => Some(as_json(&self.api.list_apps())),
"ping" => Some(ping_response(&self.api.local_domain)),
_ => None,
});
// Overwrite default
if let Some(h) = handler {
self.handler = h;
}
self.handler.on_request(request)
}
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
self.handler.on_request_readable(decoder)
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
self.handler.on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
self.handler.on_response_writable(encoder)
}
}

View File

@@ -16,12 +16,13 @@
//! REST API
#![warn(missing_docs)]
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
mod api;
mod response;
mod types;
#[cfg(feature = "serde_macros")]
include!("mod.rs.in");
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
pub use self::api::RestApi;
pub use self::types::App;

View File

@@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blocks database migrations.
mod api;
mod response;
mod v8;
pub use self::v8::V8;
pub use self::api::RestApi;
pub use self::api::App;

View File

@@ -16,21 +16,8 @@
use serde::Serialize;
use serde_json;
use endpoint::Handler;
use handlers::{ContentHandler, EchoHandler};
use endpoint::{ContentHandler, Handler};
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
}
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
}
pub fn ping_response(local_domain: &str) -> Box<Handler> {
Box::new(EchoHandler::cors(vec![
format!("http://{}", local_domain),
// Allow CORS calls also for localhost
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
]))
Box::new(ContentHandler::new(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
}

View File

@@ -1,21 +0,0 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#[cfg(feature = "serde_macros")]
include!("types.rs.in");
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/types.rs"));

View File

@@ -1,50 +0,0 @@
// 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 endpoint::EndpointInfo;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
/// Creates `App` instance from `EndpointInfo` and `id`.
pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ApiError {
pub code: String,
pub title: String,
pub detail: String,
}

View File

@@ -22,7 +22,7 @@ use parity_dapps::WebApp;
mod fs;
extern crate parity_dapps_status;
extern crate parity_dapps_home;
extern crate parity_dapps_builtins;
pub const DAPPS_DOMAIN : &'static str = ".parity";
pub const RPC_PATH : &'static str = "rpc";
@@ -34,7 +34,7 @@ pub fn main_page() -> &'static str {
}
pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_dapps_home::App::default(), UTILS_PATH.to_owned()))
Box::new(PageEndpoint::with_prefix(parity_dapps_builtins::App::default(), UTILS_PATH.to_owned()))
}
pub fn all_endpoints(dapps_path: String) -> Endpoints {
@@ -44,7 +44,7 @@ pub fn all_endpoints(dapps_path: String) -> Endpoints {
// because we use Cross-Origin LocalStorage.
// TODO [ToDr] Account naming should be moved to parity.
pages.insert("home".into(), Box::new(
PageEndpoint::new_safe_to_embed(parity_dapps_home::App::default())
PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default())
));
pages.insert("proxy".into(), ProxyPac::boxed());
insert::<parity_dapps_status::App>(&mut pages, "parity");
@@ -52,6 +52,8 @@ pub fn all_endpoints(dapps_path: String) -> Endpoints {
// Optional dapps
wallet_page(&mut pages);
daodapp_page(&mut pages);
makerotc_page(&mut pages);
pages
}
@@ -64,6 +66,22 @@ fn wallet_page(pages: &mut Endpoints) {
#[cfg(not(feature = "parity-dapps-wallet"))]
fn wallet_page(_pages: &mut Endpoints) {}
#[cfg(feature = "parity-dapps-dao")]
fn daodapp_page(pages: &mut Endpoints) {
extern crate parity_dapps_dao;
insert::<parity_dapps_dao::App>(pages, "dao");
}
#[cfg(not(feature = "parity-dapps-dao"))]
fn daodapp_page(_pages: &mut Endpoints) {}
#[cfg(feature = "parity-dapps-makerotc")]
fn makerotc_page(pages: &mut Endpoints) {
extern crate parity_dapps_makerotc;
insert::<parity_dapps_makerotc::App>(pages, "makerotc");
}
#[cfg(not(feature = "parity-dapps-makerotc"))]
fn makerotc_page(_pages: &mut Endpoints) {}
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) {
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default())));
}

View File

@@ -16,7 +16,11 @@
//! URL Endpoint traits
use hyper::{server, net};
use hyper::status::StatusCode;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use std::io::Write;
use std::collections::BTreeMap;
#[derive(Debug, PartialEq, Default, Clone)]
@@ -38,8 +42,58 @@ pub struct EndpointInfo {
pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<net::HttpStream> + Send>;
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream> + Send>;
}
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
pub type Handler = server::Handler<net::HttpStream> + Send;
pub type Handler = server::Handler<HttpStream> + Send;
pub struct ContentHandler {
content: String,
mimetype: String,
write_pos: usize,
}
impl ContentHandler {
pub fn new(content: String, mimetype: String) -> Self {
ContentHandler {
content: content,
mimetype: mimetype,
write_pos: 0
}
}
}
impl server::Handler<HttpStream> for ContentHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Ok);
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let bytes = self.content.as_bytes();
if self.write_pos == bytes.len() {
return Next::end();
}
match encoder.write(&bytes[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}

View File

@@ -1,44 +0,0 @@
// 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/>.
//! Authorization Handlers
use hyper::{server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
pub struct AuthRequiredHandler;
impl server::Handler<HttpStream> for AuthRequiredHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
Next::write()
}
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
Next::end()
}
}

View File

@@ -1,101 +0,0 @@
// 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/>.
//! Simple Content Handler
use std::io::Write;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
pub struct ContentHandler {
code: StatusCode,
content: String,
mimetype: String,
write_pos: usize,
}
impl ContentHandler {
pub fn ok(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::Ok,
content: content,
mimetype: mimetype,
write_pos: 0
}
}
pub fn forbidden(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::Forbidden,
content: content,
mimetype: mimetype,
write_pos: 0
}
}
pub fn not_found(content: String, mimetype: String) -> Self {
ContentHandler {
code: StatusCode::NotFound,
content: content,
mimetype: mimetype,
write_pos: 0
}
}
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
ContentHandler {
code: code,
content: content,
mimetype: mimetype,
write_pos: 0,
}
}
}
impl server::Handler<HttpStream> for ContentHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(self.code);
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let bytes = self.content.as_bytes();
if self.write_pos == bytes.len() {
return Next::end();
}
match encoder.write(&bytes[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}

View File

@@ -1,148 +0,0 @@
// 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/>.
//! Echo Handler
use std::io::Read;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::method::Method;
use hyper::net::HttpStream;
use unicase::UniCase;
use super::ContentHandler;
#[derive(Debug, PartialEq)]
/// Type of Cross-Origin request
enum Cors {
/// Not a Cross-Origin request - no headers needed
No,
/// Cross-Origin request with valid Origin
Allowed(String),
/// Cross-Origin request with invalid Origin
Forbidden,
}
pub struct EchoHandler {
safe_origins: Vec<String>,
content: String,
cors: Cors,
handler: Option<ContentHandler>,
}
impl EchoHandler {
pub fn cors(safe_origins: Vec<String>) -> Self {
EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::Forbidden,
handler: None,
}
}
fn cors_header(&self, origin: Option<String>) -> Cors {
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
for safe in safe_origins {
if origin.starts_with(safe) {
return true;
}
}
false
}
match origin {
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
Cors::Allowed(origin.clone())
},
None => Cors::No,
_ => Cors::Forbidden,
}
}
}
impl server::Handler<HttpStream> for EchoHandler {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
let origin = request.headers().get_raw("origin")
.and_then(|list| list.get(0))
.and_then(|origin| String::from_utf8(origin.clone()).ok());
self.cors = self.cors_header(origin);
// Don't even read the payload if origin is forbidden!
if let Cors::Forbidden = self.cors {
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
Next::write()
} else {
Next::read()
}
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
match decoder.read_to_string(&mut self.content) {
Ok(0) => {
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
Next::write()
},
Ok(_) => Next::read(),
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::read(),
_ => Next::end(),
}
}
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
if let Cors::Allowed(ref domain) = self.cors {
let mut headers = res.headers_mut();
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
headers.set(header::AccessControlAllowHeaders(vec![
UniCase("origin".to_owned()),
UniCase("content-type".to_owned()),
UniCase("accept".to_owned()),
]));
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
}
self.handler.as_mut().unwrap().on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
self.handler.as_mut().unwrap().on_response_writable(encoder)
}
}
#[test]
fn should_return_correct_cors_value() {
// given
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
let cut = EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::No,
handler: None,
};
// when
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
let res4 = cut.cors_header(None);
// then
assert_eq!(res1, Cors::Forbidden);
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
assert_eq!(res4, Cors::No);
}

View File

@@ -1,57 +0,0 @@
// 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/>.
//! Hyper handlers implementations.
mod auth;
mod echo;
mod content;
mod redirect;
pub use self::auth::AuthRequiredHandler;
pub use self::echo::EchoHandler;
pub use self::content::ContentHandler;
pub use self::redirect::Redirection;
use url::Url;
use hyper::{server, header, net, uri};
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
match *req.uri() {
uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
uri::RequestUri::AbsolutePath(ref path) => {
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match req.headers().get::<header::Host>() {
Some(ref host) => {
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
},
None => return None,
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
},
_ => None,
}
}

View File

@@ -45,32 +45,27 @@
#[macro_use]
extern crate log;
extern crate url as url_lib;
extern crate url;
extern crate hyper;
extern crate unicase;
extern crate serde;
extern crate serde_json;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate parity_dapps;
extern crate ethcore_rpc;
extern crate ethcore_util;
extern crate mime_guess;
mod endpoint;
mod apps;
mod page;
mod router;
mod handlers;
mod rpc;
mod api;
mod proxypac;
mod url;
use std::sync::{Arc, Mutex};
use std::net::SocketAddr;
use std::collections::HashMap;
use jsonrpc_core::{IoHandler, IoDelegate};
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use ethcore_rpc::Extendable;
@@ -125,11 +120,10 @@ impl Server {
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone()));
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils());
special
});
let bind_address = format!("{}", addr);
try!(hyper::Server::http(addr))
.handle(move |_| router::Router::new(
@@ -137,7 +131,6 @@ impl Server {
endpoints.clone(),
special.clone(),
authorization.clone(),
bind_address.clone(),
))
.map(|(l, srv)| {

View File

@@ -16,8 +16,7 @@
//! Serving ProxyPac file
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler;
use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath};
use apps::DAPPS_DOMAIN;
pub struct ProxyPac;
@@ -42,7 +41,7 @@ function FindProxyForURL(url, host) {{
}}
"#,
DAPPS_DOMAIN, path.host, path.port);
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
Box::new(ContentHandler::new(content, "application/javascript".to_owned()))
}
}

View File

@@ -16,23 +16,24 @@
//! HTTP Authorization implementations
use std::io::Write;
use std::collections::HashMap;
use hyper::{server, net, header, status};
use endpoint::Handler;
use handlers::{AuthRequiredHandler, ContentHandler};
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
/// Authorization result
pub enum Authorized {
/// Authorization was successful.
Yes,
/// Unsuccessful authorization. Handler for further work is returned.
No(Box<Handler>),
No(Box<server::Handler<HttpStream> + Send>),
}
/// Authorization interface
pub trait Authorization : Send + Sync {
/// Checks if authorization is valid.
fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
fn is_authorized(&self, req: &server::Request<HttpStream>)-> Authorized;
}
/// HTTP Basic Authorization handler
@@ -44,22 +45,18 @@ pub struct HttpBasicAuth {
pub struct NoAuth;
impl Authorization for NoAuth {
fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
fn is_authorized(&self, _req: &server::Request<HttpStream>)-> Authorized {
Authorized::Yes
}
}
impl Authorization for HttpBasicAuth {
fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
fn is_authorized(&self, req: &server::Request<HttpStream>) -> Authorized {
let auth = self.check_auth(&req);
match auth {
Access::Denied => {
Authorized::No(Box::new(ContentHandler::new(
status::StatusCode::Unauthorized,
"<h1>Unauthorized</h1>".into(),
"text/html".into(),
)))
Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 }))
},
Access::AuthRequired => {
Authorized::No(Box::new(AuthRequiredHandler))
@@ -92,7 +89,7 @@ impl HttpBasicAuth {
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
}
fn check_auth(&self, req: &server::Request<net::HttpStream>) -> Access {
fn check_auth(&self, req: &server::Request<HttpStream>) -> Access {
match req.headers().get::<header::Authorization<header::Basic>>() {
Some(&header::Authorization(
header::Basic { ref username, password: Some(ref password) }
@@ -102,3 +99,63 @@ impl HttpBasicAuth {
}
}
}
pub struct UnauthorizedHandler {
write_pos: usize,
}
impl server::Handler<HttpStream> for UnauthorizedHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let response = "Unauthorized".as_bytes();
if self.write_pos == response.len() {
return Next::end();
}
match encoder.write(&response[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}
pub struct AuthRequiredHandler;
impl server::Handler<HttpStream> for AuthRequiredHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(StatusCode::Unauthorized);
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
Next::write()
}
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
Next::end()
}
}

View File

@@ -1,45 +0,0 @@
// 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 DAPPS_DOMAIN;
use hyper::server;
use hyper::net::HttpStream;
use jsonrpc_http_server::{is_host_header_valid};
use handlers::ContentHandler;
pub fn is_valid(request: &server::Request<HttpStream>, bind_address: &str, endpoints: Vec<String>) -> bool {
let mut endpoints = endpoints.into_iter()
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
.collect::<Vec<String>>();
// Add localhost domain as valid too if listening on loopback interface.
endpoints.push(bind_address.replace("127.0.0.1", "localhost").into());
endpoints.push(bind_address.into());
is_host_header_valid(request, &endpoints)
}
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
Box::new(ContentHandler::forbidden(
r#"
<h1>Request with disallowed <code>Host</code> header has been blocked.</h1>
<p>Check the URL in your browser address bar.</p>
"#.into(),
"text/html".into()
))
}

View File

@@ -17,19 +17,23 @@
//! Router implementation
//! Processes request handling authorization and dispatching it to proper application.
mod url;
mod redirect;
pub mod auth;
mod host_validation;
use DAPPS_DOMAIN;
use std::sync::Arc;
use std::collections::HashMap;
use url::{Url, Host};
use hyper::{self, server, Next, Encoder, Decoder};
use url::Host;
use hyper;
use hyper::{server, uri, header};
use hyper::{Next, Encoder, Decoder};
use hyper::net::HttpStream;
use apps;
use endpoint::{Endpoint, Endpoints, EndpointPath};
use handlers::{Redirection, extract_url};
use self::url::Url;
use self::auth::{Authorization, Authorized};
use self::redirect::Redirection;
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]
@@ -45,46 +49,40 @@ pub struct Router<A: Authorization + 'static> {
endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>,
bind_address: String,
handler: Box<server::Handler<HttpStream> + Send>,
}
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
// Validate Host header
if !host_validation::is_valid(&req, &self.bind_address, self.endpoints.keys().cloned().collect()) {
self.handler = host_validation::host_invalid_response();
return self.handler.on_request(req);
}
// Check authorization
let auth = self.authorization.is_authorized(&req);
if let Authorized::No(handler) = auth {
self.handler = handler;
return self.handler.on_request(req);
}
// Choose proper handler depending on path / domain
let url = extract_url(&req);
let endpoint = extract_endpoint(&url);
self.handler = match auth {
Authorized::No(handler) => handler,
Authorized::Yes => {
let url = extract_url(&req);
let endpoint = extract_endpoint(&url);
self.handler = match endpoint {
// First check special endpoints
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default())
},
// Then delegate to dapp
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
},
// Redirection to main page
_ if *req.method() == hyper::method::Method::Get => {
Redirection::new(self.main_page)
},
// RPC by default
_ => {
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
match endpoint {
// First check special endpoints
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default())
},
// Then delegate to dapp
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
},
// Redirection to main page
_ if *req.method() == hyper::method::Method::Get => {
Redirection::new(self.main_page)
},
// RPC by default
_ => {
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
}
}
}
};
@@ -113,9 +111,7 @@ impl<A: Authorization> Router<A> {
main_page: &'static str,
endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>,
bind_address: String,
) -> Self {
authorization: Arc<A>) -> Self {
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
Router {
@@ -123,12 +119,37 @@ impl<A: Authorization> Router<A> {
endpoints: endpoints,
special: special,
authorization: authorization,
bind_address: bind_address,
handler: handler,
}
}
}
fn extract_url(req: &server::Request<HttpStream>) -> Option<Url> {
match *req.uri() {
uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
uri::RequestUri::AbsolutePath(ref path) => {
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match req.headers().get::<header::Host>() {
Some(ref host) => {
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
},
None => return None,
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
},
_ => None,
}
}
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 {

View File

@@ -16,14 +16,14 @@
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
use url_lib::{self};
pub use url_lib::Host;
use url::Host;
use url::{self};
/// HTTP/HTTPS URL type for Iron.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Url {
/// Raw url of url
pub raw: url_lib::Url,
pub raw: url::Url,
/// The host field of the URL, probably a domain.
pub host: Host,
@@ -62,14 +62,14 @@ impl Url {
/// See: http://url.spec.whatwg.org/#special-scheme
pub fn parse(input: &str) -> Result<Url, String> {
// Parse the string using rust-url, then convert.
match url_lib::Url::parse(input) {
match url::Url::parse(input) {
Ok(raw_url) => Url::from_generic_url(raw_url),
Err(e) => Err(format!("{}", e))
}
}
/// Create a `Url` from a `rust-url` `Url`.
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
// Map empty usernames to None.
let username = match raw_url.username() {
"" => None,

View File

@@ -23,22 +23,19 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
Box::new(RpcEndpoint {
handler: handler,
panic_handler: panic_handler,
cors_domain: Some(vec![AccessControlAllowOrigin::Null]),
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
allowed_hosts: None,
cors_domain: vec![AccessControlAllowOrigin::Null],
})
}
struct RpcEndpoint {
handler: Arc<IoHandler>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
allowed_hosts: Option<Vec<String>>,
cors_domain: Vec<AccessControlAllowOrigin>,
}
impl Endpoint for RpcEndpoint {
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), self.allowed_hosts.clone(), panic_handler))
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), panic_handler))
}
}

View File

@@ -3,7 +3,7 @@ description = "Ethcore Database"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore-db"
version = "1.3.0"
version = "1.2.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
@@ -12,7 +12,7 @@ syntex = "*"
ethcore-ipc-codegen = { path = "../ipc/codegen" }
[dependencies]
clippy = { version = "0.0.80", optional = true}
clippy = { version = "0.0.77", optional = true}
ethcore-devtools = { path = "../devtools" }
ethcore-ipc = { path = "../ipc/rpc" }
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }

View File

@@ -17,7 +17,8 @@
//! Ethcore rocksdb ipc service
use traits::*;
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator, IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator,
IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
use std::sync::{RwLock, Arc};
use std::convert::From;
use ipc::IpcConfig;
@@ -25,6 +26,12 @@ use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::{VecDeque, HashMap, BTreeMap};
impl From<String> for Error {
fn from(s: String) -> Error {
Error::RocksDb(s)
}
}
enum WriteCacheEntry {
Remove,
Write(Vec<u8>),
@@ -53,7 +60,7 @@ impl WriteCache {
self.entries.insert(key, WriteCacheEntry::Remove);
}
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
fn get(&self, key: &Vec<u8>) -> Option<Vec<u8>> {
self.entries.get(key).and_then(
|vec_ref| match vec_ref {
&WriteCacheEntry::Write(ref val) => Some(val.clone()),
@@ -130,8 +137,8 @@ impl Database {
}
pub fn flush(&self) -> Result<(), Error> {
let mut cache_lock = self.write_cache.write();
let db_lock = self.db.read();
let mut cache_lock = self.write_cache.write().unwrap();
let db_lock = self.db.read().unwrap();
if db_lock.is_none() { return Ok(()); }
let db = db_lock.as_ref().unwrap();
@@ -140,8 +147,8 @@ impl Database {
}
pub fn flush_all(&self) -> Result<(), Error> {
let mut cache_lock = self.write_cache.write();
let db_lock = self.db.read();
let mut cache_lock = self.write_cache.write().unwrap();
let db_lock = self.db.read().unwrap();
if db_lock.is_none() { return Ok(()); }
let db = db_lock.as_ref().expect("we should have exited with Ok(()) on the previous step");
@@ -160,7 +167,7 @@ impl Drop for Database {
#[derive(Ipc)]
impl DatabaseService for Database {
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
let mut db = self.db.write();
let mut db = self.db.write().unwrap();
if db.is_some() { return Err(Error::AlreadyOpen); }
let mut opts = Options::new();
@@ -187,7 +194,7 @@ impl DatabaseService for Database {
fn close(&self) -> Result<(), Error> {
try!(self.flush_all());
let mut db = self.db.write();
let mut db = self.db.write().unwrap();
if db.is_none() { return Err(Error::IsClosed); }
*db = None;
@@ -195,19 +202,19 @@ impl DatabaseService for Database {
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
let mut cache_lock = self.write_cache.write();
let mut cache_lock = self.write_cache.write().unwrap();
cache_lock.write(key.to_vec(), value.to_vec());
Ok(())
}
fn delete(&self, key: &[u8]) -> Result<(), Error> {
let mut cache_lock = self.write_cache.write();
let mut cache_lock = self.write_cache.write().unwrap();
cache_lock.remove(key.to_vec());
Ok(())
}
fn write(&self, transaction: DBTransaction) -> Result<(), Error> {
let mut cache_lock = self.write_cache.write();
let mut cache_lock = self.write_cache.write().unwrap();
let mut writes = transaction.writes.borrow_mut();
for kv in writes.drain(..) {
@@ -224,13 +231,13 @@ impl DatabaseService for Database {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
{
let key_vec = key.to_vec();
let cache_hit = self.write_cache.read().get(&key_vec);
let cache_hit = self.write_cache.read().unwrap().get(&key_vec);
if cache_hit.is_some() {
return Ok(Some(cache_hit.expect("cache_hit.is_some() = true, still there is none somehow here")))
}
}
let db_lock = self.db.read();
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
match try!(db.get(key)) {
@@ -242,7 +249,7 @@ impl DatabaseService for Database {
}
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error> {
let db_lock = self.db.read();
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
let mut iter = db.iterator(IteratorMode::From(prefix, Direction::Forward));
@@ -254,17 +261,17 @@ impl DatabaseService for Database {
}
fn is_empty(&self) -> Result<bool, Error> {
let db_lock = self.db.read();
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
Ok(db.iterator(IteratorMode::Start).next().is_none())
}
fn iter(&self) -> Result<IteratorHandle, Error> {
let db_lock = self.db.read();
let db_lock = self.db.read().unwrap();
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
let mut iterators = self.iterators.write();
let mut iterators = self.iterators.write().unwrap();
let next_iterator = iterators.keys().last().unwrap_or(&0) + 1;
iterators.insert(next_iterator, db.iterator(IteratorMode::Start));
Ok(next_iterator)
@@ -272,7 +279,7 @@ impl DatabaseService for Database {
fn iter_next(&self, handle: IteratorHandle) -> Option<KeyValue>
{
let mut iterators = self.iterators.write();
let mut iterators = self.iterators.write().unwrap();
let mut iterator = match iterators.get_mut(&handle) {
Some(some_iterator) => some_iterator,
None => { return None; },
@@ -287,7 +294,7 @@ impl DatabaseService for Database {
}
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error> {
let mut iterators = self.iterators.write();
let mut iterators = self.iterators.write().unwrap();
iterators.remove(&handle);
Ok(())
}

View File

@@ -31,8 +31,8 @@ pub struct KeyValue {
pub value: Vec<u8>,
}
#[derive(Debug, Binary)]
pub enum Error {
#[derive(Debug, Binary)]
pub enum Error {
AlreadyOpen,
IsClosed,
RocksDb(String),
@@ -41,12 +41,6 @@ pub enum Error {
UncommitedTransactions,
}
impl From<String> for Error {
fn from(s: String) -> Error {
Error::RocksDb(s)
}
}
/// Database configuration
#[derive(Binary)]
pub struct DatabaseConfig {
@@ -74,7 +68,7 @@ impl DatabaseConfig {
}
}
pub trait DatabaseService : Sized {
pub trait DatabaseService : Sized {
/// Opens database in the specified path
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;

View File

@@ -3,7 +3,7 @@ description = "Ethcore development/test/build tools"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore-devtools"
version = "1.3.0"
version = "1.2.0"
authors = ["Ethcore <admin@ethcore.io>"]
[dependencies]

View File

@@ -19,9 +19,9 @@
extern crate rand;
mod random_path;
mod test_socket;
mod stop_guard;
pub mod random_path;
pub mod test_socket;
pub mod stop_guard;
pub use random_path::*;
pub use test_socket::*;

View File

@@ -19,7 +19,6 @@
use std::path::*;
use std::fs;
use std::env;
use std::ops::{Deref, DerefMut};
use rand::random;
pub struct RandomTempPath {
@@ -75,35 +74,6 @@ impl Drop for RandomTempPath {
}
}
pub struct GuardedTempResult<T> {
pub result: Option<T>,
pub _temp: RandomTempPath
}
impl<T> GuardedTempResult<T> {
pub fn reference(&self) -> &T {
self.result.as_ref().unwrap()
}
pub fn reference_mut(&mut self) -> &mut T {
self.result.as_mut().unwrap()
}
pub fn take(&mut self) -> T {
self.result.take().unwrap()
}
}
impl<T> Deref for GuardedTempResult<T> {
type Target = T;
fn deref(&self) -> &T { self.result.as_ref().unwrap() }
}
impl<T> DerefMut for GuardedTempResult<T> {
fn deref_mut(&mut self) -> &mut T { self.result.as_mut().unwrap() }
}
#[test]
fn creates_dir() {
let temp = RandomTempPath::create_dir();

View File

@@ -62,7 +62,6 @@ impl TestSocket {
impl Read for TestSocket {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len());
if self.cursor > end_position { return Ok(0) }
let len = cmp::max(end_position - self.cursor, 0);
match len {
0 => Ok(0),
@@ -70,7 +69,7 @@ impl Read for TestSocket {
for i in self.cursor..end_position {
buf[i-self.cursor] = self.read_buffer[i];
}
self.cursor = end_position;
self.cursor = self.cursor + buf.len();
Ok(len)
}
}

14
doc.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# generate documentation only for partiy and ethcore libraries
cargo doc --no-deps --verbose \
-p ethkey \
-p ethstore \
-p ethash \
-p ethcore-util \
-p ethcore \
-p ethsync \
-p ethcore-rpc \
-p ethcore-signer \
-p ethcore-dapps \
-p parity \

View File

@@ -32,5 +32,4 @@ RUN git clone https://github.com/ethcore/parity && \
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity
ENTRYPOINT ["/build/parity/target/release/parity"]
RUN file /build/parity/target/release/parity

View File

@@ -1,6 +1,6 @@
[package]
name = "ethash"
version = "1.3.0"
version = "1.2.0"
authors = ["arkpar <arkadiy@ethcore.io"]
[lib]
@@ -9,4 +9,3 @@ authors = ["arkpar <arkadiy@ethcore.io"]
log = "0.3"
sha3 = { path = "../util/sha3" }
primal = "0.2.3"
parking_lot = "0.2.6"

View File

@@ -21,6 +21,7 @@
use primal::is_prime;
use std::cell::Cell;
use std::sync::Mutex;
use std::mem;
use std::ptr;
use sha3;
@@ -29,8 +30,6 @@ use std::path::PathBuf;
use std::io::{self, Read, Write};
use std::fs::{self, File};
use parking_lot::Mutex;
pub const ETHASH_EPOCH_LENGTH: u64 = 30000;
pub const ETHASH_CACHE_ROUNDS: usize = 3;
pub const ETHASH_MIX_BYTES: usize = 128;
@@ -135,7 +134,7 @@ impl Light {
}
pub fn to_file(&self) -> io::Result<()> {
let seed_compute = self.seed_compute.lock();
let seed_compute = self.seed_compute.lock().unwrap();
let path = Light::file_path(seed_compute.get_seedhash(self.block_number));
try!(fs::create_dir_all(path.parent().unwrap()));
let mut file = try!(File::create(path));
@@ -271,12 +270,11 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
let page_size = 4 * MIX_WORDS;
let num_full_pages = (full_size / page_size) as u32;
let cache: &[Node] = &light.cache; // deref once for better performance
for i in 0..(ETHASH_ACCESSES as u32) {
let index = fnv_hash(f_mix.get_unchecked(0).as_words().get_unchecked(0) ^ i, *mix.get_unchecked(0).as_words().get_unchecked((i as usize) % MIX_WORDS)) % num_full_pages;
for n in 0..MIX_NODES {
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, cache);
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, light);
for w in 0..NODE_WORDS {
*mix.get_unchecked_mut(n).as_words_mut().get_unchecked_mut(w) = fnv_hash(*mix.get_unchecked(n).as_words().get_unchecked(w), *tmp_node.as_words().get_unchecked(w));
}
@@ -307,17 +305,18 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
}
}
fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
fn calculate_dag_item(node_index: u32, light: &Light) -> Node {
unsafe {
let num_parent_nodes = cache.len();
let init = cache.get_unchecked(node_index as usize % num_parent_nodes);
let num_parent_nodes = light.cache.len();
let cache_nodes = &light.cache;
let init = cache_nodes.get_unchecked(node_index as usize % num_parent_nodes);
let mut ret = init.clone();
*ret.as_words_mut().get_unchecked_mut(0) ^= node_index;
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
for i in 0..ETHASH_DATASET_PARENTS {
let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32;
let parent = cache.get_unchecked(parent_index as usize);
let parent = cache_nodes.get_unchecked(parent_index as usize);
for w in 0..NODE_WORDS {
*ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w));
}

View File

@@ -18,8 +18,6 @@
//! See https://github.com/ethereum/wiki/wiki/Ethash
extern crate primal;
extern crate sha3;
extern crate parking_lot;
#[macro_use]
extern crate log;
mod compute;
@@ -28,8 +26,7 @@ use std::mem;
use compute::Light;
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty};
use std::sync::Arc;
use parking_lot::Mutex;
use std::sync::{Arc, Mutex};
struct LightCache {
recent_epoch: Option<u64>,
@@ -64,7 +61,7 @@ impl EthashManager {
pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork {
let epoch = block_number / ETHASH_EPOCH_LENGTH;
let light = {
let mut lights = self.cache.lock();
let mut lights = self.cache.lock().unwrap();
let light = match lights.recent_epoch.clone() {
Some(ref e) if *e == epoch => lights.recent.clone(),
_ => match lights.prev_epoch.clone() {
@@ -111,12 +108,12 @@ fn test_lru() {
let hash = [0u8; 32];
ethash.compute_light(1, &hash, 1);
ethash.compute_light(50000, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 0);
ethash.compute_light(1, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 1);
ethash.compute_light(70000, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 2);
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 0);
}

View File

@@ -3,11 +3,12 @@ description = "Ethcore library"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore"
version = "1.3.0"
version = "1.2.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
syntex = "*"
"ethcore-ipc-codegen" = { path = "../ipc/codegen" }
[dependencies]
@@ -16,25 +17,20 @@ env_logger = "0.3"
rustc-serialize = "0.3"
heapsize = "0.3"
rust-crypto = "0.2.34"
time = "0.1"
ethcore-util = { path = "../util" }
evmjit = { path = "../evmjit", optional = true }
ethash = { path = "../ethash" }
num_cpus = "0.2"
clippy = { version = "0.0.77", optional = true}
crossbeam = "0.2.9"
lazy_static = "0.2"
bloomchain = "0.1"
rayon = "0.3.1"
semver = "0.2"
bit-set = "0.4"
time = "0.1"
evmjit = { path = "../evmjit", optional = true }
clippy = { version = "0.0.80", optional = true}
ethash = { path = "../ethash" }
ethcore-util = { path = "../util" }
ethcore-io = { path = "../util/io" }
ethcore-devtools = { path = "../devtools" }
ethjson = { path = "../json" }
ethcore-ipc = { path = "../ipc/rpc" }
bloomchain = "0.1"
"ethcore-ipc" = { path = "../ipc/rpc" }
rayon = "0.3.1"
ethstore = { path = "../ethstore" }
ethcore-ipc-nano = { path = "../ipc/nano" }
rand = "0.3"
[dependencies.hyper]
git = "https://github.com/ethcore/hyper"
@@ -47,5 +43,3 @@ json-tests = []
test-heavy = []
dev = ["clippy"]
default = []
benches = []
ipc = []

View File

@@ -14,10 +14,20 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate ethcore_ipc_codegen;
extern crate syntex;
extern crate ethcore_ipc_codegen as codegen;
use std::env;
use std::path::Path;
fn main() {
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
ethcore_ipc_codegen::derive_ipc("src/client/traits.rs").unwrap();
ethcore_ipc_codegen::derive_ipc("src/client/chain_notify.rs").unwrap();
let out_dir = env::var_os("OUT_DIR").unwrap();
// serialization pass
{
let src = Path::new("src/types/mod.rs.in");
let dst = Path::new(&out_dir).join("types.rs");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
}

View File

@@ -1,6 +1,5 @@
{
"name": "Ethereum Classic",
"forkName": "classic",
"name": "Frontier/Homestead",
"engine": {
"Ethash": {
"params": {
@@ -18,9 +17,7 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
"networkID" : "0x1"
},
"genesis": {
"seal": {
@@ -38,10 +35,10 @@
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
},
"nodes": [
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303"
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
"enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io:30303"
],
"accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -137,9 +137,7 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
"networkID" : "0x1"
},
"genesis": {
"seal": {

View File

@@ -1,33 +0,0 @@
{
"name": "TestInstantSeal",
"engine": {
"InstantSeal": null
},
"params": {
"accountStartNonce": "0x0100000",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x00006d6f7264656e",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
}
},
"difficulty": "0x20000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
}
}

View File

@@ -4,7 +4,7 @@
"Null": null
},
"params": {
"accountStartNonce": "0x0",
"accountStartNonce": "0x0100000",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2"

View File

@@ -20,8 +20,6 @@ use util::*;
use pod_account::*;
use account_db::*;
use std::cell::{Ref, RefCell};
/// Single account in the system.
#[derive(Clone)]
pub struct Account {
@@ -37,8 +35,6 @@ pub struct Account {
code_hash: Option<H256>,
// Code cache of the account.
code_cache: Bytes,
// Account is new or has been modified
filth: Filth,
}
impl Account {
@@ -51,8 +47,7 @@ impl Account {
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(code.sha3()),
code_cache: code,
filth: Filth::Dirty,
code_cache: code
}
}
@@ -63,9 +58,8 @@ impl Account {
nonce: pod.nonce,
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
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()),
filth: Filth::Dirty,
code_hash: Some(pod.code.sha3()),
code_cache: pod.code
}
}
@@ -78,7 +72,6 @@ impl Account {
storage_overlay: RefCell::new(HashMap::new()),
code_hash: Some(SHA3_EMPTY),
code_cache: vec![],
filth: Filth::Dirty,
}
}
@@ -92,7 +85,6 @@ impl Account {
storage_overlay: RefCell::new(HashMap::new()),
code_hash: Some(r.val_at(3)),
code_cache: vec![],
filth: Filth::Clean,
}
}
@@ -106,7 +98,6 @@ impl Account {
storage_overlay: RefCell::new(HashMap::new()),
code_hash: None,
code_cache: vec![],
filth: Filth::Dirty,
}
}
@@ -115,7 +106,6 @@ impl Account {
pub fn init_code(&mut self, code: Bytes) {
assert!(self.code_hash.is_none());
self.code_cache = code;
self.filth = Filth::Dirty;
}
/// Reset this account's code to the given code.
@@ -127,7 +117,6 @@ impl Account {
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
pub fn set_storage(&mut self, key: H256, value: H256) {
self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
self.filth = Filth::Dirty;
}
/// Get (and cache) the contents of the trie's storage at `key`.
@@ -138,11 +127,7 @@ impl Account {
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){
Ok(x) => x.map_or_else(U256::zero, decode),
Err(e) => panic!("Encountered potential DB corruption: {}", e),
};
(Filth::Clean, item.into())
(Filth::Clean, H256::from(db.get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)})))
}).1.clone()
}
@@ -187,16 +172,6 @@ impl Account {
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY))
}
/// Is this a new or modified account?
pub fn is_dirty(&self) -> bool {
self.filth == Filth::Dirty
}
/// Mark account as clean.
pub fn set_clean(&mut self) {
self.filth = Filth::Clean
}
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
// TODO: fill out self.code_cache;
@@ -226,28 +201,21 @@ impl Account {
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
/// Increment the nonce of the account by one.
pub fn inc_nonce(&mut self) {
self.nonce = self.nonce + U256::from(1u8);
self.filth = Filth::Dirty;
}
pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); }
/// Increment the nonce of the account by one.
pub fn add_balance(&mut self, x: &U256) {
self.balance = self.balance + *x;
self.filth = Filth::Dirty;
}
pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; }
/// Increment the nonce of the account by one.
/// Panics if balance is less than `x`
pub fn sub_balance(&mut self, x: &U256) {
assert!(self.balance >= *x);
self.balance = self.balance - *x;
self.filth = Filth::Dirty;
}
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
pub fn commit_storage(&mut self, db: &mut AccountDBMut) {
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root)
.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.");
@@ -255,13 +223,9 @@ impl Account {
if f == &Filth::Dirty {
// cast key and value to trait type,
// so we can call overloaded `to_bytes` method
let res = match v.is_zero() {
true => t.remove(k),
false => t.insert(k, &encode(&U256::from(v.as_slice()))),
};
if let Err(e) = res {
warn!("Encountered potential DB corruption: {}", e);
match v.is_zero() {
true => { t.remove(k); },
false => { t.insert(k, &encode(&U256::from(v.as_slice()))); },
}
*f = Filth::Clean;
}
@@ -304,16 +268,6 @@ mod tests {
use super::*;
use account_db::*;
#[test]
fn account_compress() {
let raw = Account::new_basic(2.into(), 4.into()).rlp();
let rlp = UntrustedRlp::new(&raw);
let compact_vec = rlp.compress(RlpType::Snapshot).to_vec();
assert!(raw.len() > compact_vec.len());
let again_raw = UntrustedRlp::new(&compact_vec).decompress(RlpType::Snapshot);
assert_eq!(raw, again_raw.to_vec());
}
#[test]
fn storage_at() {
let mut db = MemoryDB::new();
@@ -321,7 +275,7 @@ mod tests {
let rlp = {
let mut a = Account::new_contract(69.into(), 0.into());
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
a.commit_storage(&Default::default(), &mut db);
a.commit_storage(&mut db);
a.init_code(vec![]);
a.commit_code(&mut db);
a.rlp()
@@ -359,7 +313,7 @@ mod tests {
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(0.into(), 0x1234.into());
assert_eq!(a.storage_root(), None);
a.commit_storage(&Default::default(), &mut db);
a.commit_storage(&mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}
@@ -369,11 +323,11 @@ mod tests {
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(0.into(), 0x1234.into());
a.commit_storage(&Default::default(), &mut db);
a.commit_storage(&mut db);
a.set_storage(1.into(), 0x1234.into());
a.commit_storage(&Default::default(), &mut db);
a.commit_storage(&mut db);
a.set_storage(1.into(), 0.into());
a.commit_storage(&Default::default(), &mut db);
a.commit_storage(&mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}

View File

@@ -1,59 +1,26 @@
// 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/>.
//! DB backend wrapper for Account trie
use util::*;
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
// combines a key with an address hash to ensure uniqueness.
// leaves the first 96 bits untouched in order to support partial key lookup.
#[inline]
fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
let mut dst = key.clone();
{
let last_src: &[u8] = &*address_hash;
let last_dst: &mut [u8] = &mut *dst;
for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) {
*k ^= *a
}
}
dst
}
// TODO: introduce HashDBMut?
/// DB backend wrapper for Account trie
/// Transforms trie node keys for the database
pub struct AccountDB<'db> {
db: &'db HashDB,
address_hash: H256,
address: H256,
}
#[inline]
fn combine_key<'a>(address: &'a H256, key: &'a H256) -> H256 {
address ^ key
}
impl<'db> AccountDB<'db> {
/// Create a new AccountDB from an address.
pub fn new(db: &'db HashDB, address: &Address) -> Self {
Self::from_hash(db, address.sha3())
}
/// Create a new AcountDB from an address' hash.
pub fn from_hash(db: &'db HashDB, address_hash: H256) -> Self {
pub fn new(db: &'db HashDB, address: &Address) -> AccountDB<'db> {
AccountDB {
db: db,
address_hash: address_hash,
address: address.into(),
}
}
}
@@ -67,14 +34,14 @@ impl<'db> HashDB for AccountDB<'db>{
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
self.db.get(&combine_key(&self.address_hash, key))
self.db.get(&combine_key(&self.address, key))
}
fn contains(&self, key: &H256) -> bool {
if key == &SHA3_NULL_RLP {
return true;
}
self.db.contains(&combine_key(&self.address_hash, key))
self.db.contains(&combine_key(&self.address, key))
}
fn insert(&mut self, _value: &[u8]) -> H256 {
@@ -93,26 +60,20 @@ impl<'db> HashDB for AccountDB<'db>{
/// DB backend wrapper for Account trie
pub struct AccountDBMut<'db> {
db: &'db mut HashDB,
address_hash: H256,
address: H256,
}
impl<'db> AccountDBMut<'db> {
/// Create a new AccountDB from an address.
pub fn new(db: &'db mut HashDB, address: &Address) -> Self {
Self::from_hash(db, address.sha3())
}
/// Create a new AcountDB from an address' hash.
pub fn from_hash(db: &'db mut HashDB, address_hash: H256) -> Self {
pub fn new(db: &'db mut HashDB, address: &Address) -> AccountDBMut<'db> {
AccountDBMut {
db: db,
address_hash: address_hash,
address: address.into(),
}
}
#[allow(dead_code)]
pub fn immutable(&'db self) -> AccountDB<'db> {
AccountDB { db: self.db, address_hash: self.address_hash.clone() }
AccountDB { db: self.db, address: self.address.clone() }
}
}
@@ -125,14 +86,14 @@ impl<'db> HashDB for AccountDBMut<'db>{
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
self.db.get(&combine_key(&self.address_hash, key))
self.db.get(&combine_key(&self.address, key))
}
fn contains(&self, key: &H256) -> bool {
if key == &SHA3_NULL_RLP {
return true;
}
self.db.contains(&combine_key(&self.address_hash, key))
self.db.contains(&combine_key(&self.address, key))
}
fn insert(&mut self, value: &[u8]) -> H256 {
@@ -140,7 +101,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
return SHA3_NULL_RLP.clone();
}
let k = value.sha3();
let ak = combine_key(&self.address_hash, &k);
let ak = combine_key(&self.address, &k);
self.db.emplace(ak, value.to_vec());
k
}
@@ -149,7 +110,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
if key == SHA3_NULL_RLP {
return;
}
let key = combine_key(&self.address_hash, &key);
let key = combine_key(&self.address, &key);
self.db.emplace(key, value.to_vec())
}
@@ -157,7 +118,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
if key == &SHA3_NULL_RLP {
return;
}
let key = combine_key(&self.address_hash, key);
let key = combine_key(&self.address, key);
self.db.remove(&key)
}
}

View File

@@ -17,14 +17,13 @@
//! Account management.
use std::fmt;
use std::sync::RwLock;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use util::{Address as H160, H256, H520, Mutex, RwLock};
use util::{Address as H160, H256, H520};
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
use ethstore::dir::{KeyDirectory};
use ethstore::ethkey::{Address as SSAddress, Message as SSMessage, Secret as SSSecret, Random, Generator};
/// Type of unlock.
#[derive(Clone)]
enum Unlock {
@@ -33,8 +32,6 @@ enum Unlock {
/// Account unlocked permantently can always sign message.
/// Use with caution.
Perm,
/// Account unlocked with a timeout
Timed((Instant, u32)),
}
/// Data associated with account.
@@ -110,23 +107,18 @@ impl_bridge_type!(Message, 32, H256, SSMessage);
impl_bridge_type!(Address, 20, H160, SSAddress);
#[derive(Default)]
struct NullDir {
accounts: RwLock<HashMap<SSAddress, SafeAccount>>,
}
struct NullDir;
impl KeyDirectory for NullDir {
fn load(&self) -> Result<Vec<SafeAccount>, SSError> {
Ok(self.accounts.read().values().cloned().collect())
Ok(vec![])
}
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, SSError> {
self.accounts.write().insert(account.address.clone(), account.clone());
Ok(account)
fn insert(&self, _account: SafeAccount) -> Result<(), SSError> {
Ok(())
}
fn remove(&self, address: &SSAddress) -> Result<(), SSError> {
self.accounts.write().remove(address);
fn remove(&self, _address: &SSAddress) -> Result<(), SSError> {
Ok(())
}
}
@@ -134,36 +126,15 @@ impl KeyDirectory for NullDir {
/// Account management.
/// Responsible for unlocking accounts.
pub struct AccountProvider {
unlocked: Mutex<HashMap<SSAddress, AccountData>>,
unlocked: RwLock<HashMap<SSAddress, AccountData>>,
sstore: Box<SecretStore>,
}
/// Collected account metadata
#[derive(Clone, Debug, PartialEq)]
pub struct AccountMeta {
/// The name of the account.
pub name: String,
/// The rest of the metadata of the account.
pub meta: String,
/// The 128-bit UUID of the account, if it has one (brain-wallets don't).
pub uuid: Option<String>,
}
impl Default for AccountMeta {
fn default() -> Self {
AccountMeta {
name: String::new(),
meta: "{}".to_owned(),
uuid: None,
}
}
}
impl AccountProvider {
/// Creates new account provider.
pub fn new(sstore: Box<SecretStore>) -> Self {
AccountProvider {
unlocked: Mutex::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
sstore: sstore,
}
}
@@ -171,8 +142,8 @@ impl AccountProvider {
/// Creates not disk backed provider.
pub fn transient_provider() -> Self {
AccountProvider {
unlocked: Mutex::new(HashMap::new()),
sstore: Box::new(EthStore::open(Box::new(NullDir::default())).unwrap())
unlocked: RwLock::new(HashMap::new()),
sstore: Box::new(EthStore::open(Box::new(NullDir)).unwrap())
}
}
@@ -192,42 +163,8 @@ impl AccountProvider {
}
/// Returns addresses of all accounts.
pub fn accounts(&self) -> Result<Vec<H160>, Error> {
let accounts = try!(self.sstore.accounts()).into_iter().map(|a| H160(a.into())).collect();
Ok(accounts)
}
/// Returns each account along with name and meta.
pub fn accounts_info(&self) -> Result<HashMap<H160, AccountMeta>, Error> {
let r: HashMap<H160, AccountMeta> = try!(self.sstore.accounts())
.into_iter()
.map(|a| (H160(a.clone().into()), self.account_meta(a).unwrap_or_else(|_| Default::default())))
.collect();
Ok(r)
}
/// Returns each account along with name and meta.
pub fn account_meta<A>(&self, account: A) -> Result<AccountMeta, Error> where Address: From<A> {
let account = Address::from(account).into();
Ok(AccountMeta {
name: try!(self.sstore.name(&account)),
meta: try!(self.sstore.meta(&account)),
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a UUID
})
}
/// Returns each account along with name and meta.
pub fn set_account_name<A>(&self, account: A, name: String) -> Result<(), Error> where Address: From<A> {
let account = Address::from(account).into();
try!(self.sstore.set_name(&account, name));
Ok(())
}
/// Returns each account along with name and meta.
pub fn set_account_meta<A>(&self, account: A, meta: String) -> Result<(), Error> where Address: From<A> {
let account = Address::from(account).into();
try!(self.sstore.set_meta(&account, meta));
Ok(())
pub fn accounts(&self) -> Vec<H160> {
self.sstore.accounts().into_iter().map(|a| H160(a.into())).collect()
}
/// Helper method used for unlocking accounts.
@@ -239,10 +176,12 @@ impl AccountProvider {
let _ = try!(self.sstore.sign(&account, &password, &Default::default()));
// check if account is already unlocked pernamently, if it is, do nothing
let mut unlocked = self.unlocked.lock();
if let Some(data) = unlocked.get(&account) {
if let Unlock::Perm = data.unlock {
return Ok(())
{
let unlocked = self.unlocked.read().unwrap();
if let Some(data) = unlocked.get(&account) {
if let Unlock::Perm = data.unlock {
return Ok(())
}
}
}
@@ -251,6 +190,7 @@ impl AccountProvider {
password: password,
};
let mut unlocked = self.unlocked.write().unwrap();
unlocked.insert(account, data);
Ok(())
}
@@ -265,15 +205,10 @@ impl AccountProvider {
self.unlock_account(account, password, Unlock::Temp)
}
/// Unlocks account temporarily with a timeout.
pub fn unlock_account_timed<A>(&self, account: A, password: String, duration_ms: u32) -> Result<(), Error> where Address: From<A> {
self.unlock_account(account, password, Unlock::Timed((Instant::now(), duration_ms)))
}
/// Checks if given account is unlocked
pub fn is_unlocked<A>(&self, account: A) -> bool where Address: From<A> {
let account = Address::from(account).into();
let unlocked = self.unlocked.lock();
let unlocked = self.unlocked.read().unwrap();
unlocked.get(&account).is_some()
}
@@ -283,20 +218,15 @@ impl AccountProvider {
let message = Message::from(message).into();
let data = {
let mut unlocked = self.unlocked.lock();
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
if let Unlock::Temp = data.unlock {
unlocked.remove(&account).expect("data exists: so key must exist: qed");
}
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
if start.elapsed() > Duration::from_millis(*duration as u64) {
unlocked.remove(&account).expect("data exists: so key must exist: qed");
return Err(Error::NotUnlocked);
}
}
data
let unlocked = self.unlocked.read().unwrap();
try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone()
};
if let Unlock::Temp = data.unlock {
let mut unlocked = self.unlocked.write().unwrap();
unlocked.remove(&account).expect("data exists: so key must exist: qed");
}
let signature = try!(self.sstore.sign(&account, &data.password, &message));
Ok(H520(signature.into()))
}
@@ -314,7 +244,6 @@ impl AccountProvider {
mod tests {
use super::AccountProvider;
use ethstore::ethkey::{Generator, Random};
use std::time::Duration;
#[test]
fn unlock_account_temp() {
@@ -340,16 +269,4 @@ mod tests {
assert!(ap.sign(kp.address(), [0u8; 32]).is_ok());
assert!(ap.sign(kp.address(), [0u8; 32]).is_ok());
}
#[test]
fn unlock_account_timer() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
assert!(ap.sign(kp.address(), [0u8; 32]).is_ok());
::std::thread::sleep(Duration::from_millis(2000));
assert!(ap.sign(kp.address(), [0u8; 32]).is_err());
}
}

View File

@@ -17,7 +17,6 @@
//! Evm input params.
use common::*;
use ethjson;
use types::executed::CallType;
/// Transaction value
#[derive(Clone, Debug)]
@@ -59,10 +58,7 @@ pub struct ActionParams {
/// Code being executed.
pub code: Option<Bytes>,
/// Input data.
pub data: Option<Bytes>,
/// Type of call
pub call_type: CallType,
pub data: Option<Bytes>
}
impl Default for ActionParams {
@@ -77,18 +73,16 @@ impl Default for ActionParams {
gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()),
code: None,
data: None,
call_type: CallType::None,
data: None
}
}
}
impl From<ethjson::vm::Transaction> for ActionParams {
fn from(t: ethjson::vm::Transaction) -> Self {
let address: Address = t.address.into();
ActionParams {
code_address: Address::new(),
address: address,
address: t.address.into(),
sender: t.sender.into(),
origin: t.origin.into(),
code: Some(t.code.into()),
@@ -96,7 +90,6 @@ impl From<ethjson::vm::Transaction> for ActionParams {
gas: t.gas.into(),
gas_price: t.gas_price.into(),
value: ActionValue::Transfer(t.value.into()),
call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct?
}
}
}

View File

@@ -19,8 +19,8 @@
use common::*;
use account_provider::AccountProvider;
use block::*;
use spec::CommonParams;
use engines::Engine;
use spec::{CommonParams, Spec};
use engine::*;
use evm::Schedule;
use ethjson;
@@ -176,16 +176,16 @@ impl Header {
}
}
/// Create a new test chain spec with `BasicAuthority` consensus engine.
pub fn new_test_authority() -> Spec { Spec::load(include_bytes!("../res/test_authority.json")) }
#[cfg(test)]
mod tests {
use super::*;
use common::*;
use block::*;
use tests::helpers::*;
use account_provider::AccountProvider;
use spec::Spec;
/// Create a new test chain spec with `BasicAuthority` consensus engine.
fn new_test_authority() -> Spec { Spec::load(include_bytes!("../../res/test_authority.json")) }
#[test]
fn has_valid_metadata() {
@@ -202,7 +202,7 @@ mod tests {
author: 0.into(),
timestamp: 0,
difficulty: 0.into(),
last_hashes: Arc::new(vec![]),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
});
@@ -246,16 +246,16 @@ mod tests {
tap.unlock_account_permanently(addr, "".into()).unwrap();
let spec = new_test_authority();
let engine = &*spec.engine;
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine, seal).is_ok());
assert!(b.try_seal(engine.deref(), seal).is_ok());
}
}

View File

@@ -16,15 +16,17 @@
//! Blockchain block.
#![cfg_attr(feature="dev", allow(ptr_arg))] // Because of &LastHashes -> &Vec<_>
use common::*;
use engines::Engine;
use engine::*;
use state::*;
use verification::PreverifiedBlock;
use trace::FlatTrace;
use trace::Trace;
use evm::Factory as EvmFactory;
/// A block, encoded as it is on the block chain.
#[derive(Default, Debug, Clone, PartialEq)]
#[derive(Default, Debug, Clone)]
pub struct Block {
/// The header of this block.
pub header: Header,
@@ -40,7 +42,7 @@ impl Block {
UntrustedRlp::new(b).as_val::<Block>().is_ok()
}
/// Get the RLP-encoding of the block with or without the seal.
/// Get the RLP-encoding of the block without the seal.
pub fn rlp_bytes(&self, seal: Seal) -> Bytes {
let mut block_rlp = RlpStream::new_list(3);
self.header.stream_rlp(&mut block_rlp, seal);
@@ -76,7 +78,7 @@ pub struct ExecutedBlock {
receipts: Vec<Receipt>,
transactions_set: HashSet<H256>,
state: State,
traces: Option<Vec<Vec<FlatTrace>>>,
traces: Option<Vec<Trace>>,
}
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
@@ -84,15 +86,15 @@ pub struct BlockRefMut<'a> {
/// Block header.
pub header: &'a mut Header,
/// Block transactions.
pub transactions: &'a [SignedTransaction],
pub transactions: &'a Vec<SignedTransaction>,
/// Block uncles.
pub uncles: &'a [Header],
pub uncles: &'a Vec<Header>,
/// Transaction receipts.
pub receipts: &'a [Receipt],
pub receipts: &'a Vec<Receipt>,
/// State.
pub state: &'a mut State,
/// Traces.
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
pub traces: &'a Option<Vec<Trace>>,
}
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
@@ -100,15 +102,15 @@ pub struct BlockRef<'a> {
/// Block header.
pub header: &'a Header,
/// Block transactions.
pub transactions: &'a [SignedTransaction],
pub transactions: &'a Vec<SignedTransaction>,
/// Block uncles.
pub uncles: &'a [Header],
pub uncles: &'a Vec<Header>,
/// Transaction receipts.
pub receipts: &'a [Receipt],
pub receipts: &'a Vec<Receipt>,
/// State.
pub state: &'a State,
/// Traces.
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
pub traces: &'a Option<Vec<Trace>>,
}
impl ExecutedBlock {
@@ -163,16 +165,16 @@ pub trait IsBlock {
fn state(&self) -> &State { &self.block().state }
/// Get all information on transactions in this block.
fn transactions(&self) -> &[SignedTransaction] { &self.block().base.transactions }
fn transactions(&self) -> &Vec<SignedTransaction> { &self.block().base.transactions }
/// Get all information on receipts in this block.
fn receipts(&self) -> &[Receipt] { &self.block().receipts }
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
/// Get all information concerning transaction tracing in this block.
fn traces(&self) -> &Option<Vec<Vec<FlatTrace>>> { &self.block().traces }
fn traces(&self) -> &Option<Vec<Trace>> { &self.block().traces }
/// Get all uncles in this block.
fn uncles(&self) -> &[Header] { &self.block().base.uncles }
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
}
/// Trait for a object that has a state database.
@@ -193,7 +195,7 @@ pub struct OpenBlock<'x> {
block: ExecutedBlock,
engine: &'x Engine,
vm_factory: &'x EvmFactory,
last_hashes: Arc<LastHashes>,
last_hashes: LastHashes,
}
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
@@ -204,7 +206,7 @@ pub struct OpenBlock<'x> {
pub struct ClosedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: Arc<LastHashes>,
last_hashes: LastHashes,
unclosed_state: State,
}
@@ -231,16 +233,15 @@ impl<'x> OpenBlock<'x> {
pub fn new(
engine: &'x Engine,
vm_factory: &'x EvmFactory,
trie_factory: TrieFactory,
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: Arc<LastHashes>,
last_hashes: LastHashes,
author: Address,
gas_range_target: (U256, U256),
extra_data: Bytes,
) -> Result<Self, Error> {
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory));
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()));
let mut r = OpenBlock {
block: ExecutedBlock::new(state, tracing),
engine: engine,
@@ -275,15 +276,6 @@ impl<'x> OpenBlock<'x> {
/// Alter the gas limit for the block.
pub fn set_gas_used(&mut self, a: U256) { self.block.base.header.set_gas_used(a); }
/// Alter the uncles hash the block.
pub fn set_uncles_hash(&mut self, h: H256) { self.block.base.header.set_uncles_hash(h); }
/// Alter transactions root for the block.
pub fn set_transactions_root(&mut self, h: H256) { self.block.base.header.set_transactions_root(h); }
/// Alter the receipts root for the block.
pub fn set_receipts_root(&mut self, h: H256) { self.block.base.header.set_receipts_root(h); }
/// Alter the extra_data for the block.
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
if extra_data.len() > self.engine.maximum_extra_data_size() {
@@ -316,7 +308,7 @@ impl<'x> OpenBlock<'x> {
author: self.block.base.header.author.clone(),
timestamp: self.block.base.header.timestamp,
difficulty: self.block.base.header.difficulty.clone(),
last_hashes: self.last_hashes.clone(),
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(),
}
@@ -337,9 +329,9 @@ impl<'x> OpenBlock<'x> {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.base.transactions.push(t);
let t = outcome.trace;
self.block.traces.as_mut().map(|traces| traces.push(t));
self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed")));
self.block.receipts.push(outcome.receipt);
Ok(self.block.receipts.last().unwrap())
Ok(&self.block.receipts.last().unwrap())
}
Err(x) => Err(From::from(x))
}
@@ -374,17 +366,11 @@ impl<'x> OpenBlock<'x> {
let mut s = self;
s.engine.on_close_block(&mut s.block);
if s.block.base.header.transactions_root.is_zero() || s.block.base.header.transactions_root == SHA3_NULL_RLP {
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
}
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
if s.block.base.header.uncles_hash.is_zero() {
s.block.base.header.uncles_hash = uncle_bytes.sha3();
}
if s.block.base.header.receipts_root.is_zero() || s.block.base.header.receipts_root == SHA3_NULL_RLP {
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|ref r| r.rlp_bytes().to_vec()).collect());
}
s.block.base.header.uncles_hash = uncle_bytes.sha3();
s.block.base.header.state_root = s.block.state.root().clone();
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|ref r| r.rlp_bytes().to_vec()).collect());
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
s.block.base.header.note_dirty();
@@ -498,26 +484,22 @@ pub fn enact(
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
last_hashes: LastHashes,
vm_factory: &EvmFactory
) -> Result<LockedBlock, Error> {
{
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()));
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
}
}
let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![]));
let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![]));
b.set_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp());
b.set_author(header.author().clone());
b.set_extra_data(header.extra_data().clone()).unwrap_or_else(|e| warn!("Couldn't set extradata: {}. Ignoring.", e));
b.set_uncles_hash(header.uncles_hash().clone());
b.set_transactions_root(header.transactions_root().clone());
b.set_receipts_root(header.receipts_root().clone());
for t in transactions { try!(b.push_transaction(t.clone(), None)); }
for u in uncles { try!(b.push_uncle(u.clone())); }
Ok(b.close_and_lock())
@@ -531,13 +513,12 @@ pub fn enact_bytes(
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
last_hashes: LastHashes,
vm_factory: &EvmFactory
) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
@@ -548,12 +529,11 @@ pub fn enact_verified(
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
last_hashes: LastHashes,
vm_factory: &EvmFactory
) -> Result<LockedBlock, Error> {
let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
@@ -564,12 +544,11 @@ pub fn enact_and_seal(
tracing: bool,
db: Box<JournalDB>,
parent: &Header,
last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory,
trie_factory: TrieFactory,
last_hashes: LastHashes,
vm_factory: &EvmFactory
) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal())))
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory)).seal(engine, header.seal())))
}
#[cfg(test)]
@@ -582,38 +561,38 @@ mod tests {
fn open_block() {
use spec::*;
let spec = Spec::new_test();
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
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 = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
let _ = b.seal(&*spec.engine, vec![]);
let _ = b.seal(engine.deref(), vec![]);
}
#[test]
fn enact_block() {
use spec::*;
let spec = Spec::new_test();
let engine = &*spec.engine;
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
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, vec![]).unwrap();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
.close_and_lock().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default()).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes);
@@ -626,30 +605,29 @@ mod tests {
fn enact_block_with_uncle() {
use spec::*;
let spec = Spec::new_test();
let engine = &*spec.engine;
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
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 open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new();
uncle2_header.extra_data = b"uncle2".to_vec();
open_block.push_uncle(uncle1_header).unwrap();
open_block.push_uncle(uncle2_header).unwrap();
let b = open_block.close_and_lock().seal(engine, vec![]).unwrap();
let b = open_block.close_and_lock().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default()).unwrap();
let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes);

View File

@@ -18,18 +18,15 @@
//! Sorts them ready for blockchain insertion.
use std::thread::{JoinHandle, self};
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::{Condvar as SCondvar, Mutex as SMutex};
use util::*;
use io::*;
use verification::*;
use error::*;
use engines::Engine;
use engine::Engine;
use views::*;
use header::*;
use service::*;
use client::BlockStatus;
pub use types::block_queue_info::BlockQueueInfo;
use util::panics::*;
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
@@ -37,7 +34,7 @@ const MIN_MEM_LIMIT: usize = 16384;
const MIN_QUEUE_LIMIT: usize = 512;
/// Block queue configuration
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct BlockQueueConfig {
/// Maximum number of blocks to keep in unverified queue.
/// When the limit is reached, is_full returns true.
@@ -56,6 +53,22 @@ impl Default for BlockQueueConfig {
}
}
/// Block queue status
#[derive(Debug)]
pub struct BlockQueueInfo {
/// Number of queued blocks pending verification
pub unverified_queue_size: usize,
/// Number of verified queued blocks pending import
pub verified_queue_size: usize,
/// Number of blocks being verified
pub verifying_queue_size: usize,
/// Configured maximum number of blocks in the queue
pub max_queue_size: usize,
/// Configured maximum number of bytes to use
pub max_mem_use: usize,
/// Heap memory used in bytes
pub mem_used: usize,
}
impl BlockQueueInfo {
/// The total size of the queues.
@@ -80,13 +93,13 @@ impl BlockQueueInfo {
/// Sorts them ready for blockchain insertion.
pub struct BlockQueue {
panic_handler: Arc<PanicHandler>,
engine: Arc<Engine>,
more_to_verify: Arc<SCondvar>,
engine: Arc<Box<Engine>>,
more_to_verify: Arc<Condvar>,
verification: Arc<Verification>,
verifiers: Vec<JoinHandle<()>>,
deleting: Arc<AtomicBool>,
ready_signal: Arc<QueueSignal>,
empty: Arc<SCondvar>,
empty: Arc<Condvar>,
processing: RwLock<HashSet<H256>>,
max_queue_size: usize,
max_mem_use: usize,
@@ -105,7 +118,7 @@ struct VerifyingBlock {
struct QueueSignal {
deleting: Arc<AtomicBool>,
signalled: AtomicBool,
message_channel: IoChannel<ClientIoMessage>,
message_channel: IoChannel<NetSyncMessage>,
}
impl QueueSignal {
@@ -117,7 +130,7 @@ impl QueueSignal {
}
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
if let Err(e) = self.message_channel.send(ClientIoMessage::BlockVerified) {
if let Err(e) = self.message_channel.send(UserMessage(SyncMessage::BlockVerified)) {
debug!("Error sending BlockVerified message: {:?}", e);
}
}
@@ -134,30 +147,25 @@ struct Verification {
verified: Mutex<VecDeque<PreverifiedBlock>>,
verifying: Mutex<VecDeque<VerifyingBlock>>,
bad: Mutex<HashSet<H256>>,
more_to_verify: SMutex<()>,
empty: SMutex<()>,
}
impl BlockQueue {
/// Creates a new queue instance.
pub fn new(config: BlockQueueConfig, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>) -> BlockQueue {
pub fn new(config: BlockQueueConfig, engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
let verification = Arc::new(Verification {
unverified: Mutex::new(VecDeque::new()),
verified: Mutex::new(VecDeque::new()),
verifying: Mutex::new(VecDeque::new()),
bad: Mutex::new(HashSet::new()),
more_to_verify: SMutex::new(()),
empty: SMutex::new(()),
});
let more_to_verify = Arc::new(SCondvar::new());
let more_to_verify = Arc::new(Condvar::new());
let deleting = Arc::new(AtomicBool::new(false));
let ready_signal = Arc::new(QueueSignal {
deleting: deleting.clone(),
signalled: AtomicBool::new(false),
message_channel: message_channel
});
let empty = Arc::new(SCondvar::new());
let empty = Arc::new(Condvar::new());
let panic_handler = PanicHandler::new_in_arc();
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
@@ -196,17 +204,17 @@ impl BlockQueue {
}
}
fn verify(verification: Arc<Verification>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>) {
fn verify(verification: Arc<Verification>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<Condvar>) {
while !deleting.load(AtomicOrdering::Acquire) {
{
let mut more_to_verify = verification.more_to_verify.lock().unwrap();
let mut unverified = verification.unverified.lock().unwrap();
if verification.unverified.lock().is_empty() && verification.verifying.lock().is_empty() {
if unverified.is_empty() && verification.verifying.lock().unwrap().is_empty() {
empty.notify_all();
}
while verification.unverified.lock().is_empty() && !deleting.load(AtomicOrdering::Acquire) {
more_to_verify = wait.wait(more_to_verify).unwrap();
while unverified.is_empty() && !deleting.load(AtomicOrdering::Acquire) {
unverified = wait.wait(unverified).unwrap();
}
if deleting.load(AtomicOrdering::Acquire) {
@@ -215,20 +223,20 @@ impl BlockQueue {
}
let block = {
let mut unverified = verification.unverified.lock();
let mut unverified = verification.unverified.lock().unwrap();
if unverified.is_empty() {
continue;
}
let mut verifying = verification.verifying.lock();
let mut verifying = verification.verifying.lock().unwrap();
let block = unverified.pop_front().unwrap();
verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
block
};
let block_hash = block.header.hash();
match verify_block_unordered(block.header, block.bytes, &*engine) {
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) {
Ok(verified) => {
let mut verifying = verification.verifying.lock();
let mut verifying = verification.verifying.lock().unwrap();
for e in verifying.iter_mut() {
if e.hash == block_hash {
e.block = Some(verified);
@@ -237,16 +245,16 @@ impl BlockQueue {
}
if !verifying.is_empty() && verifying.front().unwrap().hash == block_hash {
// we're next!
let mut verified = verification.verified.lock();
let mut bad = verification.bad.lock();
let mut verified = verification.verified.lock().unwrap();
let mut bad = verification.bad.lock().unwrap();
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
ready.set();
}
},
Err(err) => {
let mut verifying = verification.verifying.lock();
let mut verified = verification.verified.lock();
let mut bad = verification.bad.lock();
let mut verifying = verification.verifying.lock().unwrap();
let mut verified = verification.verified.lock().unwrap();
let mut bad = verification.bad.lock().unwrap();
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
bad.insert(block_hash.clone());
verifying.retain(|e| e.hash != block_hash);
@@ -271,29 +279,29 @@ impl BlockQueue {
/// Clear the queue and stop verification activity.
pub fn clear(&self) {
let mut unverified = self.verification.unverified.lock();
let mut verifying = self.verification.verifying.lock();
let mut verified = self.verification.verified.lock();
let mut unverified = self.verification.unverified.lock().unwrap();
let mut verifying = self.verification.verifying.lock().unwrap();
let mut verified = self.verification.verified.lock().unwrap();
unverified.clear();
verifying.clear();
verified.clear();
self.processing.write().clear();
self.processing.write().unwrap().clear();
}
/// Wait for unverified queue to be empty
pub fn flush(&self) {
let mut lock = self.verification.empty.lock().unwrap();
while !self.verification.unverified.lock().is_empty() || !self.verification.verifying.lock().is_empty() {
lock = self.empty.wait(lock).unwrap();
let mut unverified = self.verification.unverified.lock().unwrap();
while !unverified.is_empty() || !self.verification.verifying.lock().unwrap().is_empty() {
unverified = self.empty.wait(unverified).unwrap();
}
}
/// Check if the block is currently in the queue
pub fn block_status(&self, hash: &H256) -> BlockStatus {
if self.processing.read().contains(hash) {
if self.processing.read().unwrap().contains(&hash) {
return BlockStatus::Queued;
}
if self.verification.bad.lock().contains(hash) {
if self.verification.bad.lock().unwrap().contains(&hash) {
return BlockStatus::Bad;
}
BlockStatus::Unknown
@@ -304,11 +312,11 @@ impl BlockQueue {
let header = BlockView::new(&bytes).header();
let h = header.hash();
{
if self.processing.read().contains(&h) {
if self.processing.read().unwrap().contains(&h) {
return Err(ImportError::AlreadyQueued.into());
}
let mut bad = self.verification.bad.lock();
let mut bad = self.verification.bad.lock().unwrap();
if bad.contains(&h) {
return Err(ImportError::KnownBad.into());
}
@@ -319,16 +327,16 @@ impl BlockQueue {
}
}
match verify_block_basic(&header, &bytes, &*self.engine) {
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
Ok(()) => {
self.processing.write().insert(h.clone());
self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes });
self.processing.write().unwrap().insert(h.clone());
self.verification.unverified.lock().unwrap().push_back(UnverifiedBlock { header: header, bytes: bytes });
self.more_to_verify.notify_all();
Ok(h)
},
Err(err) => {
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
self.verification.bad.lock().insert(h.clone());
self.verification.bad.lock().unwrap().insert(h.clone());
Err(err)
}
}
@@ -339,14 +347,14 @@ impl BlockQueue {
if block_hashes.is_empty() {
return;
}
let mut verified_lock = self.verification.verified.lock();
let mut verified = &mut *verified_lock;
let mut bad = self.verification.bad.lock();
let mut processing = self.processing.write();
let mut verified_lock = self.verification.verified.lock().unwrap();
let mut verified = verified_lock.deref_mut();
let mut bad = self.verification.bad.lock().unwrap();
let mut processing = self.processing.write().unwrap();
bad.reserve(block_hashes.len());
for hash in block_hashes {
bad.insert(hash.clone());
processing.remove(hash);
processing.remove(&hash);
}
let mut new_verified = VecDeque::new();
@@ -366,15 +374,15 @@ impl BlockQueue {
if block_hashes.is_empty() {
return;
}
let mut processing = self.processing.write();
let mut processing = self.processing.write().unwrap();
for hash in block_hashes {
processing.remove(hash);
processing.remove(&hash);
}
}
/// Removes up to `max` verified blocks from the queue
pub fn drain(&self, max: usize) -> Vec<PreverifiedBlock> {
let mut verified = self.verification.verified.lock();
let mut verified = self.verification.verified.lock().unwrap();
let count = min(max, verified.len());
let mut result = Vec::with_capacity(count);
for _ in 0..count {
@@ -391,15 +399,15 @@ impl BlockQueue {
/// Get queue status.
pub fn queue_info(&self) -> BlockQueueInfo {
let (unverified_len, unverified_bytes) = {
let v = self.verification.unverified.lock();
let v = self.verification.unverified.lock().unwrap();
(v.len(), v.heap_size_of_children())
};
let (verifying_len, verifying_bytes) = {
let v = self.verification.verifying.lock();
let v = self.verification.verifying.lock().unwrap();
(v.len(), v.heap_size_of_children())
};
let (verified_len, verified_bytes) = {
let v = self.verification.verified.lock();
let v = self.verification.verified.lock().unwrap();
(v.len(), v.heap_size_of_children())
};
BlockQueueInfo {
@@ -413,18 +421,18 @@ impl BlockQueue {
+ verifying_bytes
+ verified_bytes
// TODO: https://github.com/servo/heapsize/pull/50
//+ self.processing.read().heap_size_of_children(),
//+ self.processing.read().unwrap().heap_size_of_children(),
}
}
/// Optimise memory footprint of the heap fields.
pub fn collect_garbage(&self) {
{
self.verification.unverified.lock().shrink_to_fit();
self.verification.verifying.lock().shrink_to_fit();
self.verification.verified.lock().shrink_to_fit();
self.verification.unverified.lock().unwrap().shrink_to_fit();
self.verification.verifying.lock().unwrap().shrink_to_fit();
self.verification.verified.lock().unwrap().shrink_to_fit();
}
self.processing.write().shrink_to_fit();
self.processing.write().unwrap().shrink_to_fit();
}
}
@@ -450,7 +458,6 @@ impl Drop for BlockQueue {
#[cfg(test)]
mod tests {
use util::*;
use io::*;
use spec::*;
use block_queue::*;
use tests::helpers::*;
@@ -460,7 +467,7 @@ mod tests {
fn get_test_queue() -> BlockQueue {
let spec = get_test_spec();
let engine = spec.engine;
BlockQueue::new(BlockQueueConfig::default(), engine, IoChannel::disconnected())
BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
}
#[test]
@@ -468,7 +475,7 @@ mod tests {
// TODO better test
let spec = Spec::new_test();
let engine = spec.engine;
let _ = BlockQueue::new(BlockQueueConfig::default(), engine, IoChannel::disconnected());
let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
}
#[test]
@@ -531,7 +538,7 @@ mod tests {
let engine = spec.engine;
let mut config = BlockQueueConfig::default();
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
let queue = BlockQueue::new(config, engine, IoChannel::disconnected());
let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
assert!(!queue.queue_info().is_full());
let mut blocks = get_good_dummy_block_seq(50);
for b in blocks.drain(..) {

View File

@@ -14,7 +14,6 @@
// 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::bytes::Bytes;
use util::numbers::{U256,H256};
use header::BlockNumber;
@@ -26,7 +25,5 @@ pub struct BestBlock {
/// Best block number.
pub number: BlockNumber,
/// Best block total difficulty.
pub total_difficulty: U256,
/// Best block uncompressed bytes
pub block: Bytes,
pub total_difficulty: U256
}

View File

@@ -17,6 +17,8 @@
use util::numbers::{U256,H256};
use header::BlockNumber;
use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap};
/// Brief info about inserted block.
#[derive(Clone)]
pub struct BlockInfo {
@@ -38,7 +40,7 @@ pub enum BlockLocation {
/// It's not a part of the canon chain.
Branch,
/// It's part of the fork which should become canon chain,
/// because its total difficulty is higher than current
/// because it's total difficulty is higher than current
/// canon chain difficulty.
BranchBecomingCanonChain(BranchBecomingCanonChainData),
}
@@ -52,3 +54,43 @@ pub struct BranchBecomingCanonChainData {
/// Hashes of the blocks which were invalidated.
pub retracted: Vec<H256>,
}
impl FromRawBytesVariable for BranchBecomingCanonChainData {
fn from_bytes_variable(bytes: &[u8]) -> Result<BranchBecomingCanonChainData, FromBytesError> {
type Tuple = (Vec<H256>, Vec<H256>, H256);
let (enacted, retracted, ancestor) = try!(Tuple::from_bytes_variable(bytes));
Ok(BranchBecomingCanonChainData { ancestor: ancestor, enacted: enacted, retracted: retracted })
}
}
impl FromRawBytesVariable for BlockLocation {
fn from_bytes_variable(bytes: &[u8]) -> Result<BlockLocation, FromBytesError> {
match bytes[0] {
0 => Ok(BlockLocation::CanonChain),
1 => Ok(BlockLocation::Branch),
2 => Ok(BlockLocation::BranchBecomingCanonChain(
try!(BranchBecomingCanonChainData::from_bytes_variable(&bytes[1..bytes.len()])))),
_ => Err(FromBytesError::UnknownMarker)
}
}
}
impl ToBytesWithMap for BranchBecomingCanonChainData {
fn to_bytes_map(&self) -> Vec<u8> {
(&self.enacted, &self.retracted, &self.ancestor).to_bytes_map()
}
}
impl ToBytesWithMap for BlockLocation {
fn to_bytes_map(&self) -> Vec<u8> {
match *self {
BlockLocation::CanonChain => vec![0u8],
BlockLocation::Branch => vec![1u8],
BlockLocation::BranchBecomingCanonChain(ref data) => {
let mut bytes = (&data.enacted, &data.retracted, &data.ancestor).to_bytes_map();
bytes.insert(0, 2u8);
bytes
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@
//! Blockchain configuration.
/// Blockchain configuration.
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub struct Config {
/// Preferred cache size in bytes.
pub pref_cache_size: usize,

View File

@@ -41,7 +41,7 @@ pub enum ExtrasIndex {
fn with_index(hash: &H256, i: ExtrasIndex) -> H264 {
let mut result = H264::default();
result[0] = i as u8;
(*result)[1..].clone_from_slice(hash);
result.deref_mut()[1..].clone_from_slice(hash);
result
}

View File

@@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain generator for tests.
mod bloom;
mod block;
mod complete;

View File

@@ -26,7 +26,7 @@ mod import_route;
mod update;
#[cfg(test)]
pub mod generator;
mod generator;
pub use self::blockchain::{BlockProvider, BlockChain};
pub use self::cache::CacheSize;

View File

@@ -6,11 +6,9 @@ use blooms::BloomGroup;
use super::extras::{BlockDetails, BlockReceipts, TransactionAddress, LogGroupPosition};
/// Block extras update info.
pub struct ExtrasUpdate<'a> {
pub struct ExtrasUpdate {
/// Block info.
pub info: BlockInfo,
/// Current block uncompressed rlp bytes
pub block: &'a [u8],
/// Modified block hashes.
pub block_hashes: HashMap<BlockNumber, H256>,
/// Modified block details.

View File

@@ -1,72 +0,0 @@
// 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 std::collections::{VecDeque, HashSet};
use std::hash::Hash;
const COLLECTION_QUEUE_SIZE: usize = 8;
pub struct CacheManager<T> where T: Eq + Hash {
pref_cache_size: usize,
max_cache_size: usize,
bytes_per_cache_entry: usize,
cache_usage: VecDeque<HashSet<T>>
}
impl<T> CacheManager<T> where T: Eq + Hash {
pub fn new(pref_cache_size: usize, max_cache_size: usize, bytes_per_cache_entry: usize) -> Self {
CacheManager {
pref_cache_size: pref_cache_size,
max_cache_size: max_cache_size,
bytes_per_cache_entry: bytes_per_cache_entry,
cache_usage: (0..COLLECTION_QUEUE_SIZE).into_iter().map(|_| Default::default()).collect(),
}
}
pub fn note_used(&mut self, id: T) {
if !self.cache_usage[0].contains(&id) {
if let Some(c) = self.cache_usage.iter_mut().skip(1).find(|e| e.contains(&id)) {
c.remove(&id);
}
self.cache_usage[0].insert(id);
}
}
/// Collects unused objects from cache.
/// 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();
return;
}
for _ in 0..COLLECTION_QUEUE_SIZE {
let current_size = notify_unused(self.cache_usage.pop_back().unwrap());
self.cache_usage.push_front(Default::default());
if current_size < self.max_cache_size {
break;
}
}
}
fn rotate_cache_if_needed(&mut self) {
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
let cache = self.cache_usage.pop_back().unwrap();
self.cache_usage.push_front(cache);
}
}
}

View File

@@ -1,47 +0,0 @@
// 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 ipc::{IpcConfig, BinaryConvertError};
use std::collections::VecDeque;
use std::mem;
/// Represents what has to be handled by actor listening to chain events
#[derive(Ipc)]
pub trait ChainNotify : Send + Sync {
/// fires when chain has new blocks
fn new_blocks(&self,
_imported: Vec<H256>,
_invalid: Vec<H256>,
_enacted: Vec<H256>,
_retracted: Vec<H256>,
_sealed: Vec<H256>,
_duration: u64) {
// does nothing by default
}
/// fires when chain achieves active mode
fn start(&self) {
// does nothing by default
}
/// fires when chain achieves passive mode
fn stop(&self) {
// does nothing by default
}
}
impl IpcConfig for ChainNotify { }

View File

@@ -13,63 +13,47 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::{HashSet, HashMap, VecDeque};
use std::sync::{Arc, Weak};
use std::path::{Path};
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::time::{Instant};
use time::precise_time_ns;
// util
use util::{journaldb, rlp, Bytes, View, PerfTimer, Itertools, Mutex, RwLock};
use util::journaldb::JournalDB;
use util::rlp::{UntrustedRlp};
use util::numbers::*;
use util::sha3::*;
use util::kvdb::*;
//! Blockchain database client.
// other
use io::*;
use views::{BlockView, HeaderView, BodyView};
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult};
use header::BlockNumber;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use util::*;
use util::panics::*;
use views::BlockView;
use error::{ImportError, ExecutionError, BlockError, ImportResult};
use header::{BlockNumber, Header};
use state::State;
use spec::Spec;
use basic_types::Seal;
use engines::Engine;
use service::ClientIoMessage;
use engine::Engine;
use views::HeaderView;
use service::{NetSyncMessage, SyncMessage};
use env_info::LastHashes;
use verification;
use verification::{PreverifiedBlock, Verifier};
use block::*;
use transaction::{LocalizedTransaction, SignedTransaction, Action};
use blockchain::extras::TransactionAddress;
use types::filter::Filter;
use filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient,
TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile,
BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics };
use client::Error as ClientError;
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace;
use trace::FlatTransactionTraces;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use util::TrieFactory;
use snapshot::{self, io as snapshot_io};
// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -99,28 +83,12 @@ impl ClientReport {
}
}
struct SleepState {
last_activity: Option<Instant>,
last_autosleep: Option<Instant>,
}
impl SleepState {
fn new(awake: bool) -> Self {
SleepState {
last_activity: match awake { false => None, true => Some(Instant::now()) },
last_autosleep: match awake { false => Some(Instant::now()), true => None },
}
}
}
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
pub struct Client {
mode: Mode,
chain: Arc<BlockChain>,
tracedb: Arc<TraceDB<BlockChain>>,
engine: Arc<Engine>,
db: Arc<Database>,
engine: Arc<Box<Engine>>,
state_db: Mutex<Box<JournalDB>>,
block_queue: BlockQueue,
report: RwLock<ClientReport>,
@@ -128,85 +96,80 @@ pub struct Client {
panic_handler: Arc<PanicHandler>,
verifier: Box<Verifier>,
vm_factory: Arc<EvmFactory>,
trie_factory: TrieFactory,
miner: Arc<Miner>,
sleep_state: Mutex<SleepState>,
liveness: AtomicBool,
io_channel: IoChannel<ClientIoMessage>,
notify: RwLock<Vec<Weak<ChainNotify>>>,
io_channel: IoChannel<NetSyncMessage>,
queue_transactions: AtomicUsize,
last_hashes: RwLock<VecDeque<H256>>,
}
const HISTORY: u64 = 1200;
// DO NOT TOUCH THIS ANY MORE UNLESS YOU REALLY KNOW WHAT YOU'RE DOING.
// Altering it will force a blanket DB update for *all* JournalDB-derived
// databases.
// Instead, add/upgrade the version string of the individual JournalDB-derived database
// of which you actually want force an upgrade.
const CLIENT_DB_VER_STR: &'static str = "5.3";
// database columns
/// Column for State
pub const DB_COL_STATE: Option<u32> = Some(0);
/// Column for Block headers
pub const DB_COL_HEADERS: Option<u32> = Some(1);
/// Column for Block bodies
pub const DB_COL_BODIES: Option<u32> = Some(2);
/// Column for Extras
pub const DB_COL_EXTRA: Option<u32> = Some(3);
/// Column for Traces
pub const DB_COL_TRACE: Option<u32> = Some(4);
/// Number of columns in DB
pub const DB_NO_OF_COLUMNS: Option<u32> = Some(5);
/// Get the path for the databases given the root path and information on the databases.
pub fn get_db_path(path: &Path, pruning: journaldb::Algorithm, genesis_hash: H256) -> PathBuf {
let mut dir = path.to_path_buf();
dir.push(H64::from(genesis_hash).hex());
//TODO: sec/fat: pruned/full versioning
// version here is a bit useless now, since it's controlled only be the pruning algo.
dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, pruning));
dir
}
/// Append a path element to the given path and return the string.
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {
let mut p = path.as_ref().to_path_buf();
pub fn append_path(path: &Path, item: &str) -> String {
let mut p = path.to_path_buf();
p.push(item);
p.to_str().unwrap().to_owned()
}
impl Client {
/// Create a new client with given spec and DB path and custom verifier.
/// Create a new client with given spec and DB path and custom verifier.
pub fn new(
config: ClientConfig,
spec: &Spec,
spec: Spec,
path: &Path,
miner: Arc<Miner>,
message_channel: IoChannel<ClientIoMessage>,
) -> Result<Arc<Client>, ClientError> {
let path = path.to_path_buf();
message_channel: IoChannel<NetSyncMessage>)
-> Result<Arc<Client>, ClientError>
{
let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block();
let mut db_config = DatabaseConfig::with_columns(DB_NO_OF_COLUMNS);
db_config.cache_size = config.db_cache_size;
db_config.compaction = config.db_compaction.compaction_profile();
db_config.wal = config.db_wal;
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone())));
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
let mut state_db_config = match config.db_cache_size {
None => DatabaseConfig::default(),
Some(cache_size) => DatabaseConfig::with_cache(cache_size),
};
let mut state_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 batch = DBTransaction::new(&db);
try!(state_db.commit(&batch, 0, &spec.genesis_header().hash(), None));
try!(db.write(batch).map_err(ClientError::Database));
if config.db_compaction == DatabaseCompactionProfile::HDD {
state_db_config = state_db_config.compaction(CompactionProfile::hdd());
}
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
let mut state_db = journaldb::new(
&append_path(&path, "state"),
config.pruning,
state_db_config
);
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
}
let engine = spec.engine.clone();
let engine = Arc::new(spec.engine);
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel.clone());
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
let client = Client {
sleep_state: Mutex::new(SleepState::new(awake)),
liveness: AtomicBool::new(awake),
mode: config.mode,
chain: chain,
tracedb: tracedb,
engine: engine,
db: db,
state_db: Mutex::new(state_db),
block_queue: block_queue,
report: RwLock::new(Default::default()),
@@ -214,46 +177,20 @@ impl Client {
panic_handler: panic_handler,
verifier: verification::new(config.verifier_type),
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
trie_factory: TrieFactory::new(config.trie_spec),
miner: miner,
io_channel: message_channel,
notify: RwLock::new(Vec::new()),
queue_transactions: AtomicUsize::new(0),
last_hashes: RwLock::new(VecDeque::new()),
};
Ok(Arc::new(client))
}
/// Adds an actor to be notified on certain events
pub fn add_notify(&self, target: Arc<ChainNotify>) {
self.notify.write().push(Arc::downgrade(&target));
}
fn notify<F>(&self, f: F) where F: Fn(&ChainNotify) {
for np in self.notify.read().iter() {
if let Some(n) = np.upgrade() {
f(&*n);
}
}
}
/// Flush the block import queue.
pub fn flush_queue(&self) {
self.block_queue.flush();
while !self.block_queue.queue_info().is_empty() {
self.import_verified_blocks();
}
}
fn build_last_hashes(&self, parent_hash: H256) -> Arc<LastHashes> {
{
let hashes = self.last_hashes.read();
if hashes.front().map_or(false, |h| h == &parent_hash) {
let mut res = Vec::from(hashes.clone());
res.resize(256, H256::default());
return Arc::new(res);
}
}
fn build_last_hashes(&self, parent_hash: H256) -> LastHashes {
let mut last_hashes = LastHashes::new();
last_hashes.resize(256, H256::new());
last_hashes[0] = parent_hash;
@@ -265,13 +202,11 @@ impl Client {
None => break,
}
}
let mut cached_hashes = self.last_hashes.write();
*cached_hashes = VecDeque::from(last_hashes.clone());
Arc::new(last_hashes)
last_hashes
}
fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result<LockedBlock, ()> {
let engine = &*self.engine;
let engine = self.engine.deref().deref();
let header = &block.header;
// Check the block isn't so old we won't be able to enact it.
@@ -282,7 +217,7 @@ impl Client {
}
// Verify Block Family
let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, &*self.chain);
let verify_family_result = self.verifier.verify_block_family(&header, &block.bytes, engine, self.chain.deref());
if let Err(e) = verify_family_result {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
@@ -298,9 +233,9 @@ impl Client {
// Enact Verified Block
let parent = chain_has_parent.unwrap();
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().boxed_clone();
let db = self.state_db.lock().unwrap().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);
if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
@@ -308,7 +243,7 @@ impl Client {
// Final Verification
let locked_block = enact_result.unwrap();
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
if let Err(e) = self.verifier.verify_block_final(&header, locked_block.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
}
@@ -343,53 +278,52 @@ impl Client {
}
/// This is triggered by a message coming from a block queue when the block is ready for insertion
pub fn import_verified_blocks(&self) -> usize {
pub fn import_verified_blocks(&self, io: &IoChannel<NetSyncMessage>) -> usize {
let max_blocks_to_import = 64;
let (imported_blocks, import_results, invalid_blocks, imported, duration) = {
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
let mut invalid_blocks = HashSet::new();
let mut import_results = Vec::with_capacity(max_blocks_to_import);
let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_verified_blocks");
let start = precise_time_ns();
let blocks = self.block_queue.drain(max_blocks_to_import);
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
let mut invalid_blocks = HashSet::new();
let mut import_results = Vec::with_capacity(max_blocks_to_import);
for block in blocks {
let header = &block.header;
if invalid_blocks.contains(&header.parent_hash) {
invalid_blocks.insert(header.hash());
continue;
}
let closed_block = self.check_and_close_block(&block);
if let Err(_) = closed_block {
invalid_blocks.insert(header.hash());
continue;
}
let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_verified_blocks");
let blocks = self.block_queue.drain(max_blocks_to_import);
let closed_block = closed_block.unwrap();
imported_blocks.push(header.hash());
let original_best = self.chain_info().best_block_hash;
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
import_results.push(route);
for block in blocks {
let header = &block.header;
self.report.write().accrue_block(&block);
if invalid_blocks.contains(&header.parent_hash) {
invalid_blocks.insert(header.hash());
continue;
}
let imported = imported_blocks.len();
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
{
if !invalid_blocks.is_empty() {
self.block_queue.mark_as_bad(&invalid_blocks);
}
if !imported_blocks.is_empty() {
self.block_queue.mark_as_good(&imported_blocks);
}
let closed_block = self.check_and_close_block(&block);
if let Err(_) = closed_block {
invalid_blocks.insert(header.hash());
continue;
}
let duration_ns = precise_time_ns() - start;
(imported_blocks, import_results, invalid_blocks, imported, duration_ns)
};
let closed_block = closed_block.unwrap();
imported_blocks.push(header.hash());
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
import_results.push(route);
self.report.write().unwrap().accrue_block(&block);
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
}
let imported = imported_blocks.len();
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
{
if !invalid_blocks.is_empty() {
self.block_queue.mark_as_bad(&invalid_blocks);
}
if !imported_blocks.is_empty() {
self.block_queue.mark_as_good(&imported_blocks);
}
}
{
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
@@ -399,26 +333,25 @@ impl Client {
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
}
self.notify(|notify| {
notify.new_blocks(
imported_blocks.clone(),
invalid_blocks.clone(),
enacted.clone(),
retracted.clone(),
Vec::new(),
duration,
);
});
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
imported: imported_blocks,
invalid: invalid_blocks,
enacted: enacted,
retracted: retracted,
sealed: Vec::new(),
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
}
}
self.db.flush().expect("DB flush failed.");
if self.chain_info().best_block_hash != original_best {
self.miner.update_sealing(self);
}
imported
}
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain {
let number = block.header().number();
let parent = block.header().parent_hash().clone();
// Are we committing an era?
let ancient = if number >= HISTORY {
let n = number - HISTORY;
@@ -428,56 +361,37 @@ impl Client {
};
// Commit results
let receipts = block.receipts().to_owned();
let traces = block.traces().clone().unwrap_or_else(Vec::new);
let traces: Vec<FlatTransactionTraces> = traces.into_iter()
.map(Into::into)
.collect();
let receipts = block.receipts().clone();
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
let batch = DBTransaction::new(&self.db);
// CHECK! I *think* this is fine, even if the state_root is equal to another
// already-imported block of the same number.
// TODO: Prove it with a test.
block.drain().commit(&batch, number, hash, ancient).expect("DB commit failed.");
block.drain().commit(number, hash, ancient).expect("State DB commit failed.");
let route = self.chain.insert_block(&batch, block_data, receipts);
self.tracedb.import(&batch, TraceImportRequest {
traces: traces.into(),
// And update the chain after commit to prevent race conditions
// (when something is in chain but you are not able to fetch details)
let route = self.chain.insert_block(block_data, receipts);
self.tracedb.import(TraceImportRequest {
traces: traces,
block_hash: hash.clone(),
block_number: number,
enacted: route.enacted.clone(),
retracted: route.retracted.len()
});
// Final commit to the DB
self.db.write_buffered(batch).expect("DB write failed.");
self.chain.commit();
self.update_last_hashes(&parent, hash);
route
}
fn update_last_hashes(&self, parent: &H256, hash: &H256) {
let mut hashes = self.last_hashes.write();
if hashes.front().map_or(false, |h| h == parent) {
if hashes.len() > 255 {
hashes.pop_back();
}
hashes.push_front(hash.clone());
}
}
/// Import transactions from the IO queue
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
let _timer = PerfTimer::new("import_queued_transactions");
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok()).collect();
let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
let results = self.miner.import_external_transactions(self, txs);
results.len()
}
/// Attempt to get a copy of a specific block's final state.
/// Attempt to get a copy of a specific block's state.
///
/// This will not fail if given BlockID::Latest.
/// Otherwise, this can fail (but may not) if the DB prunes state.
@@ -495,7 +409,7 @@ impl Client {
};
self.block_header(id).and_then(|header| {
let db = self.state_db.lock().boxed_clone();
let db = self.state_db.lock().unwrap().boxed_clone();
// early exit for pruned blocks
if db.is_pruned() && self.chain.best_block_number() >= block_number + HISTORY {
@@ -504,33 +418,14 @@ impl Client {
let root = HeaderView::new(&header).state_root();
State::from_existing(db, root, self.engine.account_start_nonce(), self.trie_factory.clone()).ok()
State::from_existing(db, root, self.engine.account_start_nonce()).ok()
})
}
/// Attempt to get a copy of a specific block's beginning state.
///
/// This will not fail if given BlockID::Latest.
/// Otherwise, this can fail (but may not) if the DB prunes state.
pub fn state_at_beginning(&self, id: BlockID) -> Option<State> {
// fast path for latest state.
match id {
BlockID::Pending => self.state_at(BlockID::Latest),
id => match self.block_number(id) {
None | Some(0) => None,
Some(n) => self.state_at(BlockID::Number(n - 1)),
}
}
}
/// Get a copy of the best block's state.
pub fn state(&self) -> State {
State::from_existing(
self.state_db.lock().boxed_clone(),
HeaderView::new(&self.best_block_header()).state_root(),
self.engine.account_start_nonce(),
self.trie_factory.clone())
.expect("State root of best block header always valid.")
State::from_existing(self.state_db.lock().unwrap().boxed_clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
.expect("State root of best block header always valid.")
}
/// Get info on the cache.
@@ -540,48 +435,20 @@ impl Client {
/// Get the report.
pub fn report(&self) -> ClientReport {
let mut report = self.report.read().clone();
report.state_db_mem = self.state_db.lock().mem_used();
let mut report = self.report.read().unwrap().clone();
report.state_db_mem = self.state_db.lock().unwrap().mem_used();
report
}
/// Tick the client.
// TODO: manage by real events.
pub fn tick(&self) {
self.chain.collect_garbage();
self.block_queue.collect_garbage();
self.tracedb.collect_garbage();
}
match self.mode {
Mode::Dark(timeout) => {
let mut ss = self.sleep_state.lock();
if let Some(t) = ss.last_activity {
if Instant::now() > t + timeout {
self.sleep();
ss.last_activity = None;
}
}
}
Mode::Passive(timeout, wakeup_after) => {
let mut ss = self.sleep_state.lock();
let now = Instant::now();
if let Some(t) = ss.last_activity {
if now > t + timeout {
self.sleep();
ss.last_activity = None;
ss.last_autosleep = Some(now);
}
}
if let Some(t) = ss.last_autosleep {
if now > t + wakeup_after {
self.wake_up();
ss.last_activity = Some(now);
ss.last_autosleep = None;
}
}
}
_ => {}
}
/// Set up the cache behaviour.
pub fn configure_cache(&self, pref_cache_size: usize, max_cache_size: usize) {
self.chain.configure_cache(pref_cache_size, max_cache_size);
}
/// Look up the block number for the given block ID.
@@ -594,39 +461,6 @@ impl Client {
}
}
/// Take a snapshot at the given block.
/// If the ID given is "latest", this will default to 1000 blocks behind.
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().boxed_clone();
let best_block_number = self.chain_info().best_block_number;
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
if best_block_number > HISTORY + block_number && db.is_pruned() {
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(())
}
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
match id {
BlockID::Hash(hash) => Some(hash),
@@ -645,34 +479,11 @@ impl Client {
})
}
}
fn wake_up(&self) {
if !self.liveness.load(AtomicOrdering::Relaxed) {
self.liveness.store(true, AtomicOrdering::Relaxed);
self.notify(|n| n.start());
trace!(target: "mode", "wake_up: Waking.");
}
}
fn sleep(&self) {
if self.liveness.load(AtomicOrdering::Relaxed) {
// only sleep if the import queue is mostly empty.
if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON {
self.liveness.store(false, AtomicOrdering::Relaxed);
self.notify(|n| n.stop());
trace!(target: "mode", "sleep: Sleeping.");
} else {
trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
// TODO: Consider uncommenting.
//*self.last_activity.lock() = Some(Instant::now());
}
}
}
}
impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash());
let env_info = EnvInfo {
@@ -685,9 +496,7 @@ impl BlockChainClient for Client {
gas_limit: U256::max_value(),
};
// that's just a copy of the state.
let mut state = try!(self.state_at(block).ok_or(CallError::StatePruned));
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let mut state = self.state();
let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message)
@@ -699,74 +508,37 @@ impl BlockChainClient for Client {
state.add_balance(&sender, &(needed_balance - balance));
}
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options));
let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options);
// TODO gav move this into Executive.
ret.state_diff = original_state.map(|original| state.diff_from(original));
Ok(ret)
}
fn replay(&self, id: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError> {
let address = try!(self.transaction_address(id).ok_or(CallError::TransactionNotFound));
let header_data = try!(self.block_header(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned));
let body_data = try!(self.block_body(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned));
let mut state = try!(self.state_at_beginning(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned));
let txs = BodyView::new(&body_data).transactions();
if address.index >= txs.len() {
return Err(CallError::TransactionNotFound);
}
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let view = HeaderView::new(&header_data);
let last_hashes = self.build_last_hashes(view.hash());
let mut env_info = EnvInfo {
number: view.number(),
author: view.author(),
timestamp: view.timestamp(),
difficulty: view.difficulty(),
last_hashes: last_hashes,
gas_used: U256::zero(),
gas_limit: view.gas_limit(),
};
for t in txs.iter().take(address.index) {
match Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, Default::default()) {
Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; }
Err(ee) => { return Err(CallError::Execution(ee)) }
if analytics.state_diffing {
if let Ok(ref mut x) = ret {
x.state_diff = Some(state.diff_from(self.state()));
}
}
let t = &txs[address.index];
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options));
ret.state_diff = original_state.map(|original| state.diff_from(original));
Ok(ret)
ret
}
fn keep_alive(&self) {
if self.mode != Mode::Active {
self.wake_up();
(*self.sleep_state.lock()).last_activity = Some(Instant::now());
}
}
fn best_block_header(&self) -> Bytes {
self.chain.best_block_header()
}
fn block_header(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_header_data(&hash))
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
}
fn block_body(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_body(&hash))
Self::block_hash(&self.chain, id).and_then(|hash| {
self.chain.block(&hash).map(|bytes| {
let rlp = Rlp::new(&bytes);
let mut body = RlpStream::new_list(2);
body.append_raw(rlp.at(1).as_raw(), 1);
body.append_raw(rlp.at(2).as_raw(), 1);
body.out()
})
})
}
fn block(&self, id: BlockID) -> Option<Bytes> {
if let BlockID::Pending = id {
if let Some(block) = self.miner.pending_block() {
if let &BlockID::Pending = &id {
if let Some(block) = self.miner.pending_block() {
return Some(block.rlp_bytes(Seal::Without));
}
}
@@ -784,10 +556,10 @@ impl BlockChainClient for Client {
}
fn block_total_difficulty(&self, id: BlockID) -> Option<U256> {
if let BlockID::Pending = id {
if let &BlockID::Pending = &id {
if let Some(block) = self.miner.pending_block() {
return Some(*block.header.difficulty() + self.block_total_difficulty(BlockID::Latest).expect("blocks in chain have details; qed"));
}
}
}
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
}
@@ -800,8 +572,8 @@ impl BlockChainClient for Client {
Self::block_hash(&self.chain, id)
}
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
self.state_at(id).map(|s| s.code(address))
fn code(&self, address: &Address) -> Option<Bytes> {
self.state().code(address)
}
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
@@ -816,15 +588,15 @@ impl BlockChainClient for Client {
self.transaction_address(id).and_then(|address| self.chain.transaction(&address))
}
fn uncle(&self, id: UncleID) -> Option<Bytes> {
let index = id.position;
self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index))
fn uncle(&self, id: UncleID) -> Option<Header> {
let index = id.1;
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index))
}
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
self.transaction_address(id).and_then(|address| self.chain.block_number(&address.block_hash).and_then(|block_number| {
let t = self.chain.block_body(&address.block_hash)
.and_then(|block| BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index));
self.transaction_address(id).and_then(|address| {
let t = self.chain.block(&address.block_hash)
.and_then(|block| BlockView::new(&block).localized_transaction_at(address.index));
match (t, self.chain.transaction_receipt(&address)) {
(Some(tx), Some(receipt)) => {
@@ -863,7 +635,7 @@ impl BlockChainClient for Client {
},
_ => None
}
}))
})
}
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
@@ -878,24 +650,24 @@ impl BlockChainClient for Client {
}
fn state_data(&self, hash: &H256) -> Option<Bytes> {
self.state_db.lock().state(hash)
self.state_db.lock().unwrap().state(hash)
}
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
self.chain.block_receipts(hash).map(|receipts| rlp::encode(&receipts).to_vec())
}
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
fn import_block(&self, bytes: Bytes) -> ImportResult {
{
let header = BlockView::new(&bytes).header_view();
if self.chain.is_known(&header.sha3()) {
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
return Err(ImportError::AlreadyInChain.into());
}
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
return Err(BlockError::UnknownParent(header.parent_hash()).into());
}
}
Ok(try!(self.block_queue.import_block(bytes)))
self.block_queue.import_block(bytes)
}
fn queue_info(&self) -> BlockQueueInfo {
@@ -939,7 +711,7 @@ impl BlockChainClient for Client {
blocks.into_iter()
.filter_map(|number| self.chain.block_hash(number).map(|hash| (number, hash)))
.filter_map(|(number, hash)| self.chain.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
.filter_map(|(number, hash, receipts)| self.chain.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
.filter_map(|(number, hash, receipts)| self.chain.block(&hash).map(|ref b| (number, hash, receipts, BlockView::new(b).transaction_hashes())))
.flat_map(|(number, hash, receipts, hashes)| {
let mut log_index = 0;
receipts.into_iter()
@@ -949,8 +721,8 @@ impl BlockChainClient for Client {
receipt.logs.into_iter()
.enumerate()
.filter(|tuple| filter.matches(&tuple.1))
.map(|(i, log)| LocalizedLogEntry {
entry: log,
.map(|(i, log)| LocalizedLogEntry {
entry: log,
block_hash: hash.clone(),
block_number: number,
transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new),
@@ -960,6 +732,7 @@ impl BlockChainClient for Client {
.collect::<Vec<LocalizedLogEntry>>()
})
.collect::<Vec<LocalizedLogEntry>>()
})
.collect()
}
@@ -1005,7 +778,7 @@ impl BlockChainClient for Client {
}
fn last_hashes(&self) -> LastHashes {
(*self.build_last_hashes(self.chain.best_block_hash())).clone()
self.build_last_hashes(self.chain.best_block_hash())
}
fn queue_transactions(&self, transactions: Vec<Bytes>) {
@@ -1013,7 +786,7 @@ impl BlockChainClient for Client {
debug!("Ignoring {} transactions: queue is full", transactions.len());
} else {
let len = transactions.len();
match self.io_channel.send(ClientIoMessage::NewTransactions(transactions)) {
match self.io_channel.send(NetworkIoMessage::User(SyncMessage::NewTransactions(transactions))) {
Ok(_) => {
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
}
@@ -1031,16 +804,15 @@ impl BlockChainClient for Client {
impl MiningBlockChainClient for Client {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = &*self.engine;
let engine = self.engine.deref().deref();
let h = self.chain.best_block_hash();
let mut open_block = OpenBlock::new(
engine,
&self.vm_factory,
self.trie_factory.clone(),
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().boxed_clone(),
&self.chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
self.state_db.lock().unwrap().boxed_clone(),
&self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
self.build_last_hashes(h.clone()),
author,
gas_range_target,
@@ -1067,7 +839,8 @@ impl MiningBlockChainClient for Client {
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_sealed_block");
let start = precise_time_ns();
let original_best = self.chain_info().best_block_hash;
let h = block.header().hash();
let number = block.header().number();
@@ -1076,20 +849,23 @@ impl MiningBlockChainClient for Client {
let route = self.commit_block(block, &h, &block_data);
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
{
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
imported: vec![h.clone()],
invalid: vec![],
enacted: enacted,
retracted: retracted,
sealed: vec![h.clone()],
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
}
if self.chain_info().best_block_hash != original_best {
self.miner.update_sealing(self);
}
self.notify(|notify| {
notify.new_blocks(
vec![h.clone()],
vec![],
enacted.clone(),
retracted.clone(),
vec![h.clone()],
precise_time_ns() - start,
);
});
self.db.flush().expect("DB flush failed.");
Ok(h)
}
}

View File

@@ -14,15 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::str::FromStr;
pub use std::time::Duration;
pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch};
pub use evm::VMType;
pub use verification::VerifierType;
use util::{journaldb, CompactionProfile};
use util::trie::TrieSpec;
use util::journaldb;
/// Client state db compaction profile
#[derive(Debug, PartialEq)]
@@ -34,54 +31,11 @@ pub enum DatabaseCompactionProfile {
}
impl Default for DatabaseCompactionProfile {
fn default() -> Self {
DatabaseCompactionProfile::Default
}
}
impl DatabaseCompactionProfile {
/// Returns corresponding compaction profile.
pub fn compaction_profile(&self) -> CompactionProfile {
match *self {
DatabaseCompactionProfile::Default => Default::default(),
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
}
}
}
impl FromStr for DatabaseCompactionProfile {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ssd" | "default" => Ok(DatabaseCompactionProfile::Default),
"hdd" => Ok(DatabaseCompactionProfile::HDD),
_ => Err("Invalid compaction profile given. Expected hdd/ssd (default).".into()),
}
}
}
/// Operating mode for the client.
#[derive(Debug, Eq, PartialEq)]
pub enum Mode {
/// Always on.
Active,
/// Goes offline after RLP is inactive for some (given) time, but
/// comes back online after a while of inactivity.
Passive(Duration, Duration),
/// Goes offline after RLP is inactive for some (given) time and
/// stays inactive.
Dark(Duration),
}
impl Default for Mode {
fn default() -> Self {
Mode::Active
}
fn default() -> Self { DatabaseCompactionProfile::Default }
}
/// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, PartialEq, Default)]
#[derive(Debug, Default)]
pub struct ClientConfig {
/// Block queue configuration.
pub queue: BlockQueueConfig,
@@ -91,8 +45,6 @@ pub struct ClientConfig {
pub tracing: TraceConfig,
/// VM type.
pub vm_type: VMType,
/// Trie type.
pub trie_spec: TrieSpec,
/// The JournalDB ("pruning") algorithm to use.
pub pruning: journaldb::Algorithm,
/// The name of the client instance.
@@ -101,32 +53,6 @@ pub struct ClientConfig {
pub db_cache_size: Option<usize>,
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Should db have WAL enabled?
pub db_wal: bool,
/// Operating mode
pub mode: Mode,
/// Type of block verifier used by client.
pub verifier_type: VerifierType,
}
#[cfg(test)]
mod test {
use super::{DatabaseCompactionProfile, Mode};
#[test]
fn test_default_compaction_profile() {
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Default);
}
#[test]
fn test_parsing_compaction_profile() {
assert_eq!(DatabaseCompactionProfile::Default, "ssd".parse().unwrap());
assert_eq!(DatabaseCompactionProfile::Default, "default".parse().unwrap());
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
}
#[test]
fn test_mode_default() {
assert_eq!(Mode::default(), Mode::Active);
}
}

View File

@@ -1,20 +1,11 @@
use trace::Error as TraceError;
use util::UtilError;
use std::fmt::{Display, Formatter, Error as FmtError};
use util::trie::TrieError;
/// Client configuration errors.
#[derive(Debug)]
pub enum Error {
/// TraceDB configuration error.
Trace(TraceError),
/// TrieDB-related error.
Trie(TrieError),
/// Database error
Database(String),
/// Util error
Util(UtilError),
}
impl From<TraceError> for Error {
@@ -23,31 +14,10 @@ impl From<TraceError> for Error {
}
}
impl From<TrieError> for Error {
fn from(err: TrieError) -> Self {
Error::Trie(err)
}
}
impl From<UtilError> for Error {
fn from(err: UtilError) -> Self {
Error::Util(err)
}
}
impl<E> From<Box<E>> for Error where Error: From<E> {
fn from(err: Box<E>) -> Self {
Error::from(*err)
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match *self {
Error::Trace(ref err) => write!(f, "{}", err),
Error::Trie(ref err) => write!(f, "{}", err),
Error::Util(ref err) => write!(f, "{}", err),
Error::Database(ref s) => write!(f, "Database error: {}", s),
Error::Trace(ref err) => write!(f, "{}", err)
}
}
}

View File

@@ -16,36 +16,217 @@
//! Blockchain database client.
mod client;
mod config;
mod error;
mod test_client;
mod trace;
mod client;
pub use self::client::*;
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::error::Error;
pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use types::trace_filter::Filter as TraceFilter;
pub use self::trace::Filter as TraceFilter;
pub use executive::{Executed, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo};
pub use self::chain_notify::{ChainNotify, ChainNotifyClient};
pub use types::call_analytics::CallAnalytics;
pub use block_import_error::BlockImportError;
pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError;
pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
use util::bytes::Bytes;
use util::hash::{Address, H256, H2048};
use util::numbers::U256;
use util::Itertools;
use blockchain::TreeRoute;
use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
use views::{BlockView};
use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
use evm::Factory as EvmFactory;
mod traits {
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
include!(concat!(env!("OUT_DIR"), "/traits.rs"));
/// Options concerning what analytics we run on the call.
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
pub struct CallAnalytics {
/// Make a transaction trace.
pub transaction_tracing: bool,
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
}
pub mod chain_notify {
//! Chain notify interface
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
include!(concat!(env!("OUT_DIR"), "/chain_notify.rs"));
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Get raw block header data by block id.
fn block_header(&self, id: BlockID) -> Option<Bytes>;
/// Get raw block body data by block id.
/// Block body is an RLP list of two items: uncles and transactions.
fn block_body(&self, id: BlockID) -> Option<Bytes>;
/// Get raw block data by block header hash.
fn block(&self, id: BlockID) -> Option<Bytes>;
/// Get block status by block header hash.
fn block_status(&self, id: BlockID) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty(&self, id: BlockID) -> Option<U256>;
/// Attempt to get address nonce at given block.
/// May not fail on BlockID::Latest.
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>;
/// Get address nonce at the latest block's state.
fn latest_nonce(&self, address: &Address) -> U256 {
self.nonce(address, BlockID::Latest)
.expect("nonce will return Some when given BlockID::Latest. nonce was given BlockID::Latest. \
Therefore nonce has returned Some; qed")
}
/// Get block hash.
fn block_hash(&self, id: BlockID) -> Option<H256>;
/// Get address code.
fn code(&self, address: &Address) -> Option<Bytes>;
/// Get address balance at the given block's state.
///
/// May not return None if given BlockID::Latest.
/// Returns None if and only if the block's root hash has been pruned from the DB.
fn balance(&self, address: &Address, id: BlockID) -> Option<U256>;
/// Get address balance at the latest block's state.
fn latest_balance(&self, address: &Address) -> U256 {
self.balance(address, BlockID::Latest)
.expect("balance will return Some if given BlockID::Latest. balance was given BlockID::Latest \
Therefore balance has returned Some; qed")
}
/// Get value of the storage at given position at the given block's state.
///
/// May not return None if given BlockID::Latest.
/// Returns None if and only if the block's root hash has been pruned from the DB.
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256>;
/// Get value of the storage at given position at the latest block's state.
fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 {
self.storage_at(address, position, BlockID::Latest)
.expect("storage_at will return Some if given BlockID::Latest. storage_at was given BlockID::Latest. \
Therefore storage_at has returned Some; qed")
}
/// Get transaction with given hash.
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
/// Get uncle with given id.
fn uncle(&self, id: UncleID) -> Option<Header>;
/// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
/// Get a tree route between `from` and `to`.
/// See `BlockChain::tree_route`.
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
/// Get all possible uncle hashes for a block.
fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>>;
/// Get latest state node
fn state_data(&self, hash: &H256) -> Option<Bytes>;
/// Get raw block receipts data by block header hash.
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Import a block into the blockchain.
fn import_block(&self, bytes: Bytes) -> ImportResult;
/// Get block queue information.
fn queue_info(&self) -> BlockQueueInfo;
/// Clear block queue and abort all import activity.
fn clear_queue(&self);
/// Get blockchain information.
fn chain_info(&self) -> BlockChainInfo;
/// Get the best block header.
fn best_block_header(&self) -> Bytes {
// TODO: lock blockchain only once
self.block_header(BlockID::Hash(self.chain_info().best_block_hash)).unwrap()
}
/// Returns numbers of blocks containing given bloom.
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>;
/// Returns logs matching given filter.
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
/// Makes a non-persistent transaction call.
// TODO: should be able to accept blockchain location for call.
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;
/// Returns traces matching given filter.
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;
/// Returns trace with given id.
fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>;
/// Returns traces created by transaction.
fn transaction_traces(&self, trace: TransactionID) -> Option<Vec<LocalizedTrace>>;
/// Returns traces created by transaction from block.
fn block_traces(&self, trace: BlockID) -> Option<Vec<LocalizedTrace>>;
/// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes;
/// Queue transactions for importing.
fn queue_transactions(&self, transactions: Vec<Bytes>);
/// list all transactions
fn pending_transactions(&self) -> Vec<SignedTransaction>;
/// Get the gas price distribution.
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
let mut h = self.chain_info().best_block_hash;
let mut corpus = Vec::new();
for _ in 0..sample_size {
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let block = BlockView::new(&block_bytes);
let header = block.header_view();
if header.number() == 0 {
break;
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
}
corpus.sort();
let n = corpus.len();
if n > 0 {
Ok((0..(distribution_size + 1))
.map(|i| corpus[i * (n - 1) / distribution_size])
.collect::<Vec<_>>()
)
} else {
Err(())
}
}
}
/// Extended client interface used for mining
pub trait MiningBlockChainClient : BlockChainClient {
/// Returns OpenBlock prepared for closing.
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes)
-> OpenBlock;
/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;
/// Import sealed block. Skips all verifications.
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
}

View File

@@ -18,26 +18,24 @@
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*;
use devtools::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics,
BlockImportError};
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics };
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
use receipt::{Receipt, LocalizedReceipt};
use blockchain::extras::BlockReceipts;
use error::{ImportResult};
use evm::{Factory as EvmFactory, VMType};
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use spec::Spec;
use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
use executive::Executed;
use error::CallError;
use error::{ExecutionError};
use trace::LocalizedTrace;
/// Test client.
@@ -61,17 +59,13 @@ pub struct TestBlockChainClient {
/// Code.
pub code: RwLock<HashMap<Address, Bytes>>,
/// Execution result.
pub execution_result: RwLock<Option<Result<Executed, CallError>>>,
pub execution_result: RwLock<Option<Executed>>,
/// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
/// Block queue size.
pub queue_size: AtomicUsize,
/// Miner
pub miner: Arc<Miner>,
/// Spec
pub spec: Spec,
/// VM Factory
pub vm_factory: EvmFactory,
}
#[derive(Clone)]
@@ -96,7 +90,7 @@ impl Default for TestBlockChainClient {
impl TestBlockChainClient {
/// Creates new test client.
pub fn new() -> Self {
let spec = Spec::new_test();
let mut client = TestBlockChainClient {
blocks: RwLock::new(HashMap::new()),
numbers: RwLock::new(HashMap::new()),
@@ -110,43 +104,41 @@ impl TestBlockChainClient {
execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()),
queue_size: AtomicUsize::new(0),
miner: Arc::new(Miner::with_spec(&spec)),
spec: spec,
vm_factory: EvmFactory::new(VMType::Interpreter),
miner: Arc::new(Miner::with_spec(Spec::new_test())),
};
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().clone();
client.genesis_hash = client.last_hash.read().unwrap().clone();
client
}
/// Set the transaction receipt result
pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) {
self.receipts.write().insert(id, receipt);
self.receipts.write().unwrap().insert(id, receipt);
}
/// Set the execution result.
pub fn set_execution_result(&self, result: Result<Executed, CallError>) {
*self.execution_result.write() = Some(result);
pub fn set_execution_result(&self, result: Executed) {
*self.execution_result.write().unwrap() = Some(result);
}
/// Set the balance of account `address` to `balance`.
pub fn set_balance(&self, address: Address, balance: U256) {
self.balances.write().insert(address, balance);
self.balances.write().unwrap().insert(address, balance);
}
/// Set nonce of account `address` to `nonce`.
pub fn set_nonce(&self, address: Address, nonce: U256) {
self.nonces.write().insert(address, nonce);
self.nonces.write().unwrap().insert(address, nonce);
}
/// Set `code` at `address`.
pub fn set_code(&self, address: Address, code: Bytes) {
self.code.write().insert(address, code);
self.code.write().unwrap().insert(address, code);
}
/// Set storage `position` to `value` for account `address`.
pub fn set_storage(&self, address: Address, position: H256, value: H256) {
self.storage.write().insert((address, position), value);
self.storage.write().unwrap().insert((address, position), value);
}
/// Set block queue size for testing
@@ -156,11 +148,11 @@ impl TestBlockChainClient {
/// Add blocks to test client.
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
let len = self.numbers.read().len();
let len = self.numbers.read().unwrap().len();
for n in len..(len + count) {
let mut header = BlockHeader::new();
header.difficulty = From::from(n);
header.parent_hash = self.last_hash.read().clone();
header.parent_hash = self.last_hash.read().unwrap().clone();
header.number = n as BlockNumber;
header.gas_limit = U256::from(1_000_000);
let uncles = match with {
@@ -168,7 +160,7 @@ impl TestBlockChainClient {
let mut uncles = RlpStream::new_list(1);
let mut uncle_header = BlockHeader::new();
uncle_header.difficulty = From::from(n);
uncle_header.parent_hash = self.last_hash.read().clone();
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
uncle_header.number = n as BlockNumber;
uncles.append(&uncle_header);
header.uncles_hash = uncles.as_raw().sha3();
@@ -181,7 +173,7 @@ impl TestBlockChainClient {
let mut txs = RlpStream::new_list(1);
let keypair = KeyPair::create().unwrap();
// Update nonces value
self.nonces.write().insert(keypair.address(), U256::one());
self.nonces.write().unwrap().insert(keypair.address(), U256::one());
let tx = Transaction {
action: Action::Create,
value: U256::from(100),
@@ -190,7 +182,7 @@ impl TestBlockChainClient {
gas_price: U256::one(),
nonce: U256::zero()
};
let signed_tx = tx.sign(keypair.secret());
let signed_tx = tx.sign(&keypair.secret());
txs.append(&signed_tx);
txs.out()
},
@@ -214,7 +206,7 @@ impl TestBlockChainClient {
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
rlp.append_raw(&rlp::NULL_RLP, 1);
self.blocks.write().insert(hash, rlp.out());
self.blocks.write().unwrap().insert(hash, rlp.out());
}
/// Make a bad block by setting invalid parent hash.
@@ -226,12 +218,12 @@ impl TestBlockChainClient {
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
rlp.append_raw(&rlp::NULL_RLP, 1);
self.blocks.write().insert(hash, rlp.out());
self.blocks.write().unwrap().insert(hash, rlp.out());
}
/// TODO:
pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 {
let blocks_read = self.numbers.read();
let blocks_read = self.numbers.read().unwrap();
let index = blocks_read.len() - delta;
blocks_read[&index].clone()
}
@@ -239,65 +231,30 @@ impl TestBlockChainClient {
fn block_hash(&self, id: BlockID) -> Option<H256> {
match id {
BlockID::Hash(hash) => Some(hash),
BlockID::Number(n) => self.numbers.read().get(&(n as usize)).cloned(),
BlockID::Earliest => self.numbers.read().get(&0).cloned(),
BlockID::Latest | BlockID::Pending => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned()
BlockID::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
BlockID::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
BlockID::Latest | BlockID::Pending => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
}
}
}
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
let temp = RandomTempPath::new();
let db = Database::open_default(temp.as_str()).unwrap();
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, None);
GuardedTempResult {
_temp: temp,
result: Some(journal_db)
}
}
impl MiningBlockChainClient for TestBlockChainClient {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = &*self.spec.engine;
let genesis_header = self.spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
self.spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = vec![genesis_header.hash()];
let mut open_block = OpenBlock::new(
engine,
self.vm_factory(),
Default::default(),
false,
db,
&genesis_header,
Arc::new(last_hashes),
author,
gas_range_target,
extra_data
).expect("Opening block for tests will not fail.");
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
open_block.set_timestamp(10_000_000);
open_block
fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock {
unimplemented!();
}
fn vm_factory(&self) -> &EvmFactory {
&self.vm_factory
unimplemented!();
}
fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult {
Ok(H256::default())
unimplemented!();
}
}
impl BlockChainClient for TestBlockChainClient {
fn call(&self, _t: &SignedTransaction, _block: BlockID, _analytics: CallAnalytics) -> Result<Executed, CallError> {
self.execution_result.read().clone().unwrap()
}
fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result<Executed, CallError> {
self.execution_result.read().clone().unwrap()
fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
Ok(self.execution_result.read().unwrap().clone().unwrap())
}
fn block_total_difficulty(&self, _id: BlockID) -> Option<U256> {
@@ -310,7 +267,7 @@ impl BlockChainClient for TestBlockChainClient {
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256> {
match id {
BlockID::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params.account_start_nonce)),
BlockID::Latest => Some(self.nonces.read().unwrap().get(address).cloned().unwrap_or_else(U256::zero)),
_ => None,
}
}
@@ -319,16 +276,13 @@ impl BlockChainClient for TestBlockChainClient {
self.nonce(address, BlockID::Latest).unwrap()
}
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
match id {
BlockID::Latest => Some(self.code.read().get(address).cloned()),
_ => None,
}
fn code(&self, address: &Address) -> Option<Bytes> {
self.code.read().unwrap().get(address).cloned()
}
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
if let BlockID::Latest = id {
Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero))
Some(self.balances.read().unwrap().get(address).cloned().unwrap_or_else(U256::zero))
} else {
None
}
@@ -340,7 +294,7 @@ impl BlockChainClient for TestBlockChainClient {
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> {
if let BlockID::Latest = id {
Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
Some(self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
} else {
None
}
@@ -350,12 +304,12 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
fn uncle(&self, _id: UncleID) -> Option<BlockHeader> {
unimplemented!();
}
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
self.receipts.read().get(&id).cloned()
self.receipts.read().unwrap().get(&id).cloned()
}
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockID, _to_block: BlockID) -> Option<Vec<BlockNumber>> {
@@ -370,31 +324,27 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn best_block_header(&self) -> Bytes {
self.block_header(BlockID::Hash(self.chain_info().best_block_hash)).expect("Best block always have header.")
}
fn block_header(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
}
fn block_body(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| {
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
let mut stream = RlpStream::new_list(2);
stream.append_raw(Rlp::new(r).at(1).as_raw(), 1);
stream.append_raw(Rlp::new(r).at(2).as_raw(), 1);
stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
stream.append_raw(Rlp::new(&r).at(2).as_raw(), 1);
stream.out()
}))
}
fn block(&self, id: BlockID) -> Option<Bytes> {
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned())
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
}
fn block_status(&self, id: BlockID) -> BlockStatus {
match id {
BlockID::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain,
BlockID::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain,
BlockID::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
BlockID::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
_ => BlockStatus::Unknown
}
}
@@ -405,7 +355,7 @@ impl BlockChainClient for TestBlockChainClient {
ancestor: H256::new(),
index: 0,
blocks: {
let numbers_read = self.numbers.read();
let numbers_read = self.numbers.read().unwrap();
let mut adding = false;
let mut blocks = Vec::new();
@@ -458,15 +408,15 @@ impl BlockChainClient for TestBlockChainClient {
None
}
fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
fn import_block(&self, b: Bytes) -> ImportResult {
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
let h = header.hash();
let number: usize = header.number as usize;
if number > self.blocks.read().len() {
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number);
if number > self.blocks.read().unwrap().len() {
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
}
if number > 0 {
match self.blocks.read().get(&header.parent_hash) {
match self.blocks.read().unwrap().get(&header.parent_hash) {
Some(parent) => {
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
if parent.number != (header.number - 1) {
@@ -478,27 +428,27 @@ impl BlockChainClient for TestBlockChainClient {
}
}
}
let len = self.numbers.read().len();
let len = self.numbers.read().unwrap().len();
if number == len {
{
let mut difficulty = self.difficulty.write();
*difficulty = *difficulty + header.difficulty;
let mut difficulty = self.difficulty.write().unwrap();
*difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
}
mem::replace(&mut *self.last_hash.write(), h.clone());
self.blocks.write().insert(h.clone(), b);
self.numbers.write().insert(number, h.clone());
mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
self.blocks.write().unwrap().insert(h.clone(), b);
self.numbers.write().unwrap().insert(number, h.clone());
let mut parent_hash = header.parent_hash;
if number > 0 {
let mut n = number - 1;
while n > 0 && self.numbers.read()[&n] != parent_hash {
*self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone();
while n > 0 && self.numbers.read().unwrap()[&n] != parent_hash {
*self.numbers.write().unwrap().get_mut(&n).unwrap() = parent_hash.clone();
n -= 1;
parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
parent_hash = Rlp::new(&self.blocks.read().unwrap()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
}
}
}
else {
self.blocks.write().insert(h.clone(), b.to_vec());
self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
}
Ok(h)
}
@@ -519,11 +469,11 @@ impl BlockChainClient for TestBlockChainClient {
fn chain_info(&self) -> BlockChainInfo {
BlockChainInfo {
total_difficulty: *self.difficulty.read(),
pending_total_difficulty: *self.difficulty.read(),
total_difficulty: *self.difficulty.read().unwrap(),
pending_total_difficulty: *self.difficulty.read().unwrap(),
genesis_hash: self.genesis_hash.clone(),
best_block_hash: self.last_hash.read().clone(),
best_block_number: self.blocks.read().len() as BlockNumber - 1,
best_block_hash: self.last_hash.read().unwrap().clone(),
best_block_number: self.blocks.read().unwrap().len() as BlockNumber - 1,
}
}

View File

@@ -1,12 +1,13 @@
//! Bridge between Tracedb and Blockchain.
use util::{H256};
use std::ops::Range;
use util::{Address, H256};
use header::BlockNumber;
use trace::DatabaseExtras as TraceDatabaseExtras;
use blockchain::{BlockChain, BlockProvider};
use blockchain::extras::TransactionAddress;
pub use types::trace_filter::Filter;
use super::BlockID;
impl TraceDatabaseExtras for BlockChain {
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
@@ -25,3 +26,13 @@ impl TraceDatabaseExtras for BlockChain {
.map(|tx| tx.hash())
}
}
/// Easy to use trace filter.
pub struct Filter {
/// Range of filtering.
pub range: Range<BlockID>,
/// From address.
pub from_address: Vec<Address>,
/// To address.
pub to_address: Vec<Address>,
}

View File

@@ -1,229 +0,0 @@
// 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::bytes::Bytes;
use util::hash::{Address, H256, H2048};
use util::numbers::U256;
use util::Itertools;
use blockchain::TreeRoute;
use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
use header::{BlockNumber};
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
use filter::Filter;
use views::{BlockView};
use error::{ImportResult, CallError};
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
use evm::Factory as EvmFactory;
use types::ids::*;
use types::trace_filter::Filter as TraceFilter;
use executive::Executed;
use env_info::LastHashes;
use types::call_analytics::CallAnalytics;
use block_import_error::BlockImportError;
use std::mem;
use std::collections::VecDeque;
use ipc::{IpcConfig, BinaryConvertError};
use types::blockchain_info::BlockChainInfo;
use types::block_status::BlockStatus;
#[derive(Ipc)]
#[ipc(client_ident="RemoteClient")]
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Should be called by any external-facing interface when actively using the client.
/// To minimise chatter, there's no need to call more than once every 30s.
fn keep_alive(&self) {}
/// Get raw block header data by block id.
fn block_header(&self, id: BlockID) -> Option<Bytes>;
/// Get raw block body data by block id.
/// Block body is an RLP list of two items: uncles and transactions.
fn block_body(&self, id: BlockID) -> Option<Bytes>;
/// Get raw block data by block header hash.
fn block(&self, id: BlockID) -> Option<Bytes>;
/// Get block status by block header hash.
fn block_status(&self, id: BlockID) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty(&self, id: BlockID) -> Option<U256>;
/// Attempt to get address nonce at given block.
/// May not fail on BlockID::Latest.
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>;
/// Get address nonce at the latest block's state.
fn latest_nonce(&self, address: &Address) -> U256 {
self.nonce(address, BlockID::Latest)
.expect("nonce will return Some when given BlockID::Latest. nonce was given BlockID::Latest. \
Therefore nonce has returned Some; qed")
}
/// Get block hash.
fn block_hash(&self, id: BlockID) -> Option<H256>;
/// Get address code at given block's state.
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.
///
/// May not return None if given BlockID::Latest.
/// Returns None if and only if the block's root hash has been pruned from the DB.
fn balance(&self, address: &Address, id: BlockID) -> Option<U256>;
/// Get address balance at the latest block's state.
fn latest_balance(&self, address: &Address) -> U256 {
self.balance(address, BlockID::Latest)
.expect("balance will return Some if given BlockID::Latest. balance was given BlockID::Latest \
Therefore balance has returned Some; qed")
}
/// Get value of the storage at given position at the given block's state.
///
/// May not return None if given BlockID::Latest.
/// Returns None if and only if the block's root hash has been pruned from the DB.
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256>;
/// Get value of the storage at given position at the latest block's state.
fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 {
self.storage_at(address, position, BlockID::Latest)
.expect("storage_at will return Some if given BlockID::Latest. storage_at was given BlockID::Latest. \
Therefore storage_at has returned Some; qed")
}
/// Get transaction with given hash.
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
/// Get uncle with given id.
fn uncle(&self, id: UncleID) -> Option<Bytes>;
/// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
/// Get a tree route between `from` and `to`.
/// See `BlockChain::tree_route`.
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
/// Get all possible uncle hashes for a block.
fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>>;
/// Get latest state node
fn state_data(&self, hash: &H256) -> Option<Bytes>;
/// Get raw block receipts data by block header hash.
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Import a block into the blockchain.
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
/// Get block queue information.
fn queue_info(&self) -> BlockQueueInfo;
/// Clear block queue and abort all import activity.
fn clear_queue(&self);
/// Get blockchain information.
fn chain_info(&self) -> BlockChainInfo;
/// Get the best block header.
fn best_block_header(&self) -> Bytes;
/// Returns numbers of blocks containing given bloom.
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>;
/// Returns logs matching given filter.
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
/// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError>;
/// Replays a given transaction for inspection.
fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError>;
/// Returns traces matching given filter.
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;
/// Returns trace with given id.
fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>;
/// Returns traces created by transaction.
fn transaction_traces(&self, trace: TransactionID) -> Option<Vec<LocalizedTrace>>;
/// Returns traces created by transaction from block.
fn block_traces(&self, trace: BlockID) -> Option<Vec<LocalizedTrace>>;
/// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes;
/// Queue transactions for importing.
fn queue_transactions(&self, transactions: Vec<Bytes>);
/// list all transactions
fn pending_transactions(&self) -> Vec<SignedTransaction>;
/// Get the gas price distribution.
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
let mut h = self.chain_info().best_block_hash;
let mut corpus = Vec::new();
for _ in 0..sample_size {
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let block = BlockView::new(&block_bytes);
let header = block.header_view();
if header.number() == 0 {
break;
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
}
corpus.sort();
let n = corpus.len();
if n > 0 {
Ok((0..(distribution_size + 1))
.map(|i| corpus[i * (n - 1) / distribution_size])
.collect::<Vec<_>>()
)
} else {
Err(())
}
}
}
/// Extended client interface used for mining
pub trait MiningBlockChainClient : BlockChainClient {
/// Returns OpenBlock prepared for closing.
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes)
-> OpenBlock;
/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;
/// Import sealed block. Skips all verifications.
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
}
impl IpcConfig for BlockChainClient { }

View File

@@ -18,11 +18,11 @@
use std::ops::Deref;
use std::hash::Hash;
use std::sync::RwLock;
use std::collections::HashMap;
use util::{DBTransaction, Database, RwLock};
use util::{DBTransaction, Database};
use util::rlp::{encode, Encodable, decode, Decodable};
#[derive(Clone, Copy)]
pub enum CacheUpdatePolicy {
Overwrite,
@@ -62,14 +62,14 @@ pub trait Key<T> {
/// Should be used to write value into database.
pub trait Writable {
/// 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, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
/// 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, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
K: Key<T, Target = R> + Hash + Eq,
T: Encodable,
R: Deref<Target = [u8]> {
self.write(col, &key, &value);
self.write(&key, &value);
match policy {
CacheUpdatePolicy::Overwrite => {
cache.insert(key, value);
@@ -81,20 +81,20 @@ pub trait Writable {
}
/// Writes the values into the database and updates the cache.
fn extend_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
fn extend_with_cache<K, T, R>(&self, cache: &mut Cache<K, T>, values: HashMap<K, 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() {
self.write(col, &key, &value);
self.write(&key, &value);
cache.insert(key, value);
}
},
CacheUpdatePolicy::Remove => {
for (key, value) in &values {
self.write(col, key, value);
self.write(key, value);
cache.remove(key);
}
},
@@ -105,51 +105,51 @@ pub trait Writable {
/// Should be used to read values from database.
pub trait Readable {
/// Returns value for given key.
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where
fn read<T, R>(&self, key: &Key<T, Target = R>) -> Option<T> where
T: Decodable,
R: Deref<Target = [u8]>;
/// Returns value for given key either in cache or in database.
fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T> where
fn read_with_cache<K, T, C>(&self, cache: &RwLock<C>, key: &K) -> Option<T> where
K: Key<T> + Eq + Hash + Clone,
T: Clone + Decodable,
C: Cache<K, T> {
{
let read = cache.read();
let read = cache.read().unwrap();
if let Some(v) = read.get(key) {
return Some(v.clone());
}
}
self.read(col, key).map(|value: T|{
let mut write = cache.write();
self.read(key).map(|value: T|{
let mut write = cache.write().unwrap();
write.insert(key.clone(), value.clone());
value
})
}
/// Returns true if given value exists.
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>;
fn exists<T, R>(&self, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>;
/// Returns true if given value exists either in cache or in database.
fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool where
fn exists_with_cache<K, T, R, C>(&self, cache: &RwLock<C>, key: &K) -> bool where
K: Eq + Hash + Key<T, Target = R>,
R: Deref<Target = [u8]>,
C: Cache<K, T> {
{
let read = cache.read();
let read = cache.read().unwrap();
if read.get(key).is_some() {
return true;
}
}
self.exists::<T, R>(col, key)
self.exists::<T, R>(key)
}
}
impl Writable for DBTransaction {
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
let result = self.put(col, &key.key(), &encode(value));
fn write<T, R>(&self, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
let result = self.put(&key.key(), &encode(value));
if let Err(err) = result {
panic!("db put failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
}
@@ -157,8 +157,8 @@ impl Writable for DBTransaction {
}
impl Readable for Database {
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where T: Decodable, R: Deref<Target = [u8]> {
let result = self.get(col, &key.key());
fn read<T, R>(&self, key: &Key<T, Target = R>) -> Option<T> where T: Decodable, R: Deref<Target = [u8]> {
let result = self.get(&key.key());
match result {
Ok(option) => option.map(|v| decode(&v)),
@@ -168,8 +168,8 @@ impl Readable for Database {
}
}
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> {
let result = self.get(col, &key.key());
fn exists<T, R>(&self, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> {
let result = self.get(&key.key());
match result {
Ok(v) => v.is_some(),

View File

@@ -14,15 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Consensus engine specification and basic implementations.
mod null_engine;
mod instant_seal;
mod basic_authority;
pub use self::null_engine::NullEngine;
pub use self::instant_seal::InstantSeal;
pub use self::basic_authority::BasicAuthority;
//! Consensus engine specification
use common::*;
use account_provider::AccountProvider;

View File

@@ -1,108 +0,0 @@
// 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 std::collections::BTreeMap;
use util::hash::Address;
use builtin::Builtin;
use engines::Engine;
use spec::CommonParams;
use evm::Schedule;
use env_info::EnvInfo;
use block::ExecutedBlock;
use common::Bytes;
use account_provider::AccountProvider;
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
pub struct InstantSeal {
params: CommonParams,
builtins: BTreeMap<Address, Builtin>,
}
impl InstantSeal {
/// Returns new instance of InstantSeal with default VM Factory
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
InstantSeal {
params: params,
builtins: builtins,
}
}
}
impl Engine for InstantSeal {
fn name(&self) -> &str {
"InstantSeal"
}
fn params(&self) -> &CommonParams {
&self.params
}
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
&self.builtins
}
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
Schedule::new_homestead()
}
fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
Some(Vec::new())
}
}
#[cfg(test)]
mod tests {
use common::*;
use tests::helpers::*;
use account_provider::AccountProvider;
use spec::Spec;
use block::*;
/// Create a new test chain spec with `BasicAuthority` consensus engine.
fn new_test_instant() -> Spec { Spec::load(include_bytes!("../../res/instant_seal.json")) }
#[test]
fn instant_can_seal() {
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap();
let spec = new_test_instant();
let engine = &*spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let vm_factory = Default::default();
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();
// Seal with empty AccountProvider.
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine, seal).is_ok());
}
#[test]
fn instant_cant_verify() {
let engine = new_test_instant().engine;
let mut header: Header = Header::default();
assert!(engine.verify_block_basic(&header, None).is_ok());
header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]);
assert!(engine.verify_block_unordered(&header, None).is_ok());
}
}

View File

@@ -36,7 +36,7 @@ pub struct EnvInfo {
/// The block gas limit.
pub gas_limit: U256,
/// The last 256 block hashes.
pub last_hashes: Arc<LastHashes>,
pub last_hashes: LastHashes,
/// The gas used.
pub gas_used: U256,
}
@@ -49,7 +49,7 @@ impl Default for EnvInfo {
timestamp: 0,
difficulty: 0.into(),
gas_limit: 0.into(),
last_hashes: Arc::new(vec![]),
last_hashes: vec![],
gas_used: 0.into(),
}
}
@@ -64,7 +64,7 @@ impl From<ethjson::vm::Env> for EnvInfo {
difficulty: e.difficulty.into(),
gas_limit: e.gas_limit.into(),
timestamp: e.timestamp.into(),
last_hashes: Arc::new((1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect()),
last_hashes: (1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect(),
gas_used: U256::zero(),
}
}

View File

@@ -17,17 +17,13 @@
//! General error types for use in ethcore.
use util::*;
use io::*;
use header::BlockNumber;
use basic_types::LogBloom;
use client::Error as ClientError;
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use types::block_import_error::BlockImportError;
use snapshot::Error as SnapshotError;
pub use types::executed::{ExecutionError, CallError};
pub use types::executed::ExecutionError;
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq)]
/// Errors concerning transaction processing.
pub enum TransactionError {
/// Transaction is already imported to the queue
@@ -62,6 +58,8 @@ pub enum TransactionError {
},
/// Transaction's gas limit (aka gas) is invalid.
InvalidGasLimit(OutOfBounds<U256>),
/// Transaction is invalid for some other reason.
DAORescue,
}
impl fmt::Display for TransactionError {
@@ -80,6 +78,7 @@ impl fmt::Display for TransactionError {
GasLimitExceeded { limit, got } =>
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
DAORescue => "Transaction is invalid due to the DAO rescue.".into(),
};
f.write_fmt(format_args!("Transaction error ({})", msg))
@@ -230,34 +229,22 @@ pub enum Error {
PowInvalid,
/// Error concerning TrieDBs
Trie(TrieError),
/// Io crate error.
Io(IoError),
/// Standard io error.
StdIo(::std::io::Error),
/// Snappy error.
Snappy(::util::snappy::InvalidInput),
/// Snapshot error.
Snapshot(SnapshotError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Client(ref err) => err.fmt(f),
Error::Util(ref err) => err.fmt(f),
Error::Io(ref err) => err.fmt(f),
Error::Block(ref err) => err.fmt(f),
Error::Execution(ref err) => err.fmt(f),
Error::Transaction(ref err) => err.fmt(f),
Error::Import(ref err) => err.fmt(f),
Error::Client(ref err) => f.write_fmt(format_args!("{}", err)),
Error::Util(ref err) => f.write_fmt(format_args!("{}", err)),
Error::Block(ref err) => f.write_fmt(format_args!("{}", err)),
Error::Execution(ref err) => f.write_fmt(format_args!("{}", err)),
Error::Transaction(ref err) => f.write_fmt(format_args!("{}", err)),
Error::Import(ref err) => f.write_fmt(format_args!("{}", err)),
Error::UnknownEngineName(ref name) =>
f.write_fmt(format_args!("Unknown engine name ({})", name)),
Error::PowHashInvalid => f.write_str("Invalid or out of date PoW hash."),
Error::PowInvalid => f.write_str("Invalid nonce or mishash"),
Error::Trie(ref err) => err.fmt(f),
Error::StdIo(ref err) => err.fmt(f),
Error::Snappy(ref err) => err.fmt(f),
Error::Snapshot(ref err) => err.fmt(f),
Error::Trie(ref err) => f.write_fmt(format_args!("{}", err)),
}
}
}
@@ -267,10 +254,7 @@ pub type ImportResult = Result<H256, Error>;
impl From<ClientError> for Error {
fn from(err: ClientError) -> Error {
match err {
ClientError::Trie(err) => Error::Trie(err),
_ => Error::Client(err)
}
Error::Client(err)
}
}
@@ -318,7 +302,7 @@ impl From<UtilError> for Error {
impl From<IoError> for Error {
fn from(err: IoError) -> Error {
Error::Io(err)
Error::Util(From::from(err))
}
}
@@ -328,49 +312,6 @@ impl From<TrieError> for Error {
}
}
impl From<::std::io::Error> for Error {
fn from(err: ::std::io::Error) -> Error {
Error::StdIo(err)
}
}
impl From<BlockImportError> for Error {
fn from(err: BlockImportError) -> Error {
match err {
BlockImportError::Block(e) => Error::Block(e),
BlockImportError::Import(e) => Error::Import(e),
BlockImportError::Other(s) => Error::Util(UtilError::SimpleString(s)),
}
}
}
impl From<snappy::InvalidInput> for Error {
fn from(err: snappy::InvalidInput) -> Error {
Error::Snappy(err)
}
}
impl From<SnapshotError> for Error {
fn from(err: SnapshotError) -> Error {
match err {
SnapshotError::Io(err) => Error::StdIo(err),
SnapshotError::Trie(err) => Error::Trie(err),
SnapshotError::Decoder(err) => err.into(),
other => Error::Snapshot(other),
}
}
}
impl<E> From<Box<E>> for Error where Error: From<E> {
fn from(err: Box<E>) -> Error {
Error::from(*err)
}
}
binary_fixed_size!(BlockError);
binary_fixed_size!(ImportError);
binary_fixed_size!(TransactionError);
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)]
macro_rules! assimilate {

View File

@@ -18,7 +18,7 @@ use ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
use common::*;
use block::*;
use spec::CommonParams;
use engines::Engine;
use engine::*;
use evm::Schedule;
use ethjson;
@@ -43,7 +43,7 @@ pub struct EthashParams {
pub dao_hardfork_transition: u64,
/// DAO hard-fork refund contract address (C).
pub dao_hardfork_beneficiary: Address,
/// DAO hard-fork DAO accounts list (L)
/// DAO hard-fork DAO accounts list (L)
pub dao_hardfork_accounts: Vec<Address>,
}
@@ -55,11 +55,11 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
duration_limit: p.duration_limit.into(),
block_reward: p.block_reward.into(),
registrar: p.registrar.map_or_else(Address::new, Into::into),
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map_or(0, Into::into),
dao_hardfork_transition: p.dao_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
registrar: p.registrar.map(Into::into).unwrap_or(Address::new()),
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map(Into::into).unwrap_or(0),
dao_hardfork_transition: p.dao_hardfork_transition.map(Into::into).unwrap_or(0x7fffffffffffffff),
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map(Into::into).unwrap_or(Address::new()),
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or(vec![]).into_iter().map(Into::into).collect(),
}
}
}
@@ -131,7 +131,7 @@ impl Engine for Ethash {
if header.number >= self.ethash_params.dao_hardfork_transition &&
header.number <= self.ethash_params.dao_hardfork_transition + 9 {
header.extra_data = b"dao-hard-fork"[..].to_owned();
}
}
header.note_dirty();
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
}
@@ -141,7 +141,7 @@ impl Engine for Ethash {
// TODO: enable trigger function maybe?
// if block.fields().header.gas_limit <= 4_000_000.into() {
let mut state = block.fields_mut().state;
for child in &self.ethash_params.dao_hardfork_accounts {
for child in self.ethash_params.dao_hardfork_accounts.iter() {
let b = state.balance(child);
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b);
}
@@ -163,9 +163,7 @@ impl Engine for Ethash {
for u in fields.uncles.iter() {
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)));
}
if let Err(e) = fields.state.commit() {
warn!("Encountered error on state commit: {}", e);
}
fields.state.commit();
}
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
@@ -201,8 +199,8 @@ impl Engine for Ethash {
if header.gas_limit > 0x7fffffffffffffffu64.into() {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit })));
}
}
Ok(())
}
@@ -350,14 +348,14 @@ mod tests {
#[test]
fn on_close_block() {
let spec = new_morden();
let engine = &*spec.engine;
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
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 = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
}
@@ -365,14 +363,14 @@ mod tests {
#[test]
fn on_close_block_with_uncle() {
let spec = new_morden();
let engine = &*spec.engine;
let engine = &spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default();
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 b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone();
@@ -398,7 +396,7 @@ mod tests {
author: 0.into(),
timestamp: 0,
difficulty: 0.into(),
last_hashes: Arc::new(vec![]),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
});
@@ -410,7 +408,7 @@ mod tests {
author: 0.into(),
timestamp: 0,
difficulty: 0.into(),
last_hashes: Arc::new(vec![]),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
});

View File

@@ -33,10 +33,14 @@ use super::spec::*;
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec.
pub fn new_frontier() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier.json")) }
pub fn new_frontier() -> Spec {
Spec::load(include_bytes!("../../res/ethereum/frontier.json"))
}
/// Create a new Frontier mainnet chain spec without the DAO hardfork.
pub fn new_classic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/classic.json")) }
pub fn new_frontier_dogmatic() -> Spec {
Spec::load(include_bytes!("../../res/ethereum/frontier-dogmatic.json"))
}
/// Create a new Frontier chain spec as though it never changes to Homestead.
pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_test.json")) }
@@ -67,8 +71,8 @@ mod tests {
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
spec.ensure_db_good(db.as_hashdb_mut());
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce()).unwrap();
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("0000000000000000000000000000000000000003")), U256::from(1u64));

View File

@@ -1,126 +0,0 @@
// 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/>.
//! benchmarking for EVM
//! should be started with:
//! ```bash
//! multirust run nightly cargo bench
//! ```
extern crate test;
use self::test::{Bencher, black_box};
use common::*;
use evm::{self, Factory, VMType};
use evm::tests::FakeExt;
#[bench]
fn simple_loop_log0_usize(b: &mut Bencher) {
simple_loop_log0(U256::from(::std::usize::MAX), b)
}
#[bench]
fn simple_loop_log0_u256(b: &mut Bencher) {
simple_loop_log0(!U256::zero(), b)
}
fn simple_loop_log0(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = black_box(
"62ffffff5b600190036000600fa0600357".from_hex().unwrap()
);
b.iter(|| {
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
#[bench]
fn mem_gas_calculation_same_usize(b: &mut Bencher) {
mem_gas_calculation_same(U256::from(::std::usize::MAX), b)
}
#[bench]
fn mem_gas_calculation_same_u256(b: &mut Bencher) {
mem_gas_calculation_same(!U256::zero(), b)
}
fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
b.iter(|| {
let code = black_box(
"6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap()
);
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
#[bench]
fn mem_gas_calculation_increasing_usize(b: &mut Bencher) {
mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b)
}
#[bench]
fn mem_gas_calculation_increasing_u256(b: &mut Bencher) {
mem_gas_calculation_increasing(!U256::zero(), b)
}
fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
b.iter(|| {
let code = black_box(
"6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap()
);
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
fn result(r: evm::Result<evm::GasLeft>) -> U256 {
match r {
Ok(evm::GasLeft::Known(v)) => v,
Ok(evm::GasLeft::NeedsReturn(v, _)) => v,
_ => U256::zero(),
}
}

View File

@@ -95,95 +95,6 @@ impl<'a> Finalize for Result<GasLeft<'a>> {
}
}
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> + ops::Sub<Output=Self> + ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self> + cmp::Ord + Sized + From<usize> + Copy {
/// Converts this cost into `U256`
fn as_u256(&self) -> U256;
/// Tries to fit `U256` into this `Cost` type
fn from_u256(val: U256) -> Result<Self>;
/// Convert to usize (may panic)
fn as_usize(&self) -> usize;
/// Add with overflow
fn overflow_add(self, other: Self) -> (Self, bool);
/// Multiple with overflow
fn overflow_mul(self, other: Self) -> (Self, bool);
/// Single-step full multiplication and shift: `(self*other) >> shr`
/// Should not overflow on intermediate steps
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
}
impl CostType for U256 {
fn as_u256(&self) -> U256 {
*self
}
fn from_u256(val: U256) -> Result<Self> {
Ok(val)
}
fn as_usize(&self) -> usize {
self.as_u64() as usize
}
fn overflow_add(self, other: Self) -> (Self, bool) {
Uint::overflowing_add(self, other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
Uint::overflowing_mul(self, other)
}
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
let x = self.full_mul(other);
let U512(parts) = x;
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
let U512(parts) = x >> shr;
(
U256([parts[0], parts[1], parts[2], parts[3]]),
overflow
)
}
}
impl CostType for usize {
fn as_u256(&self) -> U256 {
U256::from(*self)
}
fn from_u256(val: U256) -> Result<Self> {
let res = val.low_u64() as usize;
// validate if value fits into usize
if U256::from(res) != val {
return Err(Error::OutOfGas);
}
Ok(res)
}
fn as_usize(&self) -> usize {
*self
}
fn overflow_add(self, other: Self) -> (Self, bool) {
self.overflowing_add(other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
self.overflowing_mul(other)
}
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
let U128(parts) = c;
let overflow = o | (parts[1] > 0);
let U128(parts) = c >> shr;
let result = parts[0] as usize;
let overflow = overflow | (parts[0] > result as u64);
(result, overflow)
}
}
/// Evm interface
pub trait Evm {
/// This function should be used to execute transaction.
@@ -192,54 +103,3 @@ pub trait Evm {
/// to compute the final gas left.
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
}
#[test]
#[cfg(test)]
fn should_calculate_overflow_mul_shr_without_overflow() {
// given
let num = 1048576;
// when
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
let (res2, o2) = num.overflow_mul_shr(num, 20);
// then
assert_eq!(res1, U256::from(num));
assert!(!o1);
assert_eq!(res2, num);
assert!(!o2);
}
#[test]
#[cfg(test)]
fn should_calculate_overflow_mul_shr_with_overflow() {
// given
let max = ::std::u64::MAX;
let num1 = U256([max, max, max, max]);
let num2 = ::std::usize::MAX;
// when
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
// then
assert_eq!(res2, num2 - 1);
assert!(o2);
assert_eq!(res1, !U256::zero() - U256::one());
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());
}

View File

@@ -19,7 +19,6 @@
use util::common::*;
use evm::{self, Schedule};
use env_info::*;
use types::executed::CallType;
/// Result of externalities create function.
pub enum ContractCreateResult {
@@ -70,15 +69,13 @@ pub trait Ext {
/// and true if subcall was successfull.
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn call(&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
output: &mut [u8],
call_type: CallType
) -> MessageCallResult;
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> MessageCallResult;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Bytes;

View File

@@ -19,9 +19,8 @@
//! TODO: consider spliting it into two separate files.
use std::fmt;
use evm::Evm;
use util::{U256, Uint};
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
/// Type of EVM to use.
pub enum VMType {
/// JIT EVM
@@ -86,30 +85,24 @@ pub struct Factory {
impl Factory {
/// Create fresh instance of VM
/// Might choose implementation depending on supplied gas.
#[cfg(feature = "jit")]
pub fn create(&self, gas: U256) -> Box<Evm> {
pub fn create(&self) -> Box<Evm> {
match self.evm {
VMType::Jit => {
Box::new(super::jit::JitEvm::default())
},
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
Box::new(super::interpreter::Interpreter::<usize>::default())
} else {
Box::new(super::interpreter::Interpreter::<U256>::default())
VMType::Interpreter => {
Box::new(super::interpreter::Interpreter::default())
}
}
}
/// Create fresh instance of VM
/// Might choose implementation depending on supplied gas.
#[cfg(not(feature = "jit"))]
pub fn create(&self, gas: U256) -> Box<Evm> {
pub fn create(&self) -> Box<Evm> {
match self.evm {
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
Box::new(super::interpreter::Interpreter::<usize>::default())
} else {
Box::new(super::interpreter::Interpreter::<U256>::default())
VMType::Interpreter => {
Box::new(super::interpreter::Interpreter::default())
}
}
}
@@ -120,10 +113,6 @@ impl Factory {
evm: evm
}
}
fn can_fit_in_usize(gas: U256) -> bool {
gas == U256::from(gas.low_u64() as usize)
}
}
impl Default for Factory {
@@ -146,7 +135,7 @@ impl Default for Factory {
#[test]
fn test_create_vm() {
let _vm = Factory::default().create(U256::zero());
let _vm = Factory::default().create();
}
/// Create tests by injecting different VM factories

View File

@@ -79,7 +79,7 @@ fn test_get_log_topics() {
assert_eq!(get_log_topics(LOG4), 4);
}
#[derive(PartialEq, Clone, Copy)]
#[derive(PartialEq)]
pub enum GasPriceTier {
/// 0 Zero
Zero,
@@ -101,12 +101,6 @@ pub enum GasPriceTier {
Invalid
}
impl Default for GasPriceTier {
fn default() -> Self {
GasPriceTier::Invalid
}
}
/// Returns the index in schedule for specific `GasPriceTier`
pub fn get_tier_idx (tier: GasPriceTier) -> usize {
match tier {
@@ -122,7 +116,6 @@ pub fn get_tier_idx (tier: GasPriceTier) -> usize {
}
}
#[derive(Copy, Clone, Default)]
pub struct InstructionInfo {
pub name: &'static str,
pub additional: usize,
@@ -133,7 +126,7 @@ pub struct InstructionInfo {
}
impl InstructionInfo {
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> Self {
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
InstructionInfo {
name: name,
additional: additional,
@@ -145,141 +138,142 @@ impl InstructionInfo {
}
}
lazy_static! {
pub static ref INSTRUCTIONS: [InstructionInfo; 0x100] = {
let mut arr = [InstructionInfo::default(); 0x100];
arr[STOP as usize] = InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero);
arr[ADD as usize] = InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[SUB as usize] = InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[MUL as usize] = InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low);
arr[DIV as usize] = InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low);
arr[SDIV as usize] = InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low);
arr[MOD as usize] = InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low);
arr[SMOD as usize] = InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low);
arr[EXP as usize] = InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special);
arr[NOT as usize] = InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow);
arr[LT as usize] = InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[GT as usize] = InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[SLT as usize] = InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[SGT as usize] = InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[EQ as usize] = InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[ISZERO as usize] = InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow);
arr[AND as usize] = InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[OR as usize] = InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[XOR as usize] = InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[BYTE as usize] = InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow);
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid);
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid);
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low);
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special);
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base);
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext);
arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base);
arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base);
arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base);
arr[CALLDATALOAD as usize] = InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow);
arr[CALLDATASIZE as usize] = InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base);
arr[CALLDATACOPY as usize] = InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base);
arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base);
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext);
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext);
arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext);
arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base);
arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base);
arr[NUMBER as usize] = InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base);
arr[DIFFICULTY as usize] = InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base);
arr[GASLIMIT as usize] = InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base);
arr[POP as usize] = InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base);
arr[MLOAD as usize] = InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow);
arr[MSTORE as usize] = InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow);
arr[MSTORE8 as usize] = InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow);
arr[SLOAD as usize] = InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special);
arr[SSTORE as usize] = InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special);
arr[JUMP as usize] = InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid);
arr[JUMPI as usize] = InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High);
arr[PC as usize] = InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base);
arr[MSIZE as usize] = InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base);
arr[GAS as usize] = InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base);
arr[JUMPDEST as usize] = InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special);
arr[PUSH1 as usize] = InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH2 as usize] = InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH3 as usize] = InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH4 as usize] = InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH5 as usize] = InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH6 as usize] = InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH7 as usize] = InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH8 as usize] = InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH9 as usize] = InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH10 as usize] = InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH11 as usize] = InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH12 as usize] = InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH13 as usize] = InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH14 as usize] = InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH15 as usize] = InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH16 as usize] = InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH17 as usize] = InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH18 as usize] = InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH19 as usize] = InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH20 as usize] = InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH21 as usize] = InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH22 as usize] = InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH23 as usize] = InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH24 as usize] = InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH25 as usize] = InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH26 as usize] = InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH27 as usize] = InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH28 as usize] = InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH29 as usize] = InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH30 as usize] = InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH31 as usize] = InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow);
arr[PUSH32 as usize] = InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow);
arr[DUP1 as usize] = InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow);
arr[DUP2 as usize] = InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow);
arr[DUP3 as usize] = InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow);
arr[DUP4 as usize] = InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow);
arr[DUP5 as usize] = InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow);
arr[DUP6 as usize] = InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow);
arr[DUP7 as usize] = InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow);
arr[DUP8 as usize] = InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow);
arr[DUP9 as usize] = InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow);
arr[DUP10 as usize] = InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow);
arr[DUP11 as usize] = InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow);
arr[DUP12 as usize] = InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow);
arr[DUP13 as usize] = InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow);
arr[DUP14 as usize] = InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow);
arr[DUP15 as usize] = InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow);
arr[DUP16 as usize] = InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow);
arr[SWAP1 as usize] = InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow);
arr[SWAP2 as usize] = InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow);
arr[SWAP3 as usize] = InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow);
arr[SWAP4 as usize] = InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow);
arr[SWAP5 as usize] = InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow);
arr[SWAP6 as usize] = InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow);
arr[SWAP7 as usize] = InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow);
arr[SWAP8 as usize] = InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow);
arr[SWAP9 as usize] = InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow);
arr[SWAP10 as usize] = InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow);
arr[SWAP11 as usize] = InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow);
arr[SWAP12 as usize] = InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow);
arr[SWAP13 as usize] = InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow);
arr[SWAP14 as usize] = InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow);
arr[SWAP15 as usize] = InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow);
arr[SWAP16 as usize] = InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow);
arr[LOG0 as usize] = InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special);
arr[LOG1 as usize] = InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special);
arr[LOG2 as usize] = InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special);
arr[LOG3 as usize] = InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special);
arr[LOG4 as usize] = InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special);
arr[CREATE as usize] = InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special);
arr[CALL as usize] = InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special);
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special);
arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero);
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special);
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero);
arr
};
#[cfg_attr(rustfmt, rustfmt_skip)]
/// Return details about specific instruction
pub fn get_info(instruction: Instruction) -> InstructionInfo {
match instruction {
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),
SUB => InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow),
MUL => InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low),
DIV => InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low),
SDIV => InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low),
MOD => InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low),
SMOD => InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low),
EXP => InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special),
NOT => InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow),
LT => InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow),
GT => InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow),
SLT => InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow),
SGT => InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow),
EQ => InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow),
ISZERO => InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow),
AND => InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow),
OR => InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow),
XOR => InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow),
BYTE => InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow),
ADDMOD => InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid),
MULMOD => InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid),
SIGNEXTEND => InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low),
SHA3 => InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special),
ADDRESS => InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base),
BALANCE => InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext),
ORIGIN => InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base),
CALLER => InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base),
CALLVALUE => InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base),
CALLDATALOAD => InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow),
CALLDATASIZE => InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base),
CALLDATACOPY => InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow),
CODESIZE => InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base),
CODECOPY => InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow),
GASPRICE => InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base),
EXTCODESIZE => InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext),
EXTCODECOPY => InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext),
BLOCKHASH => InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext),
COINBASE => InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base),
TIMESTAMP => InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base),
NUMBER => InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base),
DIFFICULTY => InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base),
GASLIMIT => InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base),
POP => InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base),
MLOAD => InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow),
MSTORE => InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow),
MSTORE8 => InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow),
SLOAD => InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special),
SSTORE => InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special),
JUMP => InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid),
JUMPI => InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High),
PC => InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base),
MSIZE => InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base),
GAS => InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base),
JUMPDEST => InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special),
PUSH1 => InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow),
PUSH2 => InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow),
PUSH3 => InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow),
PUSH4 => InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow),
PUSH5 => InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow),
PUSH6 => InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow),
PUSH7 => InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow),
PUSH8 => InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow),
PUSH9 => InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow),
PUSH10 => InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow),
PUSH11 => InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow),
PUSH12 => InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow),
PUSH13 => InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow),
PUSH14 => InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow),
PUSH15 => InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow),
PUSH16 => InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow),
PUSH17 => InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow),
PUSH18 => InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow),
PUSH19 => InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow),
PUSH20 => InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow),
PUSH21 => InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow),
PUSH22 => InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow),
PUSH23 => InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow),
PUSH24 => InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow),
PUSH25 => InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow),
PUSH26 => InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow),
PUSH27 => InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow),
PUSH28 => InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow),
PUSH29 => InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow),
PUSH30 => InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow),
PUSH31 => InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow),
PUSH32 => InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow),
DUP1 => InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow),
DUP2 => InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow),
DUP3 => InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow),
DUP4 => InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow),
DUP5 => InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow),
DUP6 => InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow),
DUP7 => InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow),
DUP8 => InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow),
DUP9 => InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow),
DUP10 => InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow),
DUP11 => InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow),
DUP12 => InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow),
DUP13 => InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow),
DUP14 => InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow),
DUP15 => InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow),
DUP16 => InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow),
SWAP1 => InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow),
SWAP2 => InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow),
SWAP3 => InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow),
SWAP4 => InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow),
SWAP5 => InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow),
SWAP6 => InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow),
SWAP7 => InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow),
SWAP8 => InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow),
SWAP9 => InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow),
SWAP10 => InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow),
SWAP11 => InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow),
SWAP12 => InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow),
SWAP13 => InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow),
SWAP14 => InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow),
SWAP15 => InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow),
SWAP16 => InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow),
LOG0 => InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special),
LOG1 => InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special),
LOG2 => InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special),
LOG3 => InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special),
LOG4 => InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special),
CREATE => InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special),
CALL => InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special),
CALLCODE => InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special),
RETURN => InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero),
DELEGATECALL => InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special),
SUICIDE => InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero),
_ => InstructionInfo::new("INVALID_INSTRUCTION", 0, 0, 0, false, GasPriceTier::Invalid)
}
}
/// Virtual machine bytecode instruction.

View File

@@ -14,7 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Rust VM implementation
///! Rust VM implementation
use common::*;
use super::instructions as instructions;
use super::instructions::{Instruction, get_info};
use std::marker::Copy;
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
#[cfg(not(feature = "evm-debug"))]
macro_rules! evm_debug {
@@ -28,21 +34,6 @@ macro_rules! evm_debug {
}
}
mod gasometer;
mod stack;
mod memory;
use self::gasometer::Gasometer;
use self::stack::{Stack, VecStack};
use self::memory::Memory;
use std::marker::PhantomData;
use common::*;
use types::executed::CallType;
use super::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
use bit_set::BitSet;
#[cfg(feature = "evm-debug")]
fn color(instruction: Instruction, name: &'static str) -> String {
let c = instruction as usize % 6;
@@ -50,9 +41,209 @@ fn color(instruction: Instruction, name: &'static str) -> String {
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
}
macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(evm::Error::OutOfGas); }
v
}}
}
type CodePosition = usize;
type Gas = U256;
type ProgramCounter = usize;
/// Stack trait with VM-friendly API
trait Stack<T> {
/// Returns `Stack[len(Stack) - no_from_top]`
fn peek(&self, no_from_top: usize) -> &T;
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
fn swap_with_top(&mut self, no_from_top: usize);
/// Returns true if Stack has at least `no_of_elems` elements
fn has(&self, no_of_elems: usize) -> bool;
/// Get element from top and remove it from Stack. Panics if stack is empty.
fn pop_back(&mut self) -> T;
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
/// Add element on top of the Stack
fn push(&mut self, elem: T);
/// Get number of elements on Stack
fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
}
struct VecStack<S> {
stack: Vec<S>,
logs: [S; instructions::MAX_NO_OF_TOPICS]
}
impl<S : Copy> VecStack<S> {
fn with_capacity(capacity: usize, zero: S) -> Self {
VecStack {
stack: Vec::with_capacity(capacity),
logs: [zero; instructions::MAX_NO_OF_TOPICS]
}
}
}
impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn peek(&self, no_from_top: usize) -> &S {
&self.stack[self.stack.len() - no_from_top - 1]
}
fn swap_with_top(&mut self, no_from_top: usize) {
let len = self.stack.len();
self.stack.swap(len - no_from_top - 1, len - 1);
}
fn has(&self, no_of_elems: usize) -> bool {
self.stack.len() >= no_of_elems
}
fn pop_back(&mut self) -> S {
let val = self.stack.pop();
match val {
Some(x) => {
evm_debug!({
println!(" POP: {}", x)
});
x
},
None => panic!("Tried to pop from empty stack.")
}
}
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
for i in 0..no_of_elems {
self.logs[i] = self.pop_back();
}
&self.logs[0..no_of_elems]
}
fn push(&mut self, elem: S) {
evm_debug!({
println!(" PUSH: {}", elem)
});
self.stack.push(elem);
}
fn size(&self) -> usize {
self.stack.len()
}
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
}
}
trait Memory {
/// Retrieve current size of the memory
fn size(&self) -> usize;
/// Resize (shrink or expand) the memory to specified size (fills 0)
fn resize(&mut self, new_size: usize);
/// Resize the memory only if its smaller
fn expand(&mut self, new_size: usize);
/// Write single byte to memory
fn write_byte(&mut self, offset: U256, value: U256);
/// Write a word to memory. Does not resize memory!
fn write(&mut self, offset: U256, value: U256);
/// Read a word from memory
fn read(&self, offset: U256) -> U256;
/// Write slice of bytes to memory. Does not resize memory!
fn write_slice(&mut self, offset: U256, &[u8]);
/// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
fn dump(&self);
}
/// Checks whether offset and size is valid memory range
fn is_valid_range(off: usize, size: usize) -> bool {
// When size is zero we haven't actually expanded the memory
let overflow = off.overflowing_add(size).1;
size > 0 && !overflow
}
impl Memory for Vec<u8> {
fn dump(&self) {
println!("MemoryDump:");
for i in self.iter() {
println!("{:02x} ", i);
}
println!("");
}
fn size(&self) -> usize {
self.len()
}
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
let off = init_off_u.low_u64() as usize;
let size = init_size_u.low_u64() as usize;
if !is_valid_range(off, size) {
&self[0..0]
} else {
&self[off..off+size]
}
}
fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize;
U256::from(&self[off..off+32])
}
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
let off = offset.low_u64() as usize;
let s = size.low_u64() as usize;
if !is_valid_range(off, s) {
&mut self[0..0]
} else {
&mut self[off..off+s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
let off = offset.low_u64() as usize;
// TODO [todr] Optimize?
for pos in off..off+slice.len() {
self[pos] = slice[pos - off];
}
}
fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let mut val = value;
let end = off + 32;
for pos in 0..32 {
self[end - pos - 1] = val.low_u64() as u8;
val = val >> 8;
}
}
fn write_byte(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let val = value.low_u64() as u64;
self[off] = val as u8;
}
fn resize(&mut self, new_size: usize) {
self.resize(new_size, 0);
}
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
}
}
/// Abstraction over raw vector of Bytes. Easier state management of PC.
struct CodeReader<'a> {
position: ProgramCounter,
@@ -74,64 +265,66 @@ impl<'a> CodeReader<'a> {
}
}
enum InstructionResult<Gas> {
#[cfg_attr(feature="dev", allow(enum_variant_names))]
enum InstructionCost {
Gas(U256),
GasMem(U256, U256),
GasMemCopy(U256, U256, U256)
}
enum InstructionResult {
Ok,
UseAllGas,
GasLeft(Gas),
UnusedGas(Gas),
GasLeft(U256),
UnusedGas(U256),
JumpToPosition(U256),
// gas left, init_orf, init_size
StopExecutionNeedsReturn(Gas, U256, U256),
StopExecutionNeedsReturn(U256, U256, U256),
StopExecution,
}
/// Intepreter EVM implementation
#[derive(Default)]
pub struct Interpreter<Cost: CostType> {
pub struct Interpreter {
mem: Vec<u8>,
_type: PhantomData<Cost>,
}
impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
impl evm::Evm for Interpreter {
fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> {
self.mem.clear();
let code = &params.code.as_ref().unwrap();
let valid_jump_destinations = self.find_jump_destinations(code);
let valid_jump_destinations = self.find_jump_destinations(&code);
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
let mut current_gas = params.gas;
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
let mut reader = CodeReader {
position: 0,
code: code
code: &code
};
let infos = &*instructions::INSTRUCTIONS;
while reader.position < code.len() {
let instruction = code[reader.position];
reader.position += 1;
let info = infos[instruction as usize];
try!(self.verify_instruction(ext, instruction, &info, &stack));
// Calculate gas cost
let (gas_cost, mem_gas, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
// TODO: make compile-time removable if too much of a performance hit.
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &stack));
try!(gasometer.verify_gas(&gas_cost));
// TODO: make compile-time removable if too much of a performance hit.
let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost);
reader.position += 1;
try!(self.verify_gas(&current_gas, &gas_cost));
self.mem.expand(mem_size);
gasometer.current_mem_gas = mem_gas;
gasometer.current_gas = gasometer.current_gas - gas_cost;
current_gas = current_gas - gas_cost; //TODO: use operator -=
evm_debug!({
println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}",
reader.position,
color(instruction, info.name),
color(instruction, instructions::get_info(instruction).name),
instruction,
gas_cost,
gasometer.current_gas + gas_cost
current_gas + gas_cost
);
});
@@ -142,71 +335,175 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
// Execute instruction
let result = try!(self.exec_instruction(
gasometer.current_gas, &params, ext, instruction, &mut reader, &mut stack
current_gas, &params, ext, instruction, &mut reader, &mut stack
));
if trace_executed {
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
}
// Advance
match result {
InstructionResult::Ok => {},
InstructionResult::UnusedGas(gas) => {
gasometer.current_gas = gasometer.current_gas + gas;
current_gas = current_gas + gas; //TODO: use operator +=
},
InstructionResult::UseAllGas => {
gasometer.current_gas = Cost::from(0);
current_gas = U256::zero();
},
InstructionResult::GasLeft(gas_left) => {
gasometer.current_gas = gas_left;
current_gas = gas_left;
},
InstructionResult::JumpToPosition(position) => {
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
reader.position = pos;
},
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size)));
},
InstructionResult::StopExecution => break,
}
}
Ok(GasLeft::Known(gasometer.current_gas.as_u256()))
Ok(GasLeft::Known(current_gas))
}
}
impl<Cost: CostType> Interpreter<Cost> {
fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
impl Interpreter {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem(
&mut self,
ext: &evm::Ext,
instruction: Instruction,
stack: &Stack<U256>
) -> evm::Result<(U256, usize)> {
let schedule = ext.schedule();
let info = instructions::get_info(instruction);
if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL {
return Err(evm::Error::BadInstruction {
instruction: instruction
});
}
if info.tier == instructions::GasPriceTier::Invalid {
return Err(evm::Error::BadInstruction {
instruction: instruction
});
}
if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
instruction: info.name,
wanted: info.args,
on_stack: stack.size()
})
} else if stack.size() - info.args + info.ret > schedule.stack_limit {
Err(evm::Error::OutOfStack {
instruction: info.name,
wanted: info.ret - info.args,
limit: schedule.stack_limit
})
} else {
Ok(())
try!(self.verify_instructions_requirements(&info, schedule.stack_limit, stack));
let tier = instructions::get_tier_idx(info.tier);
let default_gas = U256::from(schedule.tier_step_gas[tier]);
let cost = match instruction {
instructions::SSTORE => {
let address = H256::from(stack.peek(0));
let newval = stack.peek(1);
let val = U256::from(ext.storage_at(&address).as_slice());
let gas = if self.is_zero(&val) && !self.is_zero(newval) {
schedule.sstore_set_gas
} else {
// Refund for below case is added when actually executing sstore
// !self.is_zero(&val) && self.is_zero(newval)
schedule.sstore_reset_gas
};
InstructionCost::Gas(U256::from(gas))
},
instructions::SLOAD => {
InstructionCost::Gas(U256::from(schedule.sload_gas))
},
instructions::MSTORE | instructions::MLOAD => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32)))
},
instructions::MSTORE8 => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1)))
},
instructions::RETURN => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::SHA3 => {
let w = overflowing!(add_u256_usize(stack.peek(1), 31));
let words = w >> 5;
let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words);
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALLDATACOPY | instructions::CODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), stack.peek(2).clone())
},
instructions::EXTCODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), stack.peek(3).clone())
},
instructions::JUMPDEST => {
InstructionCost::Gas(U256::one())
},
instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction);
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = overflowing!(stack.peek(1).overflowing_mul(U256::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflowing_add(U256::from(log_gas)));
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALL | instructions::CALLCODE => {
let mut gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
let mem = cmp::max(
try!(self.mem_needed(stack.peek(5), stack.peek(6))),
try!(self.mem_needed(stack.peek(3), stack.peek(4)))
);
let address = u256_to_address(stack.peek(1));
if instruction == instructions::CALL && !ext.exists(&address) {
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_new_account_gas)));
};
if stack.peek(2).clone() > U256::zero() {
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_value_transfer_gas)));
};
InstructionCost::GasMem(gas,mem)
},
instructions::DELEGATECALL => {
let gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
let mem = cmp::max(
try!(self.mem_needed(stack.peek(4), stack.peek(5))),
try!(self.mem_needed(stack.peek(2), stack.peek(3)))
);
InstructionCost::GasMem(gas, mem)
},
instructions::CREATE => {
let gas = U256::from(schedule.create_gas);
let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2)));
InstructionCost::GasMem(gas, mem)
},
instructions::EXP => {
let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = U256::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
InstructionCost::Gas(gas)
},
_ => InstructionCost::Gas(default_gas)
};
match cost {
InstructionCost::Gas(gas) => {
Ok((gas, 0))
},
InstructionCost::GasMem(gas, mem_size) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
let gas = overflowing!(gas.overflowing_add(mem_gas));
Ok((gas, new_mem_size))
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
let copy = overflowing!(add_u256_usize(&copy, 31));
let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32));
let gas = overflowing!(gas.overflowing_add(copy_gas));
let gas = overflowing!(gas.overflowing_add(mem_gas));
Ok((gas, new_mem_size))
}
}
}
@@ -235,16 +532,53 @@ impl<Cost: CostType> Interpreter<Cost> {
}
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> evm::Result<(U256, usize)> {
let gas_for_mem = |mem_size: U256| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
let a = overflowing!(s.overflowing_mul(U256::from(schedule.memory_gas)));
// We need to go to U512 to calculate s*s/quad_coeff_div
let b = U512::from(s) * U512::from(s) / U512::from(schedule.quad_coeff_div);
if b > U512::from(!U256::zero()) {
Err(evm::Error::OutOfGas)
} else {
Ok(overflowing!(a.overflowing_add(U256::from(b))))
}
};
let current_mem_size = U256::from(current_mem_size);
let req_mem_size_rounded = (overflowing!(mem_size.overflowing_add(U256::from(31))) >> 5) << 5;
let new_mem_gas = try!(gas_for_mem(U256::from(req_mem_size_rounded)));
let current_mem_gas = try!(gas_for_mem(current_mem_size));
Ok((if req_mem_size_rounded > current_mem_size {
new_mem_gas - current_mem_gas
} else {
U256::zero()
}, req_mem_size_rounded.low_u64() as usize))
}
fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result<U256> {
Ok(overflowing!(mem.overflowing_add(U256::from(add))))
}
fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result<U256> {
if self.is_zero(size) {
return Ok(U256::zero());
}
Ok(overflowing!(offset.overflowing_add(size.clone())))
}
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction(
&mut self,
gas: Cost,
gas: Gas,
params: &ActionParams,
ext: &mut evm::Ext,
instruction: Instruction,
code: &mut CodeReader,
stack: &mut Stack<U256>
) -> evm::Result<InstructionResult<Cost>> {
) -> evm::Result<InstructionResult> {
match instruction {
instructions::JUMP => {
let jump = stack.pop_back();
@@ -277,11 +611,11 @@ impl<Cost: CostType> Interpreter<Cost> {
return Ok(InstructionResult::Ok);
}
let create_result = ext.create(&gas.as_u256(), &endowment, contract_code);
let create_result = ext.create(&gas, &endowment, &contract_code);
return match create_result {
ContractCreateResult::Created(address, gas_left) => {
stack.push(address_to_u256(address));
Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
Ok(InstructionResult::GasLeft(gas_left))
},
ContractCreateResult::Failed => {
stack.push(U256::zero());
@@ -292,7 +626,7 @@ impl<Cost: CostType> Interpreter<Cost> {
},
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated.");
let call_gas = stack.pop_back();
let code_address = stack.pop_back();
let code_address = u256_to_address(&code_address);
@@ -308,22 +642,22 @@ impl<Cost: CostType> Interpreter<Cost> {
let out_size = stack.pop_back();
// Add stipend (only CALL|CALLCODE when value > 0)
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val.is_zero() {
false => Cost::from(ext.schedule().call_stipend),
true => Cost::from(0)
let call_gas = call_gas + value.map_or_else(U256::zero, |val| match val > U256::zero() {
true => U256::from(ext.schedule().call_stipend),
false => U256::zero()
});
// Get sender & receive addresses, check if we have balance
let (sender_address, receive_address, has_balance, call_type) = match instruction {
let (sender_address, receive_address, has_balance) = match instruction {
instructions::CALL => {
let has_balance = ext.balance(&params.address) >= value.unwrap();
(&params.address, &code_address, has_balance, CallType::Call)
(&params.address, &code_address, has_balance)
},
instructions::CALLCODE => {
let has_balance = ext.balance(&params.address) >= value.unwrap();
(&params.address, &params.address, has_balance, CallType::CallCode)
(&params.address, &params.address, has_balance)
},
instructions::DELEGATECALL => (&params.sender, &params.address, true, CallType::DelegateCall),
instructions::DELEGATECALL => (&params.sender, &params.address, true),
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
};
@@ -338,13 +672,13 @@ impl<Cost: CostType> Interpreter<Cost> {
// and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output, call_type)
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output)
};
return match call_result {
MessageCallResult::Success(gas_left) => {
stack.push(U256::one());
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
Ok(InstructionResult::UnusedGas(gas_left))
},
MessageCallResult::Failed => {
stack.push(U256::zero());
@@ -425,7 +759,7 @@ impl<Cost: CostType> Interpreter<Cost> {
stack.push(U256::from(code.position - 1));
},
instructions::GAS => {
stack.push(gas.as_u256());
stack.push(gas.clone());
},
instructions::ADDRESS => {
stack.push(address_to_u256(params.address.clone()));
@@ -515,11 +849,11 @@ impl<Cost: CostType> Interpreter<Cost> {
Ok(InstructionResult::Ok)
}
fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, source: &[u8]) {
fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, data: &[u8]) {
let dest_offset = stack.pop_back();
let source_offset = stack.pop_back();
let size = stack.pop_back();
let source_size = U256::from(source.len());
let source_size = U256::from(data.len());
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
true => {
@@ -531,21 +865,51 @@ impl<Cost: CostType> Interpreter<Cost> {
for i in zero_slice.iter_mut() {
*i = 0;
}
source.len()
data.len()
},
false => (size.low_u64() + source_offset.low_u64()) as usize
};
if source_offset < source_size {
let output_begin = source_offset.low_u64() as usize;
self.mem.write_slice(dest_offset, &source[output_begin..output_end]);
self.mem.write_slice(dest_offset, &data[output_begin..output_end]);
}
}
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> evm::Result<usize> {
fn verify_instructions_requirements(
&self,
info: &instructions::InstructionInfo,
stack_limit: usize,
stack: &Stack<U256>
) -> evm::Result<()> {
if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
instruction: info.name,
wanted: info.args,
on_stack: stack.size()
})
} else if stack.size() - info.args + info.ret > stack_limit {
Err(evm::Error::OutOfStack {
instruction: info.name,
wanted: info.ret - info.args,
limit: stack_limit
})
} else {
Ok(())
}
}
fn verify_gas(&self, current_gas: &U256, gas_cost: &U256) -> evm::Result<()> {
match current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
false => Ok(())
}
}
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet<usize>) -> evm::Result<usize> {
let jump = jump_u.low_u64() as usize;
if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
if valid_jump_destinations.contains(&jump) && jump_u < U256::from(!0 as usize) {
Ok(jump)
} else {
Err(evm::Error::BadJumpDestination {
@@ -555,7 +919,7 @@ impl<Cost: CostType> Interpreter<Cost> {
}
fn is_zero(&self, val: &U256) -> bool {
val.is_zero()
&U256::zero() == val
}
fn bool_to_u256(&self, val: bool) -> U256 {
@@ -767,8 +1131,8 @@ impl<Cost: CostType> Interpreter<Cost> {
Ok(())
}
fn find_jump_destinations(&self, code: &[u8]) -> BitSet {
let mut jump_dests = BitSet::with_capacity(code.len());
fn find_jump_destinations(&self, code: &[u8]) -> HashSet<CodePosition> {
let mut jump_dests = HashSet::new();
let mut position = 0;
while position < code.len() {
@@ -787,8 +1151,7 @@ impl<Cost: CostType> Interpreter<Cost> {
}
fn get_and_reset_sign(value: U256) -> (U256, bool) {
let U256(arr) = value;
let sign = arr[3].leading_zeros() == 0;
let sign = (value >> 255).low_u64() == 1;
(set_sign(value, sign), sign)
}
@@ -800,6 +1163,11 @@ fn set_sign(value: U256, sign: bool) -> U256 {
}
}
#[inline]
fn add_u256_usize(value: &U256, num: usize) -> (U256, bool) {
value.clone().overflowing_add(U256::from(num))
}
#[inline]
fn u256_to_address(value: &U256) -> Address {
Address::from(H256::from(value))
@@ -811,14 +1179,82 @@ fn address_to_u256(value: Address) -> U256 {
}
#[test]
fn test_find_jump_destinations() {
fn test_mem_gas_cost() {
// given
let interpreter = Interpreter::<U256>::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
let interpreter = Interpreter::default();
let schedule = evm::Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
// when
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
let result = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
// then
assert!(valid_jump_destinations.contains(66));
if let Ok(_) = result {
assert!(false, "Should fail with OutOfGas");
}
}
#[cfg(test)]
mod tests {
use common::*;
use super::*;
use evm;
#[test]
fn test_find_jump_destinations() {
// given
let interpreter = Interpreter::default();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
// then
assert!(valid_jump_destinations.contains(&66));
}
#[test]
fn test_calculate_mem_cost() {
// given
let interpreter = Interpreter::default();
let schedule = evm::Schedule::default();
let current_mem_size = 0;
let mem_size = U256::from(5);
// when
let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then
assert_eq!(mem_cost, U256::from(3));
assert_eq!(mem_size, 32);
}
#[test]
fn test_memory_read_and_write() {
// given
let mem: &mut super::Memory = &mut vec![];
mem.resize(0x80 + 32);
// when
mem.write(U256::from(0x80), U256::from(0xabcdef));
// then
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_and_write_byte() {
// given
let mem: &mut super::Memory = &mut vec![];
mem.resize(32);
// when
mem.write_byte(U256::from(0x1d), U256::from(0xab));
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
mem.write_byte(U256::from(0x1f), U256::from(0xef));
// then
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
}
}

View File

@@ -1,261 +0,0 @@
// 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 common::*;
use super::u256_to_address;
use evm::{self, CostType};
use evm::instructions::{self, Instruction, InstructionInfo};
use evm::interpreter::stack::Stack;
macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(evm::Error::OutOfGas); }
v
}}
}
#[cfg_attr(feature="dev", allow(enum_variant_names))]
enum InstructionCost<Cost: CostType> {
Gas(Cost),
GasMem(Cost, Cost),
GasMemCopy(Cost, Cost, Cost)
}
pub struct Gasometer<Gas: CostType> {
pub current_gas: Gas,
pub current_mem_gas: Gas,
}
impl<Gas: CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self {
Gasometer {
current_gas: current_gas,
current_mem_gas: Gas::from(0),
}
}
pub fn verify_gas(&self, gas_cost: &Gas) -> evm::Result<()> {
match &self.current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
false => Ok(())
}
}
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
pub fn get_gas_cost_mem(
&mut self,
ext: &evm::Ext,
instruction: Instruction,
info: &InstructionInfo,
stack: &Stack<U256>,
current_mem_size: usize,
) -> evm::Result<(Gas, Gas, usize)> {
let schedule = ext.schedule();
let tier = instructions::get_tier_idx(info.tier);
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let cost = match instruction {
instructions::JUMPDEST => {
InstructionCost::Gas(Gas::from(1))
},
instructions::SSTORE => {
let address = H256::from(stack.peek(0));
let newval = stack.peek(1);
let val = U256::from(ext.storage_at(&address).as_slice());
let gas = if val.is_zero() && !newval.is_zero() {
schedule.sstore_set_gas
} else {
// Refund for below case is added when actually executing sstore
// !is_zero(&val) && is_zero(newval)
schedule.sstore_reset_gas
};
InstructionCost::Gas(Gas::from(gas))
},
instructions::SLOAD => {
InstructionCost::Gas(Gas::from(schedule.sload_gas))
},
instructions::MSTORE | instructions::MLOAD => {
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
},
instructions::MSTORE8 => {
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
},
instructions::RETURN => {
InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::SHA3 => {
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
let words = w >> 5;
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALLDATACOPY | instructions::CODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
},
instructions::EXTCODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
},
instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction);
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
},
instructions::CALL | instructions::CALLCODE => {
let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
let mem = cmp::max(
try!(mem_needed(stack.peek(5), stack.peek(6))),
try!(mem_needed(stack.peek(3), stack.peek(4)))
);
let address = u256_to_address(stack.peek(1));
if instruction == instructions::CALL && !ext.exists(&address) {
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
};
if !stack.peek(2).is_zero() {
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
};
InstructionCost::GasMem(gas,mem)
},
instructions::DELEGATECALL => {
let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
let mem = cmp::max(
try!(mem_needed(stack.peek(4), stack.peek(5))),
try!(mem_needed(stack.peek(2), stack.peek(3)))
);
InstructionCost::GasMem(gas, mem)
},
instructions::CREATE => {
let gas = Gas::from(schedule.create_gas);
let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
InstructionCost::GasMem(gas, mem)
},
instructions::EXP => {
let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
InstructionCost::Gas(gas)
},
_ => InstructionCost::Gas(default_gas)
};
match cost {
InstructionCost::Gas(gas) => {
Ok((gas, self.current_mem_gas, 0))
},
InstructionCost::GasMem(gas, mem_size) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
Ok((gas, new_mem_gas, new_mem_size))
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
let copy = overflowing!(add_gas_usize(copy, 31)) >> 5;
let copy_gas = Gas::from(schedule.copy_gas) * copy;
let gas = overflowing!(gas.overflow_add(copy_gas));
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
Ok((gas, new_mem_gas, new_mem_size))
}
}
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
let gas_for_mem = |mem_size: Gas| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
// Calculate s*s/quad_coeff_div
debug_assert_eq!(schedule.quad_coeff_div, 512);
let b = overflowing!(s.overflow_mul_shr(s, 9));
Ok(overflowing!(a.overflow_add(b)))
};
let current_mem_size = Gas::from(current_mem_size);
let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size {
let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded));
(new_mem_gas - self.current_mem_gas, new_mem_gas)
} else {
(Gas::from(0), self.current_mem_gas)
};
Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize()))
}
}
#[inline]
fn mem_needed_const<Gas: CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}
#[inline]
fn mem_needed<Gas: CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
if size.is_zero() {
return Ok(Gas::from(0));
}
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
}
#[inline]
fn add_gas_usize<Gas: CostType>(value: Gas, num: usize) -> (Gas, bool) {
value.overflow_add(Gas::from(num))
}
#[test]
fn test_mem_gas_cost() {
// given
let gasometer = Gasometer::<U256>::new(U256::zero());
let schedule = evm::Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
// when
let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size);
// then
if let Ok(_) = result {
assert!(false, "Should fail with OutOfGas");
}
}
#[test]
fn test_calculate_mem_cost() {
// given
let gasometer = Gasometer::<usize>::new(0);
let schedule = evm::Schedule::default();
let current_mem_size = 0;
let mem_size = 5;
// when
let (mem_cost, new_mem_gas, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then
assert_eq!(mem_cost, 3);
assert_eq!(new_mem_gas, 3);
assert_eq!(mem_size, 32);
}

View File

@@ -1,144 +0,0 @@
// 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::{U256, Uint};
pub trait Memory {
/// Retrieve current size of the memory
fn size(&self) -> usize;
/// Resize (shrink or expand) the memory to specified size (fills 0)
fn resize(&mut self, new_size: usize);
/// Resize the memory only if its smaller
fn expand(&mut self, new_size: usize);
/// Write single byte to memory
fn write_byte(&mut self, offset: U256, value: U256);
/// Write a word to memory. Does not resize memory!
fn write(&mut self, offset: U256, value: U256);
/// Read a word from memory
fn read(&self, offset: U256) -> U256;
/// Write slice of bytes to memory. Does not resize memory!
fn write_slice(&mut self, offset: U256, &[u8]);
/// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
fn dump(&self);
}
/// Checks whether offset and size is valid memory range
fn is_valid_range(off: usize, size: usize) -> bool {
// When size is zero we haven't actually expanded the memory
let overflow = off.overflowing_add(size).1;
size > 0 && !overflow
}
impl Memory for Vec<u8> {
fn dump(&self) {
println!("MemoryDump:");
for i in self.iter() {
println!("{:02x} ", i);
}
println!("");
}
fn size(&self) -> usize {
self.len()
}
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
let off = init_off_u.low_u64() as usize;
let size = init_size_u.low_u64() as usize;
if !is_valid_range(off, size) {
&self[0..0]
} else {
&self[off..off+size]
}
}
fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize;
U256::from(&self[off..off+32])
}
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
let off = offset.low_u64() as usize;
let s = size.low_u64() as usize;
if !is_valid_range(off, s) {
&mut self[0..0]
} else {
&mut self[off..off+s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
let off = offset.low_u64() as usize;
// TODO [todr] Optimize?
for pos in off..off+slice.len() {
self[pos] = slice[pos - off];
}
}
fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
value.to_big_endian(&mut self[off..off+32]);
}
fn write_byte(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let val = value.low_u64() as u64;
self[off] = val as u8;
}
fn resize(&mut self, new_size: usize) {
self.resize(new_size, 0);
}
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
}
}
#[test]
fn test_memory_read_and_write() {
// given
let mem: &mut Memory = &mut vec![];
mem.resize(0x80 + 32);
// when
mem.write(U256::from(0x80), U256::from(0xabcdef));
// then
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_and_write_byte() {
// given
let mem: &mut Memory = &mut vec![];
mem.resize(32);
// when
mem.write_byte(U256::from(0x1d), U256::from(0xab));
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
mem.write_byte(U256::from(0x1f), U256::from(0xef));
// then
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
}

View File

@@ -1,106 +0,0 @@
// 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 std::fmt;
use evm::instructions;
/// Stack trait with VM-friendly API
pub trait Stack<T> {
/// Returns `Stack[len(Stack) - no_from_top]`
fn peek(&self, no_from_top: usize) -> &T;
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
fn swap_with_top(&mut self, no_from_top: usize);
/// Returns true if Stack has at least `no_of_elems` elements
fn has(&self, no_of_elems: usize) -> bool;
/// Get element from top and remove it from Stack. Panics if stack is empty.
fn pop_back(&mut self) -> T;
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
/// Add element on top of the Stack
fn push(&mut self, elem: T);
/// Get number of elements on Stack
fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
}
pub struct VecStack<S> {
stack: Vec<S>,
logs: [S; instructions::MAX_NO_OF_TOPICS]
}
impl<S : Copy> VecStack<S> {
pub fn with_capacity(capacity: usize, zero: S) -> Self {
VecStack {
stack: Vec::with_capacity(capacity),
logs: [zero; instructions::MAX_NO_OF_TOPICS]
}
}
}
impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn peek(&self, no_from_top: usize) -> &S {
&self.stack[self.stack.len() - no_from_top - 1]
}
fn swap_with_top(&mut self, no_from_top: usize) {
let len = self.stack.len();
self.stack.swap(len - no_from_top - 1, len - 1);
}
fn has(&self, no_of_elems: usize) -> bool {
self.stack.len() >= no_of_elems
}
fn pop_back(&mut self) -> S {
let val = self.stack.pop();
match val {
Some(x) => {
evm_debug!({
println!(" POP: {}", x)
});
x
},
None => panic!("Tried to pop from empty stack.")
}
}
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
for i in 0..no_of_elems {
self.logs[i] = self.pop_back();
}
&self.logs[0..no_of_elems]
}
fn push(&mut self, elem: S) {
evm_debug!({
println!(" PUSH: {}", elem)
});
self.stack.push(elem);
}
fn size(&self) -> usize {
self.stack.len()
}
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
}
}

View File

@@ -28,11 +28,9 @@ mod jit;
#[cfg(test)]
mod tests;
#[cfg(all(feature="benches", test))]
mod benches;
pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType};
pub use self::evm::{Evm, Error, Finalize, GasLeft, Result};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule;
pub use types::executed::CallType;
pub use self::instructions::get_info;

View File

@@ -80,6 +80,8 @@ pub struct Schedule {
pub tx_data_non_zero_gas: usize,
/// Gas price for copying memory
pub copy_gas: usize,
/// DAO Rescue softfork block
pub reject_dao_transactions: bool,
}
impl Schedule {
@@ -125,18 +127,8 @@ impl Schedule {
tx_create_gas: tcg,
tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68,
copy_gas: 3,
copy_gas: 3,
reject_dao_transactions: false,
}
}
}
#[test]
#[cfg(test)]
fn schedule_evm_assumptions() {
let s1 = Schedule::new_frontier();
let s2 = Schedule::new_homestead();
// To optimize division we assume 2**9 for quad_coeff_div
assert_eq!(s1.quad_coeff_div, 512);
assert_eq!(s2.quad_coeff_div, 512);
}

View File

@@ -15,36 +15,35 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*;
use types::executed::CallType;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug;
pub struct FakeLogEntry {
struct FakeLogEntry {
topics: Vec<H256>,
data: Bytes
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum FakeCallType {
enum FakeCallType {
Call, Create
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct FakeCall {
struct FakeCall {
call_type: FakeCallType,
gas: U256,
sender_address: Option<Address>,
receive_address: Option<Address>,
value: Option<U256>,
data: Bytes,
code_address: Option<Address>,
code_address: Option<Address>
}
/// Fake externalities test structure.
///
/// Can't do recursive calls.
#[derive(Default)]
pub struct FakeExt {
struct FakeExt {
sstore_clears: usize,
depth: usize,
store: HashMap<H256, H256>,
@@ -68,7 +67,7 @@ fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
}
impl FakeExt {
pub fn new() -> Self {
fn new() -> Self {
FakeExt::default()
}
}
@@ -120,9 +119,7 @@ impl Ext for FakeExt {
value: Option<U256>,
data: &[u8],
code_address: &Address,
_output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
_output: &mut [u8]) -> MessageCallResult {
self.calls.insert(FakeCall {
call_type: FakeCallType::Call,
@@ -184,7 +181,7 @@ fn test_stack_underflow() {
let mut ext = FakeExt::new();
let err = {
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::default());
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::default());
test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
@@ -211,7 +208,7 @@ fn test_add(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -231,7 +228,7 @@ fn test_sha3(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -251,7 +248,7 @@ fn test_address(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -273,7 +270,7 @@ fn test_origin(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -295,7 +292,7 @@ fn test_sender(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -330,7 +327,7 @@ fn test_extcodecopy(factory: super::Factory) {
ext.codes.insert(sender, sender_code);
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -350,7 +347,7 @@ fn test_log_empty(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -382,7 +379,7 @@ fn test_log_sender(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -407,7 +404,7 @@ fn test_blockhash(factory: super::Factory) {
ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -429,7 +426,7 @@ fn test_calldataload(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -450,7 +447,7 @@ fn test_author(factory: super::Factory) {
ext.info.author = author;
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -470,7 +467,7 @@ fn test_timestamp(factory: super::Factory) {
ext.info.timestamp = timestamp;
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -490,7 +487,7 @@ fn test_number(factory: super::Factory) {
ext.info.number = number;
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -510,7 +507,7 @@ fn test_difficulty(factory: super::Factory) {
ext.info.difficulty = difficulty;
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -530,7 +527,7 @@ fn test_gas_limit(factory: super::Factory) {
ext.info.gas_limit = gas_limit;
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -548,7 +545,7 @@ fn test_mul(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -566,7 +563,7 @@ fn test_sub(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -584,7 +581,7 @@ fn test_div(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -602,7 +599,7 @@ fn test_div_zero(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -620,7 +617,7 @@ fn test_mod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -639,7 +636,7 @@ fn test_smod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -658,7 +655,7 @@ fn test_sdiv(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -677,7 +674,7 @@ fn test_exp(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -697,7 +694,7 @@ fn test_comparison(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -718,7 +715,7 @@ fn test_signed_comparison(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -739,7 +736,7 @@ fn test_bitops(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -762,7 +759,7 @@ fn test_addmod_mulmod(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -783,7 +780,7 @@ fn test_byte(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -802,7 +799,7 @@ fn test_signextend(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -822,7 +819,7 @@ fn test_badinstruction_int() {
let mut ext = FakeExt::new();
let err = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
@@ -842,7 +839,7 @@ fn test_pop(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -862,7 +859,7 @@ fn test_extops(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -885,7 +882,7 @@ fn test_jumps(factory: super::Factory) {
let mut ext = FakeExt::new();
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
@@ -914,7 +911,7 @@ fn test_calls(factory: super::Factory) {
};
let gas_left = {
let mut vm = factory.create(params.gas);
let mut vm = factory.create();
test_finalize(vm.exec(params, &mut ext)).unwrap()
};

View File

@@ -17,12 +17,11 @@
//! Transaction Execution environment.
use common::*;
use state::*;
use engines::Engine;
use types::executed::CallType;
use engine::*;
use evm::{self, Ext, Factory, Finalize};
use externalities::*;
use substate::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam;
pub use types::executed::{Executed, ExecutionResult};
@@ -40,7 +39,6 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
}
/// Transaction execution options.
#[derive(Default)]
pub struct TransactOptions {
/// Enable call tracing.
pub tracing: bool,
@@ -99,11 +97,11 @@ impl<'a> Executive<'a> {
let check = options.check_nonce;
match options.tracing {
true => match options.vm_tracing {
true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::toplevel()),
true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()),
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
},
false => match options.vm_tracing {
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::toplevel()),
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()),
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
},
}
@@ -175,7 +173,6 @@ impl<'a> Executive<'a> {
value: ActionValue::Transfer(t.value),
code: Some(t.data.clone()),
data: None,
call_type: CallType::None,
};
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
},
@@ -190,7 +187,6 @@ impl<'a> Executive<'a> {
value: ActionValue::Transfer(t.value),
code: self.state.code(address),
data: Some(t.data.clone()),
call_type: CallType::Call,
};
// TODO: move output upstream
let mut out = vec![];
@@ -199,7 +195,7 @@ impl<'a> Executive<'a> {
};
// finalize here!
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())))
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
}
fn exec_vm<T, V>(
@@ -215,7 +211,7 @@ impl<'a> Executive<'a> {
let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext);
return vm_factory.create().exec(params, &mut ext).finalize(ext);
}
// Start in new thread to reset stack
@@ -226,7 +222,7 @@ impl<'a> Executive<'a> {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || {
vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext)
vm_factory.create().exec(params, &mut ext).finalize(ext)
})
}).join()
}
@@ -252,6 +248,8 @@ impl<'a> Executive<'a> {
}
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
let delegate_call = params.code_address != params.address;
if self.engine.is_builtin(&params.code_address) {
// if destination is builtin, try to execute it
@@ -261,33 +259,38 @@ impl<'a> Executive<'a> {
let trace_info = tracer.prepare_trace_call(&params);
let cost = self.engine.cost_of_builtin(&params.code_address, data);
if cost <= params.gas {
self.engine.execute_builtin(&params.code_address, data, &mut output);
self.state.clear_snapshot();
match cost <= params.gas {
true => {
self.engine.execute_builtin(&params.code_address, data, &mut output);
self.state.clear_snapshot();
// trace only top level calls to builtins to avoid DDoS attacks
if self.depth == 0 {
let mut trace_output = tracer.prepare_trace_output();
if let Some(mut out) = trace_output.as_mut() {
*out = output.to_owned();
// trace only top level calls to builtins to avoid DDoS attacks
if self.depth == 0 {
let mut trace_output = tracer.prepare_trace_output();
if let Some(mut out) = trace_output.as_mut() {
*out = output.to_owned();
}
tracer.trace_call(
trace_info,
cost,
trace_output,
self.depth,
vec![],
delegate_call
);
}
tracer.trace_call(
trace_info,
cost,
trace_output,
vec![]
);
}
Ok(params.gas - cost)
} else {
Ok(params.gas - cost)
},
// just drain the whole gas
self.state.revert_snapshot();
false => {
self.state.revert_snapshot();
tracer.trace_failed_call(trace_info, vec![]);
tracer.trace_failed_call(trace_info, self.depth, vec![], delegate_call);
Err(evm::Error::OutOfGas)
Err(evm::Error::OutOfGas)
}
}
} else {
let trace_info = tracer.prepare_trace_call(&params);
@@ -301,7 +304,7 @@ impl<'a> Executive<'a> {
let mut unconfirmed_substate = Substate::new();
// TODO: make ActionParams pass by ref then avoid copy altogether.
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is conditional on params.code.is_some(); qed"));
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition"));
let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
@@ -317,9 +320,11 @@ impl<'a> Executive<'a> {
trace_info,
gas - gas_left,
trace_output,
traces
self.depth,
traces,
delegate_call
),
_ => tracer.trace_failed_call(trace_info, traces),
_ => tracer.trace_failed_call(trace_info, self.depth, traces, delegate_call),
};
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
@@ -331,7 +336,7 @@ impl<'a> Executive<'a> {
// otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.clear_snapshot();
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
tracer.trace_call(trace_info, U256::zero(), trace_output, self.depth, vec![], delegate_call);
Ok(params.gas)
}
}
@@ -368,7 +373,7 @@ impl<'a> Executive<'a> {
let gas = params.gas;
let created = params.address.clone();
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
let mut subvmtracer = vm_tracer.prepare_subtrace(&params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
@@ -382,9 +387,10 @@ impl<'a> Executive<'a> {
gas - gas_left,
trace_output,
created,
self.depth,
subtracer.traces()
),
_ => tracer.trace_failed_create(trace_info, subtracer.traces())
_ => tracer.trace_failed_create(trace_info, self.depth, subtracer.traces())
};
self.enact_result(&res, substate, unconfirmed_substate);
@@ -398,7 +404,7 @@ impl<'a> Executive<'a> {
substate: Substate,
result: evm::Result<U256>,
output: Bytes,
trace: Vec<FlatTrace>,
trace: Option<Trace>,
vm_trace: Option<VMTrace>
) -> ExecutionResult {
let schedule = self.engine.schedule(self.info);
@@ -490,9 +496,8 @@ mod tests {
use substate::*;
use tests::helpers::*;
use trace::trace;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
use types::executed::CallType;
#[test]
fn test_contract_address() {
@@ -626,7 +631,6 @@ mod tests {
params.gas = U256::from(100_000);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100));
params.call_type = CallType::Call;
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100));
@@ -634,7 +638,7 @@ mod tests {
let engine = TestEngine::new(5);
let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default();
let mut vm_tracer = ExecutiveVMTracer::toplevel();
let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
@@ -644,37 +648,35 @@ mod tests {
assert_eq!(gas_left, U256::from(44_752));
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 1,
let expected_trace = vec![ Trace {
depth: 0,
action: trace::Action::Call(trace::Call {
from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(),
to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
value: 100.into(),
gas: 100000.into(),
input: vec![],
call_type: CallType::Call,
}),
result: trace::Res::Call(trace::CallResult {
gas_used: U256::from(55_248),
output: vec![],
}),
}, FlatTrace {
trace_address: vec![0].into_iter().collect(),
subtraces: 0,
action: trace::Action::Create(trace::Create {
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
value: 23.into(),
gas: 67979.into(),
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
}),
result: trace::Res::Create(trace::CreateResult {
gas_used: U256::from(3224),
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
subs: vec![Trace {
depth: 1,
action: trace::Action::Create(trace::Create {
from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(),
value: 23.into(),
gas: 67979.into(),
init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85]
}),
result: trace::Res::Create(trace::CreateResult {
gas_used: U256::from(3224),
address: Address::from_str("c6d80f262ae5e0f164e5fde365044d7ada2bfa34").unwrap(),
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
subs: vec![]
}]
}];
assert_eq!(tracer.traces(), expected_trace);
let expected_vm_trace = VMTrace {
@@ -693,7 +695,7 @@ mod tests {
],
subs: vec![
VMTrace {
parent_step: 6,
parent_step: 7,
code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85],
operations: vec![
VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) },
@@ -743,7 +745,7 @@ mod tests {
let engine = TestEngine::new(5);
let mut substate = Substate::new();
let mut tracer = ExecutiveTracer::default();
let mut vm_tracer = ExecutiveVMTracer::toplevel();
let mut vm_tracer = ExecutiveVMTracer::default();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
@@ -752,9 +754,8 @@ mod tests {
assert_eq!(gas_left, U256::from(96_776));
let expected_trace = vec![FlatTrace {
trace_address: Default::default(),
subtraces: 0,
let expected_trace = vec![Trace {
depth: 0,
action: trace::Action::Create(trace::Create {
from: params.sender,
value: 100.into(),
@@ -766,8 +767,8 @@ mod tests {
address: params.address,
code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53]
}),
subs: vec![]
}];
assert_eq!(tracer.traces(), expected_trace);
let expected_vm_trace = VMTrace {
@@ -1011,7 +1012,7 @@ mod tests {
gas: U256::from(100_000),
gas_price: U256::zero(),
nonce: U256::zero()
}.sign(keypair.secret());
}.sign(&keypair.secret());
let sender = t.sender().unwrap();
let contract = contract_address(&sender, &U256::zero());
@@ -1078,7 +1079,7 @@ mod tests {
gas: U256::from(100_000),
gas_price: U256::zero(),
nonce: U256::one()
}.sign(keypair.secret());
}.sign(&keypair.secret());
let sender = t.sender().unwrap();
let mut state_result = get_temp_state();
@@ -1111,7 +1112,7 @@ mod tests {
gas: U256::from(80_001),
gas_price: U256::zero(),
nonce: U256::zero()
}.sign(keypair.secret());
}.sign(&keypair.secret());
let sender = t.sender().unwrap();
let mut state_result = get_temp_state();
@@ -1146,7 +1147,7 @@ mod tests {
gas: U256::from(100_000),
gas_price: U256::one(),
nonce: U256::zero()
}.sign(keypair.secret());
}.sign(&keypair.secret());
let sender = t.sender().unwrap();
let mut state_result = get_temp_state();

View File

@@ -17,11 +17,10 @@
//! Transaction Execution environment.
use common::*;
use state::*;
use engines::Engine;
use engine::*;
use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*;
use types::executed::CallType;
use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode.
@@ -149,7 +148,6 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
value: ActionValue::Transfer(*value),
code: Some(code.to_vec()),
data: None,
call_type: CallType::None,
};
self.state.inc_nonce(&self.origin_info.address);
@@ -172,8 +170,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
value: Option<U256>,
data: &[u8],
code_address: &Address,
output: &mut [u8],
call_type: CallType
output: &mut [u8]
) -> MessageCallResult {
trace!(target: "externalities", "call");
@@ -187,7 +184,6 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
gas_price: self.origin_info.gas_price,
code: self.state.code(code_address),
data: Some(data.to_vec()),
call_type: call_type,
};
if let Some(value) = value {
@@ -266,8 +262,6 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
self.state.transfer_balance(&address, refund_address, &balance);
}
self.tracer.trace_suicide(address, balance, refund_address.clone());
self.substate.suicides.insert(address);
}
@@ -276,7 +270,7 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
}
fn env_info(&self) -> &EnvInfo {
self.env_info
&self.env_info
}
fn depth(&self) -> usize {
@@ -300,14 +294,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
mod tests {
use common::*;
use state::*;
use engines::Engine;
use engine::*;
use evm::{Ext};
use substate::*;
use tests::helpers::*;
use devtools::GuardedTempResult;
use super::*;
use trace::{NoopTracer, NoopVMTracer};
use types::executed::CallType;
fn get_test_origin() -> OriginInfo {
OriginInfo {
@@ -324,7 +316,7 @@ mod tests {
author: 0.into(),
timestamp: 0,
difficulty: 0.into(),
last_hashes: Arc::new(vec![]),
last_hashes: vec![],
gas_used: 0.into(),
gas_limit: 0.into(),
}
@@ -332,7 +324,7 @@ mod tests {
struct TestSetup {
state: GuardedTempResult<State>,
engine: Arc<Engine>,
engine: Box<Engine>,
sub_state: Substate,
env_info: EnvInfo
}
@@ -391,9 +383,7 @@ mod tests {
{
let env_info = &mut setup.env_info;
env_info.number = test_env_number;
let mut last_hashes = (*env_info.last_hashes).clone();
last_hashes.push(test_hash.clone());
env_info.last_hashes = Arc::new(last_hashes);
env_info.last_hashes.push(test_hash.clone());
}
let state = setup.state.reference_mut();
let mut tracer = NoopTracer;
@@ -428,9 +418,7 @@ mod tests {
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
&[],
&Address::new(),
&mut output,
CallType::Call
);
&mut output);
}
#[test]
@@ -464,7 +452,7 @@ mod tests {
{
let vm_factory = Default::default();
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, &vm_factory, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract(None), &mut tracer, &mut vm_tracer);
ext.suicide(refund_account);
ext.suicide(&refund_account);
}
assert_eq!(setup.sub_state.suicides.len(), 1);

View File

@@ -20,12 +20,8 @@ use util::hash::*;
use util::sha3::*;
use client::BlockID;
use log_entry::LogEntry;
use ipc::binary::BinaryConvertError;
use std::mem;
use std::collections::VecDeque;
/// Blockchain Filter.
#[derive(Binary)]
pub struct Filter {
/// Blockchain will be searched from this block.
pub from_block: BlockID,
@@ -33,27 +29,22 @@ pub struct Filter {
/// Till this block.
pub to_block: BlockID,
/// Search addresses.
///
/// Search addresses.
///
/// If None, match all.
/// If specified, log must be produced by one of these addresses.
pub address: Option<Vec<Address>>,
/// Search topics.
///
///
/// If None, match all.
/// If specified, log must contain one of these topics.
pub topics: Vec<Option<Vec<H256>>>,
pub topics: [Option<Vec<H256>>; 4],
}
impl Clone for Filter {
fn clone(&self) -> Self {
let mut topics = [
None,
None,
None,
None,
];
let mut topics = [None, None, None, None];
for i in 0..4 {
topics[i] = self.topics[i].clone();
}
@@ -62,13 +53,13 @@ impl Clone for Filter {
from_block: self.from_block.clone(),
to_block: self.to_block.clone(),
address: self.address.clone(),
topics: topics[..].to_vec()
topics: topics
}
}
}
impl Filter {
/// Returns combinations of each address and topic.
/// Returns combinations of each address and topic.
pub fn bloom_possibilities(&self) -> Vec<H2048> {
let blooms = match self.address {
Some(ref addresses) if !addresses.is_empty() =>
@@ -80,7 +71,7 @@ impl Filter {
_ => vec![H2048::new()]
};
self.topics.iter().fold(blooms, |bs, topic| match *topic {
self.topics.iter().fold(blooms, | bs, topic | match *topic {
None => bs,
Some(ref topics) => bs.into_iter().flat_map(|bloom| {
topics.into_iter().map(|topic| {
@@ -120,7 +111,7 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: None,
topics: vec![None, None, None, None],
topics: [None, None, None, None]
};
let possibilities = none_filter.bloom_possibilities();
@@ -135,11 +126,9 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: vec![
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
None,
None,
None,
None, None, None
]
};
@@ -153,11 +142,10 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: vec![
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
None,
None,
None, None
]
};
@@ -174,7 +162,7 @@ mod tests {
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
]),
topics: vec![
topics: [
Some(vec![
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()
@@ -200,11 +188,10 @@ mod tests {
from_block: BlockID::Earliest,
to_block: BlockID::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: vec![
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]),
None,
None,
None, None
]
};

View File

@@ -18,9 +18,7 @@
use util::*;
use basic_types::*;
use time::get_time;
use std::cell::RefCell;
use time::now_utc;
/// Type for Block number
pub type BlockNumber = u64;
@@ -94,10 +92,10 @@ impl PartialEq for Header {
impl Default for Header {
fn default() -> Self {
Header {
parent_hash: H256::default(),
parent_hash: ZERO_H256.clone(),
timestamp: 0,
number: 0,
author: Address::default(),
author: ZERO_ADDRESS.clone(),
transactions_root: SHA3_NULL_RLP,
uncles_hash: SHA3_EMPTY_LIST_RLP,
@@ -106,10 +104,10 @@ impl Default for Header {
state_root: SHA3_NULL_RLP,
receipts_root: SHA3_NULL_RLP,
log_bloom: ZERO_LOGBLOOM.clone(),
gas_used: U256::default(),
gas_limit: U256::default(),
gas_used: ZERO_U256,
gas_limit: ZERO_U256,
difficulty: U256::default(),
difficulty: ZERO_U256,
seal: vec![],
hash: RefCell::new(None),
bare_hash: RefCell::new(None),
@@ -139,36 +137,22 @@ impl Header {
pub fn state_root(&self) -> &H256 { &self.state_root }
/// Get the receipts root field of the header.
pub fn receipts_root(&self) -> &H256 { &self.receipts_root }
/// Get the transactions root field of the header.
pub fn transactions_root(&self) -> &H256 { &self.transactions_root }
/// Get the uncles hash field of the header.
pub fn uncles_hash(&self) -> &H256 { &self.uncles_hash }
/// Get the gas limit field of the header.
pub fn gas_limit(&self) -> &U256 { &self.gas_limit }
/// Get the difficulty field of the header.
pub fn difficulty(&self) -> &U256 { &self.difficulty }
/// Get the seal field of the header.
pub fn seal(&self) -> &[Bytes] { &self.seal }
pub fn seal(&self) -> &Vec<Bytes> { &self.seal }
// TODO: seal_at, set_seal_at &c.
/// Set the number field of the header.
pub fn set_parent_hash(&mut self, a: H256) { self.parent_hash = a; self.note_dirty(); }
/// Set the uncles hash field of the header.
pub fn set_uncles_hash(&mut self, a: H256) { self.uncles_hash = a; self.note_dirty(); }
/// Set the state root field of the header.
pub fn set_state_root(&mut self, a: H256) { self.state_root = a; self.note_dirty(); }
/// Set the transactions root field of the header.
pub fn set_transactions_root(&mut self, a: H256) { self.transactions_root = a; self.note_dirty() }
/// Set the receipts root field of the header.
pub fn set_receipts_root(&mut self, a: H256) { self.receipts_root = a; self.note_dirty() }
/// Set the log bloom field of the header.
pub fn set_log_bloom(&mut self, a: LogBloom) { self.log_bloom = a; self.note_dirty() }
/// Set the timestamp field of the header.
pub fn set_timestamp(&mut self, a: u64) { self.timestamp = a; self.note_dirty(); }
/// Set the timestamp field of the header to the current time.
pub fn set_timestamp_now(&mut self, but_later_than: u64) { self.timestamp = max(get_time().sec as u64, but_later_than + 1); self.note_dirty(); }
pub fn set_timestamp_now(&mut self, but_later_than: u64) { self.timestamp = max(now_utc().to_timespec().sec as u64, but_later_than + 1); self.note_dirty(); }
/// Set the number field of the header.
pub fn set_number(&mut self, a: BlockNumber) { self.number = a; self.note_dirty(); }
/// Set the author field of the header.
@@ -236,7 +220,7 @@ impl Header {
s.append(&self.extra_data);
if let Seal::With = with_seal {
for b in &self.seal {
s.append_raw(b, 1);
s.append_raw(&b, 1);
}
}
}

View File

@@ -22,8 +22,8 @@ use tests::helpers::*;
use devtools::*;
use spec::Genesis;
use ethjson;
use ethjson::blockchain::BlockChain;
use miner::Miner;
use io::IoChannel;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log();
@@ -42,7 +42,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
flush!(" - {}...", name);
let spec = {
let spec = |blockchain: &BlockChain| {
let genesis = Genesis::from(blockchain.genesis());
let state = From::from(blockchain.pre_state.clone());
let mut spec = match era {
@@ -60,16 +60,16 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
{
let client = Client::new(
ClientConfig::default(),
&spec,
spec(&blockchain),
temp.as_path(),
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::with_spec(spec(&blockchain))),
IoChannel::disconnected()
).unwrap();
for b in &blockchain.blocks_rlp() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());
client.flush_queue();
client.import_verified_blocks();
client.import_verified_blocks(&IoChannel::disconnected());
}
}
fail_unless(client.chain_info().best_block_hash == blockchain.best_block.into());

View File

@@ -17,12 +17,11 @@
use super::test_common::*;
use state::*;
use executive::*;
use engines::Engine;
use engine::*;
use evm;
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*;
use substate::*;
use types::executed::CallType;
use tests::helpers::*;
use ethjson;
use trace::{Tracer, NoopTracer};
@@ -38,7 +37,7 @@ struct CallCreate {
impl From<ethjson::vm::Call> for CallCreate {
fn from(c: ethjson::vm::Call) -> Self {
let dst: Option<ethjson::hash::Address> = c.destination.into();
let dst: Option<_> = c.destination.into();
CallCreate {
data: c.data.into(),
destination: dst.map(Into::into),
@@ -110,15 +109,13 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
}
fn call(&mut self,
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
_code_address: &Address,
_output: &mut [u8],
_call_type: CallType
) -> MessageCallResult {
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
self.callcreates.push(CallCreate {
data: data.to_vec(),
destination: Some(receive_address.clone()),
@@ -211,7 +208,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
&mut tracer,
&mut vm_tracer,
);
let mut evm = vm_factory.create(params.gas);
let mut evm = vm_factory.create();
let res = evm.exec(params, &mut ex);
// a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone();

View File

@@ -23,4 +23,3 @@ mod state;
mod chain;
mod homestead_state;
mod homestead_chain;
mod trie;

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