merge accountdb migration

This commit is contained in:
Robert Habermeier 2016-07-09 17:33:14 +02:00
commit 36dfa4743e
234 changed files with 5015 additions and 30967 deletions

View File

@ -33,7 +33,7 @@ env:
global: global:
# GH_TOKEN # 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= - 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" - 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 -p bigint"
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}" - ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES="" - 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" - 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"

127
Cargo.lock generated
View File

@ -3,7 +3,7 @@ name = "parity"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)", "ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
@ -70,7 +70,6 @@ dependencies = [
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -129,15 +128,15 @@ dependencies = [
[[package]] [[package]]
name = "clippy" name = "clippy"
version = "0.0.77" version = "0.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"clippy_lints 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy_lints 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "clippy_lints" name = "clippy_lints"
version = "0.0.77" version = "0.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -149,15 +148,6 @@ dependencies = [
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "cookie"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.2.4" version = "0.2.4"
@ -225,15 +215,13 @@ dependencies = [
[[package]] [[package]]
name = "eth-secp256k1" name = "eth-secp256k1"
version = "0.5.4" version = "0.5.4"
source = "git+https://github.com/ethcore/rust-secp256k1#b6fdd43bbcf6d46adb72a92dd1632a0fc834cbf5" source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
dependencies = [ dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -250,13 +238,14 @@ name = "ethcore"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.3.0", "ethash 1.3.0",
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"ethcore-ipc 1.3.0", "ethcore-ipc 1.3.0",
"ethcore-ipc-codegen 1.3.0", "ethcore-ipc-codegen 1.3.0",
"ethcore-ipc-nano 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethstore 0.1.0", "ethstore 0.1.0",
@ -268,6 +257,7 @@ dependencies = [
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -276,7 +266,7 @@ dependencies = [
name = "ethcore-dapps" name = "ethcore-dapps"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.3.0", "ethcore-rpc 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
@ -293,6 +283,7 @@ dependencies = [
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -338,10 +329,11 @@ dependencies = [
name = "ethcore-rpc" name = "ethcore-rpc"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.3.0", "ethash 1.3.0",
"ethcore 1.3.0", "ethcore 1.3.0",
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"ethcore-ipc 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethsync 1.3.0", "ethsync 1.3.0",
@ -361,7 +353,7 @@ dependencies = [
name = "ethcore-signer" name = "ethcore-signer"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.3.0", "ethcore-rpc 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
@ -381,16 +373,15 @@ dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 0.1.0", "bigint 0.1.0",
"chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"igd 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "igd 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"json-tests 0.1.0",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -401,12 +392,13 @@ dependencies = [
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.1.0", "sha3 0.1.0",
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"table 0.1.0",
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"using_queue 0.1.0",
"vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -453,7 +445,7 @@ dependencies = [
name = "ethsync" name = "ethsync"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.3.0", "ethcore 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
@ -506,26 +498,6 @@ name = "httparse"
version = "1.1.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.8.1" version = "0.8.1"
@ -579,10 +551,10 @@ dependencies = [
[[package]] [[package]]
name = "igd" name = "igd"
version = "0.4.2" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"hyper 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
"xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
@ -609,14 +581,6 @@ dependencies = [
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "json-tests"
version = "0.1.0"
dependencies = [
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "jsonrpc-core" name = "jsonrpc-core"
version = "2.0.7" version = "2.0.7"
@ -647,11 +611,6 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "language-tags"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "language-tags" name = "language-tags"
version = "0.2.2" version = "0.2.2"
@ -685,15 +644,6 @@ dependencies = [
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "mime"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.2.0" version = "0.2.0"
@ -898,7 +848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "parity-dapps" name = "parity-dapps"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/ethcore/parity-dapps-rs.git#8cc812c26c903cf5764ce0f4cc3f2a7c3ddb0dc2" source = "git+https://github.com/ethcore/parity-dapps-rs.git#8ce18c014d8b69fa31fb203b68ff240091d77a23"
dependencies = [ dependencies = [
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1154,14 +1104,6 @@ dependencies = [
"nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "serde"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "0.7.9" version = "0.7.9"
@ -1253,6 +1195,10 @@ dependencies = [
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "table"
version = "0.1.0"
[[package]] [[package]]
name = "target_info" name = "target_info"
version = "0.1.0" version = "0.1.0"
@ -1359,16 +1305,6 @@ name = "unicode-xid"
version = "0.0.3" version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "0.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "url" name = "url"
version = "0.5.9" version = "0.5.9"
@ -1390,20 +1326,15 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "using_queue"
version = "0.1.0"
[[package]] [[package]]
name = "utf8-ranges" name = "utf8-ranges"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "uuid"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "0.2.1" version = "0.2.1"

View File

@ -22,7 +22,7 @@ fdlimit = { path = "util/fdlimit" }
num_cpus = "0.2" num_cpus = "0.2"
number_prefix = "0.2" number_prefix = "0.2"
rpassword = "0.2.1" rpassword = "0.2.1"
clippy = { version = "0.0.77", optional = true} clippy = { version = "0.0.78", optional = true}
ethcore = { path = "ethcore" } ethcore = { path = "ethcore" }
ethcore-util = { path = "util" } ethcore-util = { path = "util" }
ethsync = { path = "sync" } ethsync = { path = "sync" }

View File

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

View File

@ -13,6 +13,7 @@ log = "0.3"
jsonrpc-core = "2.0" jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"
url = "1.0" url = "1.0"
rustc-serialize = "0.3" rustc-serialize = "0.3"
serde = "0.7.0" serde = "0.7.0"
@ -28,7 +29,7 @@ parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.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 } 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" } mime_guess = { version = "1.6.1" }
clippy = { version = "0.0.77", optional = true} clippy = { version = "0.0.78", optional = true}
[build-dependencies] [build-dependencies]
serde_codegen = { version = "0.7.0", optional = true } serde_codegen = { version = "0.7.0", optional = true }

View File

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

View File

@ -15,42 +15,23 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath}; use hyper::{server, net, Decoder, Encoder, Next};
use api::types::{App, ApiError};
use api::response::as_json; use api::response::{as_json, as_json_error, ping_response};
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
#[derive(Clone)]
pub struct RestApi { pub struct RestApi {
local_domain: String,
endpoints: Arc<Endpoints>, 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 { impl RestApi {
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> { pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
Box::new(RestApi { Box::new(RestApi {
endpoints: endpoints local_domain: local_domain,
endpoints: endpoints,
}) })
} }
@ -63,7 +44,58 @@ impl RestApi {
impl Endpoint for RestApi { impl Endpoint for RestApi {
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> { fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
as_json(&self.list_apps()) 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(),
}),
})
} }
} }
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,13 +16,12 @@
//! REST API //! REST API
#![warn(missing_docs)]
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))] #![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))] #![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
#[cfg(feature = "serde_macros")] mod api;
include!("mod.rs.in"); mod response;
mod types;
#[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
pub use self::api::RestApi;
pub use self::types::App;

View File

@ -16,8 +16,21 @@
use serde::Serialize; use serde::Serialize;
use serde_json; use serde_json;
use endpoint::{ContentHandler, Handler}; use endpoint::Handler;
use handlers::{ContentHandler, EchoHandler};
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> { pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
Box::new(ContentHandler::new(serde_json::to_string(val).unwrap(), "application/json".to_owned())) 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")),
]))
} }

View File

@ -14,11 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rustc_serialize::hex::FromHex; #[cfg(feature = "serde_macros")]
include!("types.rs.in");
pub fn hex_or_string(s: &str) -> Vec<u8> { #[cfg(not(feature = "serde_macros"))]
match s.starts_with("0x") { include!(concat!(env!("OUT_DIR"), "/types.rs"));
true => s[2..].from_hex().unwrap(),
false => From::from(s)
}
}

50
dapps/src/api/types.rs.in Normal file
View File

@ -0,0 +1,50 @@
// 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

@ -16,11 +16,7 @@
//! URL Endpoint traits //! URL Endpoint traits
use hyper::status::StatusCode; use hyper::{server, net};
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use std::io::Write;
use std::collections::BTreeMap; use std::collections::BTreeMap;
#[derive(Debug, PartialEq, Default, Clone)] #[derive(Debug, PartialEq, Default, Clone)]
@ -42,58 +38,8 @@ pub struct EndpointInfo {
pub trait Endpoint : Send + Sync { pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<&EndpointInfo> { None } fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream> + Send>; fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<net::HttpStream> + Send>;
} }
pub type Endpoints = BTreeMap<String, Box<Endpoint>>; pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
pub type Handler = server::Handler<HttpStream> + Send; pub type Handler = server::Handler<net::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

@ -0,0 +1,44 @@
// 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

@ -0,0 +1,92 @@
// 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 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()
},
}
}
}

148
dapps/src/handlers/echo.rs Normal file
View File

@ -0,0 +1,148 @@
// 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);
}

57
dapps/src/handlers/mod.rs Normal file
View File

@ -0,0 +1,57 @@
// 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,8 +45,9 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate url; extern crate url as url_lib;
extern crate hyper; extern crate hyper;
extern crate unicase;
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate jsonrpc_core; extern crate jsonrpc_core;
@ -59,9 +60,11 @@ mod endpoint;
mod apps; mod apps;
mod page; mod page;
mod router; mod router;
mod handlers;
mod rpc; mod rpc;
mod api; mod api;
mod proxypac; mod proxypac;
mod url;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::net::SocketAddr; use std::net::SocketAddr;
@ -120,7 +123,7 @@ impl Server {
let special = Arc::new({ let special = Arc::new({
let mut special = HashMap::new(); let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone())); special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone())); special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils()); special.insert(router::SpecialEndpoint::Utils, apps::utils());
special special
}); });

View File

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

View File

@ -16,24 +16,23 @@
//! HTTP Authorization implementations //! HTTP Authorization implementations
use std::io::Write;
use std::collections::HashMap; use std::collections::HashMap;
use hyper::{header, server, Decoder, Encoder, Next}; use hyper::{server, net, header, status};
use hyper::net::HttpStream; use endpoint::Handler;
use hyper::status::StatusCode; use handlers::{AuthRequiredHandler, ContentHandler};
/// Authorization result /// Authorization result
pub enum Authorized { pub enum Authorized {
/// Authorization was successful. /// Authorization was successful.
Yes, Yes,
/// Unsuccessful authorization. Handler for further work is returned. /// Unsuccessful authorization. Handler for further work is returned.
No(Box<server::Handler<HttpStream> + Send>), No(Box<Handler>),
} }
/// Authorization interface /// Authorization interface
pub trait Authorization : Send + Sync { pub trait Authorization : Send + Sync {
/// Checks if authorization is valid. /// Checks if authorization is valid.
fn is_authorized(&self, req: &server::Request<HttpStream>)-> Authorized; fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
} }
/// HTTP Basic Authorization handler /// HTTP Basic Authorization handler
@ -45,18 +44,22 @@ pub struct HttpBasicAuth {
pub struct NoAuth; pub struct NoAuth;
impl Authorization for NoAuth { impl Authorization for NoAuth {
fn is_authorized(&self, _req: &server::Request<HttpStream>)-> Authorized { fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
Authorized::Yes Authorized::Yes
} }
} }
impl Authorization for HttpBasicAuth { impl Authorization for HttpBasicAuth {
fn is_authorized(&self, req: &server::Request<HttpStream>) -> Authorized { fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
let auth = self.check_auth(&req); let auth = self.check_auth(&req);
match auth { match auth {
Access::Denied => { Access::Denied => {
Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 })) Authorized::No(Box::new(ContentHandler::new(
status::StatusCode::Unauthorized,
"<h1>Unauthorized</h1>".into(),
"text/html".into(),
)))
}, },
Access::AuthRequired => { Access::AuthRequired => {
Authorized::No(Box::new(AuthRequiredHandler)) Authorized::No(Box::new(AuthRequiredHandler))
@ -89,7 +92,7 @@ impl HttpBasicAuth {
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password) self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
} }
fn check_auth(&self, req: &server::Request<HttpStream>) -> Access { fn check_auth(&self, req: &server::Request<net::HttpStream>) -> Access {
match req.headers().get::<header::Authorization<header::Basic>>() { match req.headers().get::<header::Authorization<header::Basic>>() {
Some(&header::Authorization( Some(&header::Authorization(
header::Basic { ref username, password: Some(ref password) } header::Basic { ref username, password: Some(ref password) }
@ -99,63 +102,3 @@ 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

@ -17,23 +17,18 @@
//! Router implementation //! Router implementation
//! Processes request handling authorization and dispatching it to proper application. //! Processes request handling authorization and dispatching it to proper application.
mod url;
mod redirect;
pub mod auth; pub mod auth;
use DAPPS_DOMAIN; use DAPPS_DOMAIN;
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use url::Host; use url::{Url, Host};
use hyper; use hyper::{self, server, Next, Encoder, Decoder};
use hyper::{server, uri, header};
use hyper::{Next, Encoder, Decoder};
use hyper::net::HttpStream; use hyper::net::HttpStream;
use apps; use apps;
use endpoint::{Endpoint, Endpoints, EndpointPath}; use endpoint::{Endpoint, Endpoints, EndpointPath};
use self::url::Url; use handlers::{Redirection, extract_url};
use self::auth::{Authorization, Authorized}; use self::auth::{Authorization, Authorized};
use self::redirect::Redirection;
/// Special endpoints are accessible on every domain (every dapp) /// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)] #[derive(Debug, PartialEq, Hash, Eq)]
@ -124,32 +119,6 @@ impl<A: Authorization> Router<A> {
} }
} }
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 extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint { fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 { if url.path.len() <= 1 {

View File

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

View File

@ -12,7 +12,7 @@ syntex = "*"
ethcore-ipc-codegen = { path = "../ipc/codegen" } ethcore-ipc-codegen = { path = "../ipc/codegen" }
[dependencies] [dependencies]
clippy = { version = "0.0.77", optional = true} clippy = { version = "0.0.78", optional = true}
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
ethcore-ipc = { path = "../ipc/rpc" } ethcore-ipc = { path = "../ipc/rpc" }
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" } rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }

View File

@ -22,7 +22,7 @@ ethcore-util = { path = "../util" }
evmjit = { path = "../evmjit", optional = true } evmjit = { path = "../evmjit", optional = true }
ethash = { path = "../ethash" } ethash = { path = "../ethash" }
num_cpus = "0.2" num_cpus = "0.2"
clippy = { version = "0.0.77", optional = true} clippy = { version = "0.0.78", optional = true}
crossbeam = "0.2.9" crossbeam = "0.2.9"
lazy_static = "0.2" lazy_static = "0.2"
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
@ -31,6 +31,8 @@ bloomchain = "0.1"
"ethcore-ipc" = { path = "../ipc/rpc" } "ethcore-ipc" = { path = "../ipc/rpc" }
rayon = "0.3.1" rayon = "0.3.1"
ethstore = { path = "../ethstore" } ethstore = { path = "../ethstore" }
semver = "0.2"
ethcore-ipc-nano = { path = "../ipc/nano" }
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/ethcore/hyper" git = "https://github.com/ethcore/hyper"
@ -43,3 +45,4 @@ json-tests = []
test-heavy = [] test-heavy = []
dev = ["clippy"] dev = ["clippy"]
default = [] default = []
benches = []

View File

@ -30,4 +30,18 @@ fn main() {
codegen::register(&mut registry); codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap(); registry.expand("", &src, &dst).unwrap();
} }
// client interface
{
let src = Path::new("src/client/client.rs");
let intermediate = Path::new(&out_dir).join("client.intermediate.rs.in");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &src, &intermediate).unwrap();
let dst = Path::new(&out_dir).join("client.ipc.rs");
let mut registry = syntex::Registry::new();
codegen::register(&mut registry);
registry.expand("", &intermediate, &dst).unwrap();
}
} }

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30", "frontierCompatibilityModeLimit": "0x118c30"
"daoRescueSoftFork": false
} }
} }
}, },

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30", "frontierCompatibilityModeLimit": "0x118c30"
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x118c30", "frontierCompatibilityModeLimit": "0x118c30"
"daoRescueSoftFork": true
} }
} }
}, },

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0xffffffffffffffff", "frontierCompatibilityModeLimit": "0xffffffffffffffff"
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": 0, "frontierCompatibilityModeLimit": 0
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": 0, "frontierCompatibilityModeLimit": 0
"daoRescueSoftFork": true
} }
} }
}, },

View File

@ -9,8 +9,7 @@
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar": "", "registrar": "",
"frontierCompatibilityModeLimit": "0x789b0", "frontierCompatibilityModeLimit": "0x789b0"
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -9,8 +9,7 @@
"durationLimit": "0x08", "durationLimit": "0x08",
"blockReward": "0x14D1120D7B160000", "blockReward": "0x14D1120D7B160000",
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050", "registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
"frontierCompatibilityModeLimit": "0xffffffffffffffff", "frontierCompatibilityModeLimit": "0xffffffffffffffff"
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -127,7 +127,7 @@ impl Account {
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \ SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
using it will not fail."); using it will not fail.");
(Filth::Clean, H256::from(db.get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)}))) (Filth::Clean, H256::from(db.get(key).map_or(U256::zero(), |v| -> U256 {decode(v)})))
}).1.clone() }).1.clone()
} }
@ -214,8 +214,8 @@ impl Account {
} }
/// Commit the `storage_overlay` to the backing DB and update `storage_root`. /// Commit the `storage_overlay` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, db: &mut AccountDBMut) { pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root) let mut t = trie_factory.from_existing(db, &mut self.storage_root)
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \ .expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \ SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
using it will not fail."); using it will not fail.");
@ -275,7 +275,7 @@ mod tests {
let rlp = { let rlp = {
let mut a = Account::new_contract(69.into(), 0.into()); let mut a = Account::new_contract(69.into(), 0.into());
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
a.commit_storage(&mut db); a.commit_storage(&Default::default(), &mut db);
a.init_code(vec![]); a.init_code(vec![]);
a.commit_code(&mut db); a.commit_code(&mut db);
a.rlp() a.rlp()
@ -313,7 +313,7 @@ mod tests {
let mut db = AccountDBMut::new(&mut db, &Address::new()); let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(0.into(), 0x1234.into()); a.set_storage(0.into(), 0x1234.into());
assert_eq!(a.storage_root(), None); assert_eq!(a.storage_root(), None);
a.commit_storage(&mut db); a.commit_storage(&Default::default(), &mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
} }
@ -323,11 +323,11 @@ mod tests {
let mut db = MemoryDB::new(); let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new()); let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(0.into(), 0x1234.into()); a.set_storage(0.into(), 0x1234.into());
a.commit_storage(&mut db); a.commit_storage(&Default::default(), &mut db);
a.set_storage(1.into(), 0x1234.into()); a.set_storage(1.into(), 0x1234.into());
a.commit_storage(&mut db); a.commit_storage(&Default::default(), &mut db);
a.set_storage(1.into(), 0.into()); a.set_storage(1.into(), 0.into());
a.commit_storage(&mut db); a.commit_storage(&Default::default(), &mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
} }

View File

@ -1,11 +1,38 @@
// 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 //! DB backend wrapper for Account trie
use util::*; use util::*;
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1]; 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] #[inline]
fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 { fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
address_hash ^ key 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? // TODO: introduce HashDBMut?

View File

@ -203,7 +203,6 @@ mod tests {
timestamp: 0, timestamp: 0,
difficulty: 0.into(), difficulty: 0.into(),
last_hashes: vec![], last_hashes: vec![],
dao_rescue_block_gas_limit: None,
gas_used: 0.into(), gas_used: 0.into(),
gas_limit: 0.into(), gas_limit: 0.into(),
}); });
@ -254,7 +253,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default(); let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine.deref(), seal).is_ok()); assert!(b.try_seal(engine.deref(), seal).is_ok());

View File

@ -183,7 +183,6 @@ pub struct OpenBlock<'x> {
engine: &'x Engine, engine: &'x Engine,
vm_factory: &'x EvmFactory, vm_factory: &'x EvmFactory,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
} }
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields, /// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
@ -195,7 +194,6 @@ pub struct ClosedBlock {
block: ExecutedBlock, block: ExecutedBlock,
uncle_bytes: Bytes, uncle_bytes: Bytes,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
unclosed_state: State, unclosed_state: State,
} }
@ -222,22 +220,21 @@ impl<'x> OpenBlock<'x> {
pub fn new( pub fn new(
engine: &'x Engine, engine: &'x Engine,
vm_factory: &'x EvmFactory, vm_factory: &'x EvmFactory,
trie_factory: TrieFactory,
tracing: bool, tracing: bool,
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
author: Address, author: Address,
gas_range_target: (U256, U256), gas_range_target: (U256, U256),
extra_data: Bytes, extra_data: Bytes,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())); let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory));
let mut r = OpenBlock { let mut r = OpenBlock {
block: ExecutedBlock::new(state, tracing), block: ExecutedBlock::new(state, tracing),
engine: engine, engine: engine,
vm_factory: vm_factory, vm_factory: vm_factory,
last_hashes: last_hashes, last_hashes: last_hashes,
dao_rescue_block_gas_limit: dao_rescue_block_gas_limit,
}; };
r.block.base.header.parent_hash = parent.hash(); r.block.base.header.parent_hash = parent.hash();
@ -294,7 +291,6 @@ impl<'x> OpenBlock<'x> {
/// Get the environment info concerning this block. /// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo { pub fn env_info(&self) -> EnvInfo {
// TODO: memoise. // TODO: memoise.
const SOFT_FORK_BLOCK: u64 = 1_800_000;
EnvInfo { EnvInfo {
number: self.block.base.header.number, number: self.block.base.header.number,
author: self.block.base.header.author.clone(), author: self.block.base.header.author.clone(),
@ -303,7 +299,6 @@ impl<'x> OpenBlock<'x> {
last_hashes: self.last_hashes.clone(), // TODO: should be a reference. 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_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(), gas_limit: self.block.base.header.gas_limit.clone(),
dao_rescue_block_gas_limit: if self.block.base.header.number == SOFT_FORK_BLOCK { Some(self.block.base.header.gas_limit) } else { self.dao_rescue_block_gas_limit },
} }
} }
@ -350,7 +345,6 @@ impl<'x> OpenBlock<'x> {
block: s.block, block: s.block,
uncle_bytes: uncle_bytes, uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes, last_hashes: s.last_hashes,
dao_rescue_block_gas_limit: s.dao_rescue_block_gas_limit,
unclosed_state: unclosed_state, unclosed_state: unclosed_state,
} }
} }
@ -410,7 +404,6 @@ impl ClosedBlock {
engine: engine, engine: engine,
vm_factory: vm_factory, vm_factory: vm_factory,
last_hashes: self.last_hashes, last_hashes: self.last_hashes,
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit,
} }
} }
} }
@ -480,17 +473,17 @@ pub fn enact(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>, vm_factory: &EvmFactory,
vm_factory: &EvmFactory trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
{ {
if ::log::max_log_level() >= ::log::LogLevel::Trace { if ::log::max_log_level() >= ::log::LogLevel::Trace {
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce())); let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone()));
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author())); trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
} }
} }
let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone())); let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone()));
b.set_difficulty(*header.difficulty()); b.set_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit()); b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp()); b.set_timestamp(header.timestamp());
@ -508,12 +501,12 @@ pub fn enact_bytes(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>, vm_factory: &EvmFactory,
vm_factory: &EvmFactory trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes); let block = BlockView::new(block_bytes);
let header = block.header(); let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory) enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
@ -525,11 +518,11 @@ pub fn enact_verified(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>, vm_factory: &EvmFactory,
vm_factory: &EvmFactory trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let view = BlockView::new(&block.bytes); let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory) enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
@ -541,11 +534,11 @@ pub fn enact_and_seal(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>, vm_factory: &EvmFactory,
vm_factory: &EvmFactory trie_factory: TrieFactory,
) -> Result<SealedBlock, Error> { ) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view(); let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)).seal(engine, header.seal()))) Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal())))
} }
#[cfg(test)] #[cfg(test)]
@ -565,7 +558,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default(); let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
let _ = b.seal(engine.deref(), vec![]); let _ = b.seal(engine.deref(), vec![]);
} }
@ -581,7 +574,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default(); let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
.close_and_lock().seal(engine.deref(), vec![]).unwrap(); .close_and_lock().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes(); let orig_bytes = b.rlp_bytes();
let orig_db = b.drain(); let orig_db = b.drain();
@ -589,7 +582,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); 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()], None, &Default::default()).unwrap(); let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default(), Default::default()).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes); assert_eq!(e.rlp_bytes(), orig_bytes);
@ -609,7 +602,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default(); let vm_factory = Default::default();
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle1_header = Header::new(); let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec(); uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new(); let mut uncle2_header = Header::new();
@ -624,7 +617,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); 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()], None, &Default::default()).unwrap(); let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default(), Default::default()).unwrap();
let bytes = e.rlp_bytes(); let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes); assert_eq!(bytes, orig_bytes);

View File

@ -28,6 +28,8 @@ use service::*;
use client::BlockStatus; use client::BlockStatus;
use util::panics::*; use util::panics::*;
pub use types::block_queue_info::BlockQueueInfo;
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock); known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
const MIN_MEM_LIMIT: usize = 16384; const MIN_MEM_LIMIT: usize = 16384;
@ -53,22 +55,6 @@ 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 { impl BlockQueueInfo {
/// The total size of the queues. /// The total size of the queues.

View File

@ -17,8 +17,6 @@
use util::numbers::{U256,H256}; use util::numbers::{U256,H256};
use header::BlockNumber; use header::BlockNumber;
use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap};
/// Brief info about inserted block. /// Brief info about inserted block.
#[derive(Clone)] #[derive(Clone)]
pub struct BlockInfo { pub struct BlockInfo {
@ -54,43 +52,3 @@ pub struct BranchBecomingCanonChainData {
/// Hashes of the blocks which were invalidated. /// Hashes of the blocks which were invalidated.
pub retracted: Vec<H256>, 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
}
}
}
}

View File

@ -30,7 +30,7 @@ use blockchain::best_block::BestBlock;
use types::tree_route::TreeRoute; use types::tree_route::TreeRoute;
use blockchain::update::ExtrasUpdate; use blockchain::update::ExtrasUpdate;
use blockchain::{CacheSize, ImportRoute, Config}; use blockchain::{CacheSize, ImportRoute, Config};
use db::{Writable, Readable, CacheUpdatePolicy}; use db::{Writable, Readable, CacheUpdatePolicy, Key};
const LOG_BLOOMS_LEVELS: usize = 3; const LOG_BLOOMS_LEVELS: usize = 3;
const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16; const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16;
@ -295,7 +295,22 @@ impl BlockChain {
// load best block // load best block
let best_block_hash = match bc.extras_db.get(b"best").unwrap() { let best_block_hash = match bc.extras_db.get(b"best").unwrap() {
Some(best) => H256::from_slice(&best), Some(best) => {
let best = H256::from_slice(&best);
let mut b = best.clone();
while !bc.blocks_db.get(&b).unwrap().is_some() {
// track back to the best block we have in the blocks database
let extras: BlockDetails = bc.extras_db.read(&b).unwrap();
type DetailsKey = Key<BlockDetails, Target=H264>;
bc.extras_db.delete(&(DetailsKey::key(&b))).unwrap();
b = extras.parent;
}
if b != best {
info!("Restored mismatched best block. Was: {}, new: {}", best.hex(), b.hex());
bc.extras_db.put(b"best", &b).unwrap();
}
b
}
None => { None => {
// best block does not exist // best block does not exist
// we need to insert genesis into the cache // we need to insert genesis into the cache

View File

@ -14,16 +14,34 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain database client. use std::collections::{HashSet, HashMap};
use std::ops::Deref;
use std::mem;
use std::collections::VecDeque;
use std::sync::*;
use std::path::{Path, PathBuf};
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::time::Instant;
use std::fs::{create_dir, File}; // util
use std::path::PathBuf; use util::numbers::*;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use util::*;
use util::panics::*; use util::panics::*;
use util::network::*;
use util::io::*;
use util::rlp;
use util::sha3::*;
use util::Bytes;
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
use util::journaldb;
use util::journaldb::JournalDB;
use util::kvdb::*;
use util::{Applyable, Stream, View, PerfTimer, Itertools, Colour};
// other
use views::BlockView; use views::BlockView;
use error::{Error, ImportError, ExecutionError, BlockError, ImportResult}; use error::{ImportError, ExecutionError, BlockError, ImportResult};
use header::{BlockNumber, Header}; use header::BlockNumber;
use state::State; use state::State;
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
@ -35,24 +53,32 @@ use verification::{PreverifiedBlock, Verifier};
use block::*; use block::*;
use transaction::{LocalizedTransaction, SignedTransaction, Action}; use transaction::{LocalizedTransaction, SignedTransaction, Action};
use blockchain::extras::TransactionAddress; use blockchain::extras::TransactionAddress;
use filter::Filter; use types::filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics}; use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig,
DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient,
TraceFilter, CallAnalytics, BlockImportError, Mode};
use client::Error as ClientError; use client::Error as ClientError;
use env_info::EnvInfo; use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address}; use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace; use trace;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use util::TrieFactory;
use ipc::IpcConfig;
use ipc::binary::{BinaryConvertError};
// re-export
pub use types::blockchain_info::BlockChainInfo; pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus; pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory; pub use blockchain::CacheSize as BlockChainCacheSize;
use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
const MAX_TX_QUEUE_SIZE: usize = 4096; const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
impl fmt::Display for BlockChainInfo { impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -82,9 +108,24 @@ 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. /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
pub struct Client { pub struct Client {
mode: Mode,
chain: Arc<BlockChain>, chain: Arc<BlockChain>,
tracedb: Arc<TraceDB<BlockChain>>, tracedb: Arc<TraceDB<BlockChain>>,
engine: Arc<Box<Engine>>, engine: Arc<Box<Engine>>,
@ -95,9 +136,13 @@ pub struct Client {
panic_handler: Arc<PanicHandler>, panic_handler: Arc<PanicHandler>,
verifier: Box<Verifier>, verifier: Box<Verifier>,
vm_factory: Arc<EvmFactory>, vm_factory: Arc<EvmFactory>,
trie_factory: TrieFactory,
miner: Arc<Miner>, miner: Arc<Miner>,
sleep_state: Mutex<SleepState>,
liveness: AtomicBool,
io_channel: IoChannel<NetSyncMessage>, io_channel: IoChannel<NetSyncMessage>,
queue_transactions: AtomicUsize, queue_transactions: AtomicUsize,
previous_enode: Mutex<Option<String>>,
} }
const HISTORY: u64 = 1200; const HISTORY: u64 = 1200;
@ -132,9 +177,8 @@ impl Client {
spec: Spec, spec: Spec,
path: &Path, path: &Path,
miner: Arc<Miner>, miner: Arc<Miner>,
message_channel: IoChannel<NetSyncMessage>) message_channel: IoChannel<NetSyncMessage>
-> Result<Arc<Client>, ClientError> ) -> Result<Arc<Client>, ClientError> {
{
let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block(); let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
@ -165,7 +209,11 @@ impl Client {
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue); panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
let client = Client { let client = Client {
sleep_state: Mutex::new(SleepState::new(awake)),
liveness: AtomicBool::new(awake),
mode: config.mode,
chain: chain, chain: chain,
tracedb: tracedb, tracedb: tracedb,
engine: engine, engine: engine,
@ -176,11 +224,12 @@ impl Client {
panic_handler: panic_handler, panic_handler: panic_handler,
verifier: verification::new(config.verifier_type), verifier: verification::new(config.verifier_type),
vm_factory: Arc::new(EvmFactory::new(config.vm_type)), vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
trie_factory: TrieFactory::new(config.trie_spec),
miner: miner, miner: miner,
io_channel: message_channel, io_channel: message_channel,
queue_transactions: AtomicUsize::new(0), queue_transactions: AtomicUsize::new(0),
previous_enode: Mutex::new(None),
}; };
Ok(Arc::new(client)) Ok(Arc::new(client))
} }
@ -234,7 +283,7 @@ impl Client {
let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().unwrap().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.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory); let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone());
if let Err(e) = enact_result { if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
@ -349,7 +398,7 @@ impl Client {
imported imported
} }
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain { fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
let number = block.header().number(); let number = block.header().number();
// Are we committing an era? // Are we committing an era?
let ancient = if number >= HISTORY { let ancient = if number >= HISTORY {
@ -385,12 +434,8 @@ impl Client {
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize { pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
let _timer = PerfTimer::new("import_queued_transactions"); let _timer = PerfTimer::new("import_queued_transactions");
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst); self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
let fetch_account = |a: &Address| AccountDetails { let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
nonce: self.latest_nonce(a), let results = self.miner.import_external_transactions(self, txs);
balance: self.latest_balance(a),
};
let tx = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
let results = self.miner.import_transactions(self, tx, fetch_account);
results.len() results.len()
} }
@ -419,13 +464,17 @@ impl Client {
let root = HeaderView::new(&header).state_root(); let root = HeaderView::new(&header).state_root();
State::from_existing(db, root, self.engine.account_start_nonce()).ok() State::from_existing(db, root, self.engine.account_start_nonce(), self.trie_factory.clone()).ok()
}) })
} }
/// Get a copy of the best block's state. /// Get a copy of the best block's state.
pub fn state(&self) -> State { pub fn state(&self) -> State {
State::from_existing(self.state_db.lock().unwrap().boxed_clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce()) State::from_existing(
self.state_db.lock().unwrap().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.") .expect("State root of best block header always valid.")
} }
@ -442,9 +491,41 @@ impl Client {
} }
/// Tick the client. /// Tick the client.
// TODO: manage by real events.
pub fn tick(&self) { pub fn tick(&self) {
self.chain.collect_garbage(); self.chain.collect_garbage();
self.block_queue.collect_garbage(); self.block_queue.collect_garbage();
match self.mode {
Mode::Dark(timeout) => {
let mut ss = self.sleep_state.lock().unwrap();
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().unwrap();
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. /// Set up the cache behaviour.
@ -480,8 +561,45 @@ impl Client {
}) })
} }
} }
fn wake_up(&self) {
if !self.liveness.load(AtomicOrdering::Relaxed) {
self.liveness.store(true, AtomicOrdering::Relaxed);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::StartNetwork)).unwrap();
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.io_channel.send(NetworkIoMessage::User(SyncMessage::StopNetwork)).unwrap();
trace!(target: "mode", "sleep: Sleeping.");
} else {
trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
// TODO: Consider uncommenting.
//*self.last_activity.lock().unwrap() = Some(Instant::now());
}
}
}
/// Notify us that the network has been started.
pub fn network_started(&self, url: &String) {
let mut previous_enode = self.previous_enode.lock().unwrap();
if let Some(ref u) = *previous_enode {
if u == url {
return;
}
}
*previous_enode = Some(url.clone());
info!(target: "mode", "Public node URL: {}", url.apply(Colour::White.bold()));
}
} }
#[derive(Ipc)]
#[ipc(client_ident="RemoteClient")]
impl BlockChainClient for Client { impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap(); let header = self.block_header(BlockID::Latest).unwrap();
@ -495,7 +613,6 @@ impl BlockChainClient for Client {
last_hashes: last_hashes, last_hashes: last_hashes,
gas_used: U256::zero(), gas_used: U256::zero(),
gas_limit: U256::max_value(), gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit(view.parent_hash()),
}; };
// that's just a copy of the state. // that's just a copy of the state.
let mut state = self.state(); let mut state = self.state();
@ -521,6 +638,12 @@ impl BlockChainClient for Client {
ret ret
} }
fn keep_alive(&self) {
if self.mode != Mode::Active {
self.wake_up();
(*self.sleep_state.lock().unwrap()).last_activity = Some(Instant::now());
}
}
fn block_header(&self, id: BlockID) -> Option<Bytes> { fn block_header(&self, id: BlockID) -> Option<Bytes> {
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())) 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()))
@ -580,9 +703,9 @@ impl BlockChainClient for Client {
self.transaction_address(id).and_then(|address| self.chain.transaction(&address)) self.transaction_address(id).and_then(|address| self.chain.transaction(&address))
} }
fn uncle(&self, id: UncleID) -> Option<Header> { fn uncle(&self, id: UncleID) -> Option<Bytes> {
let index = id.1; let index = id.position;
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index)) self.block(id.block).and_then(|block| BlockView::new(&block).uncle_rlp_at(index))
} }
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> { fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
@ -649,17 +772,17 @@ impl BlockChainClient for Client {
self.chain.block_receipts(hash).map(|receipts| rlp::encode(&receipts).to_vec()) self.chain.block_receipts(hash).map(|receipts| rlp::encode(&receipts).to_vec())
} }
fn import_block(&self, bytes: Bytes) -> ImportResult { fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
{ {
let header = BlockView::new(&bytes).header_view(); let header = BlockView::new(&bytes).header_view();
if self.chain.is_known(&header.sha3()) { if self.chain.is_known(&header.sha3()) {
return Err(ImportError::AlreadyInChain.into()); return Err(BlockImportError::Import(ImportError::AlreadyInChain));
} }
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown { if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
return Err(BlockError::UnknownParent(header.parent_hash()).into()); return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
} }
} }
self.block_queue.import_block(bytes) Ok(try!(self.block_queue.import_block(bytes)))
} }
fn queue_info(&self) -> BlockQueueInfo { fn queue_info(&self) -> BlockQueueInfo {
@ -724,7 +847,6 @@ impl BlockChainClient for Client {
.collect::<Vec<LocalizedLogEntry>>() .collect::<Vec<LocalizedLogEntry>>()
}) })
.collect::<Vec<LocalizedLogEntry>>() .collect::<Vec<LocalizedLogEntry>>()
}) })
.collect() .collect()
} }
@ -773,14 +895,6 @@ impl BlockChainClient for Client {
self.build_last_hashes(self.chain.best_block_hash()) self.build_last_hashes(self.chain.best_block_hash())
} }
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, Error>> {
let fetch_account = |a: &Address| AccountDetails {
nonce: self.latest_nonce(a),
balance: self.latest_balance(a),
};
self.miner.import_transactions(self, transactions, fetch_account)
}
fn queue_transactions(&self, transactions: Vec<Bytes>) { fn queue_transactions(&self, transactions: Vec<Bytes>) {
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE { if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
debug!("Ignoring {} transactions: queue is full", transactions.len()); debug!("Ignoring {} transactions: queue is full", transactions.len());
@ -800,44 +914,6 @@ impl BlockChainClient for Client {
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.pending_transactions() self.miner.pending_transactions()
} }
fn take_snapshot(&self, root_dir: &Path) {
use snapshot::{ManifestData, chunk_blocks, chunk_state};
let best_header_bytes = self.best_block_header();
let best_header = HeaderView::new(&best_header_bytes);
let state_root = best_header.state_root();
trace!(target: "snapshot", "Taking snapshot starting at block {}", best_header.number());
let mut path = root_dir.to_owned();
path.push("snapshot/");
let _ = create_dir(&path);
// lock the state db while we create the state chunks.
let state_hashes = {
let state_db = self.state_db.lock().unwrap().boxed_clone();
chunk_state(state_db.as_hashdb(), &state_root, &path).unwrap()
};
let best_hash = best_header.hash();
let genesis_hash = self.chain.genesis_hash();
let block_hashes = chunk_blocks(self, best_hash, genesis_hash, &path).unwrap();
trace!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
let manifest_data = ManifestData {
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: state_root,
};
path.push("MANIFEST");
let mut manifest_file = File::create(&path).unwrap();
manifest_file.write_all(&manifest_data.to_rlp()).unwrap();
}
} }
impl MiningBlockChainClient for Client { impl MiningBlockChainClient for Client {
@ -848,11 +924,11 @@ impl MiningBlockChainClient for Client {
let mut open_block = OpenBlock::new( let mut open_block = OpenBlock::new(
engine, engine,
&self.vm_factory, &self.vm_factory,
self.trie_factory.clone(),
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().unwrap().boxed_clone(), 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.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
self.build_last_hashes(h.clone()), self.build_last_hashes(h.clone()),
self.dao_rescue_block_gas_limit(h.clone()),
author, author,
gas_range_target, gas_range_target,
extra_data, extra_data,
@ -914,3 +990,5 @@ impl MayPanic for Client {
self.panic_handler.on_panic(closure); self.panic_handler.on_panic(closure);
} }
} }
impl IpcConfig for Client { }

View File

@ -14,12 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub use std::time::Duration;
pub use block_queue::BlockQueueConfig; pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig; pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch}; pub use trace::{Config as TraceConfig, Switch};
pub use evm::VMType; pub use evm::VMType;
pub use verification::VerifierType; pub use verification::VerifierType;
use util::journaldb; use util::journaldb;
use util::trie::TrieSpec;
/// Client state db compaction profile /// Client state db compaction profile
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -34,6 +36,23 @@ impl Default for DatabaseCompactionProfile {
fn default() -> Self { DatabaseCompactionProfile::Default } fn default() -> Self { DatabaseCompactionProfile::Default }
} }
/// 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 }
}
/// Client configuration. Includes configs for all sub-systems. /// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ClientConfig { pub struct ClientConfig {
@ -45,6 +64,8 @@ pub struct ClientConfig {
pub tracing: TraceConfig, pub tracing: TraceConfig,
/// VM type. /// VM type.
pub vm_type: VMType, pub vm_type: VMType,
/// Trie type.
pub trie_spec: TrieSpec,
/// The JournalDB ("pruning") algorithm to use. /// The JournalDB ("pruning") algorithm to use.
pub pruning: journaldb::Algorithm, pub pruning: journaldb::Algorithm,
/// The name of the client instance. /// The name of the client instance.
@ -53,6 +74,8 @@ pub struct ClientConfig {
pub db_cache_size: Option<usize>, pub db_cache_size: Option<usize>,
/// State db compaction profile /// State db compaction profile
pub db_compaction: DatabaseCompactionProfile, pub db_compaction: DatabaseCompactionProfile,
/// Operating mode
pub mode: Mode,
/// Type of block verifier used by client. /// Type of block verifier used by client.
pub verifier_type: VerifierType, pub verifier_type: VerifierType,
} }

View File

@ -16,22 +16,20 @@
//! Blockchain database client. //! Blockchain database client.
mod client;
mod config; mod config;
mod error; mod error;
mod test_client; mod test_client;
mod trace; mod trace;
pub use self::client::*; pub use self::client::*;
pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType}; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::error::Error; pub use self::error::Error;
pub use types::ids::*; pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::trace::Filter as TraceFilter; pub use types::trace_filter::Filter as TraceFilter;
pub use executive::{Executed, Executive, TransactOptions}; pub use executive::{Executed, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo}; pub use env_info::{LastHashes, EnvInfo};
use std::path::Path;
use util::bytes::Bytes; use util::bytes::Bytes;
use util::hash::{Address, H256, H2048}; use util::hash::{Address, H256, H2048};
use util::numbers::U256; use util::numbers::U256;
@ -39,31 +37,34 @@ use util::Itertools;
use blockchain::TreeRoute; use blockchain::TreeRoute;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock}; use block::{OpenBlock, SealedBlock};
use header::{BlockNumber, Header}; use header::{BlockNumber};
use transaction::{LocalizedTransaction, SignedTransaction}; use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use filter::Filter; use filter::Filter;
use views::{HeaderView, BlockView}; use views::{BlockView};
use error::{ImportResult, ExecutionError}; use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
use miner::{TransactionImportResult}; pub use types::call_analytics::CallAnalytics;
use error::Error as EthError; pub use block_import_error::BlockImportError;
pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError;
/// Options concerning what analytics we run on the call. pub mod client {
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] //! Blockchain database client.
pub struct CallAnalytics {
/// Make a transaction trace. #![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
pub transaction_tracing: bool, include!(concat!(env!("OUT_DIR"), "/client.ipc.rs"));
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
} }
/// Blockchain database client. Owns and manages a blockchain and a block queue. /// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send { pub trait BlockChainClient : Sync + Send {
/// 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. /// Get raw block header data by block id.
fn block_header(&self, id: BlockID) -> Option<Bytes>; fn block_header(&self, id: BlockID) -> Option<Bytes>;
@ -127,7 +128,7 @@ pub trait BlockChainClient : Sync + Send {
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>; fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
/// Get uncle with given id. /// Get uncle with given id.
fn uncle(&self, id: UncleID) -> Option<Header>; fn uncle(&self, id: UncleID) -> Option<Bytes>;
/// Get transaction receipt with given hash. /// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>; fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
@ -146,7 +147,7 @@ pub trait BlockChainClient : Sync + Send {
fn block_receipts(&self, hash: &H256) -> Option<Bytes>; fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Import a block into the blockchain. /// Import a block into the blockchain.
fn import_block(&self, bytes: Bytes) -> ImportResult; fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
/// Get block queue information. /// Get block queue information.
fn queue_info(&self) -> BlockQueueInfo; fn queue_info(&self) -> BlockQueueInfo;
@ -188,16 +189,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get last hashes starting from best block. /// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes; fn last_hashes(&self) -> LastHashes;
/// import transactions from network/other 3rd party
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>>;
/// Queue transactions for importing. /// Queue transactions for importing.
fn queue_transactions(&self, transactions: Vec<Bytes>); fn queue_transactions(&self, transactions: Vec<Bytes>);
/// Generate a PV64 snapshot for the current best block, saving it within the
/// root directory, whose path is given.
fn take_snapshot(&self, root_dir: &Path);
/// list all transactions /// list all transactions
fn pending_transactions(&self) -> Vec<SignedTransaction>; fn pending_transactions(&self) -> Vec<SignedTransaction>;
@ -226,28 +220,6 @@ pub trait BlockChainClient : Sync + Send {
Err(()) Err(())
} }
} }
/// Get `Some` gas limit of SOFT_FORK_BLOCK, or `None` if chain is not yet that long.
fn dao_rescue_block_gas_limit(&self, chain_hash: H256) -> Option<U256> {
const SOFT_FORK_BLOCK: u64 = 1800000;
// shortcut if the canon chain is already known.
if self.chain_info().best_block_number > SOFT_FORK_BLOCK + 1000 {
return self.block_header(BlockID::Number(SOFT_FORK_BLOCK)).map(|header| HeaderView::new(&header).gas_limit());
}
// otherwise check according to `chain_hash`.
if let Some(mut header) = self.block_header(BlockID::Hash(chain_hash)) {
if HeaderView::new(&header).number() < SOFT_FORK_BLOCK {
None
} else {
while HeaderView::new(&header).number() != SOFT_FORK_BLOCK {
header = self.block_header(BlockID::Hash(HeaderView::new(&header).parent_hash())).expect("chain is complete; parent of chain entry must be in chain; qed");
}
Some(HeaderView::new(&header).gas_limit())
}
} else {
None
}
}
} }
/// Extended client interface used for mining /// Extended client interface used for mining

View File

@ -20,7 +20,9 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
use util::*; use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute; use blockchain::TreeRoute;
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics}; use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics,
BlockImportError};
use header::{Header as BlockHeader, BlockNumber}; use header::{Header as BlockHeader, BlockNumber};
use filter::Filter; use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -34,12 +36,9 @@ use spec::Spec;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock}; use block::{OpenBlock, SealedBlock};
use executive::Executed; use executive::Executed;
use error::{ExecutionError}; use error::ExecutionError;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use miner::{TransactionImportResult, AccountDetails};
use error::Error as EthError;
/// Test client. /// Test client.
pub struct TestBlockChainClient { pub struct TestBlockChainClient {
/// Blocks. /// Blocks.
@ -274,6 +273,10 @@ impl BlockChainClient for TestBlockChainClient {
} }
} }
fn latest_nonce(&self, address: &Address) -> U256 {
self.nonce(address, BlockID::Latest).unwrap()
}
fn code(&self, address: &Address) -> Option<Bytes> { fn code(&self, address: &Address) -> Option<Bytes> {
self.code.read().unwrap().get(address).cloned() self.code.read().unwrap().get(address).cloned()
} }
@ -286,6 +289,10 @@ impl BlockChainClient for TestBlockChainClient {
} }
} }
fn latest_balance(&self, address: &Address) -> U256 {
self.balance(address, BlockID::Latest).unwrap()
}
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> { fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> {
if let BlockID::Latest = id { if let BlockID::Latest = id {
Some(self.storage.read().unwrap().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))
@ -298,7 +305,7 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn uncle(&self, _id: UncleID) -> Option<BlockHeader> { fn uncle(&self, _id: UncleID) -> Option<Bytes> {
unimplemented!(); unimplemented!();
} }
@ -402,7 +409,7 @@ impl BlockChainClient for TestBlockChainClient {
None None
} }
fn import_block(&self, b: Bytes) -> ImportResult { fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
let header = Rlp::new(&b).val_at::<BlockHeader>(0); let header = Rlp::new(&b).val_at::<BlockHeader>(0);
let h = header.hash(); let h = header.hash();
let number: usize = header.number as usize; let number: usize = header.number as usize;
@ -487,25 +494,10 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>> {
let nonces = self.nonces.read().unwrap();
let balances = self.balances.read().unwrap();
let fetch_account = |a: &Address| AccountDetails {
nonce: nonces[a],
balance: balances[a],
};
self.miner.import_transactions(self, transactions, &fetch_account)
}
fn queue_transactions(&self, transactions: Vec<Bytes>) { fn queue_transactions(&self, transactions: Vec<Bytes>) {
// import right here // import right here
let tx = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect(); let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
self.import_transactions(tx); self.miner.import_external_transactions(self, txs);
}
fn take_snapshot(&self, _root_dir: &Path) {
unimplemented!()
} }
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {

View File

@ -1,13 +1,12 @@
//! Bridge between Tracedb and Blockchain. //! Bridge between Tracedb and Blockchain.
use std::ops::Range; use util::{H256};
use util::{Address, H256};
use header::BlockNumber; use header::BlockNumber;
use trace::DatabaseExtras as TraceDatabaseExtras; use trace::DatabaseExtras as TraceDatabaseExtras;
use blockchain::{BlockChain, BlockProvider}; use blockchain::{BlockChain, BlockProvider};
use blockchain::extras::TransactionAddress; use blockchain::extras::TransactionAddress;
use super::BlockID; pub use types::trace_filter::Filter;
impl TraceDatabaseExtras for BlockChain { impl TraceDatabaseExtras for BlockChain {
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> { fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
@ -26,13 +25,3 @@ impl TraceDatabaseExtras for BlockChain {
.map(|tx| tx.hash()) .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

@ -39,9 +39,6 @@ pub struct EnvInfo {
pub last_hashes: LastHashes, pub last_hashes: LastHashes,
/// The gas used. /// The gas used.
pub gas_used: U256, pub gas_used: U256,
/// Block gas limit at DAO rescue block SOFT_FORK_BLOCK or None if not yet there.
pub dao_rescue_block_gas_limit: Option<U256>,
} }
impl Default for EnvInfo { impl Default for EnvInfo {
@ -54,7 +51,6 @@ impl Default for EnvInfo {
gas_limit: 0.into(), gas_limit: 0.into(),
last_hashes: vec![], last_hashes: vec![],
gas_used: 0.into(), gas_used: 0.into(),
dao_rescue_block_gas_limit: None,
} }
} }
} }
@ -70,7 +66,6 @@ impl From<ethjson::vm::Env> for EnvInfo {
timestamp: e.timestamp.into(), timestamp: e.timestamp.into(),
last_hashes: (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(), gas_used: U256::zero(),
dao_rescue_block_gas_limit: None,
} }
} }
} }

View File

@ -20,10 +20,11 @@ use util::*;
use header::BlockNumber; use header::BlockNumber;
use basic_types::LogBloom; use basic_types::LogBloom;
use client::Error as ClientError; use client::Error as ClientError;
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use types::block_import_error::BlockImportError;
pub use types::executed::ExecutionError; pub use types::executed::ExecutionError;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
/// Errors concerning transaction processing. /// Errors concerning transaction processing.
pub enum TransactionError { pub enum TransactionError {
/// Transaction is already imported to the queue /// Transaction is already imported to the queue
@ -330,6 +331,20 @@ impl From<::util::snappy::InvalidInput> for Error {
} }
} }
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)),
}
}
}
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. // TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)] /*#![feature(concat_idents)]
macro_rules! assimilate { macro_rules! assimilate {

View File

@ -39,8 +39,6 @@ pub struct EthashParams {
pub registrar: Address, pub registrar: Address,
/// Homestead transition block number. /// Homestead transition block number.
pub frontier_compatibility_mode_limit: u64, pub frontier_compatibility_mode_limit: u64,
/// Enable the soft-fork logic.
pub dao_rescue_soft_fork: bool,
} }
impl From<ethjson::spec::EthashParams> for EthashParams { impl From<ethjson::spec::EthashParams> for EthashParams {
@ -53,7 +51,6 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
block_reward: p.block_reward.into(), block_reward: p.block_reward.into(),
registrar: p.registrar.into(), registrar: p.registrar.into(),
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(), frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
dao_rescue_soft_fork: p.dao_rescue_soft_fork.into(),
} }
} }
} }
@ -102,11 +99,7 @@ impl Engine for Ethash {
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit { if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
Schedule::new_frontier() Schedule::new_frontier()
} else { } else {
let mut s = Schedule::new_homestead(); Schedule::new_homestead()
if self.ethash_params.dao_rescue_soft_fork {
s.reject_dao_transactions = env_info.dao_rescue_block_gas_limit.map_or(false, |x| x <= 4_000_000.into());
}
s
} }
} }
@ -325,7 +318,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default(); let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close(); let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
} }
@ -340,7 +333,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default(); let vm_factory = Default::default();
let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle = Header::new(); let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106"); let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone(); uncle.author = uncle_author.clone();
@ -369,7 +362,6 @@ mod tests {
last_hashes: vec![], last_hashes: vec![],
gas_used: 0.into(), gas_used: 0.into(),
gas_limit: 0.into(), gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
}); });
assert!(schedule.stack_limit > 0); assert!(schedule.stack_limit > 0);
@ -382,7 +374,6 @@ mod tests {
last_hashes: vec![], last_hashes: vec![],
gas_used: 0.into(), gas_used: 0.into(),
gas_limit: 0.into(), gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
}); });
assert!(!schedule.have_delegate_call); assert!(!schedule.have_delegate_call);

View File

@ -33,11 +33,8 @@ use super::spec::*;
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) } pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec. /// Create a new Frontier mainnet chain spec.
pub fn new_frontier(dao_rescue: bool) -> Spec { pub fn new_frontier() -> Spec {
Spec::load(match dao_rescue { Spec::load(include_bytes!("../../res/ethereum/frontier.json"))
true => include_bytes!("../../res/ethereum/frontier_dao_rescue.json"),
false => include_bytes!("../../res/ethereum/frontier.json"),
})
} }
/// Create a new Frontier chain spec as though it never changes to Homestead. /// Create a new Frontier chain spec as though it never changes to Homestead.
@ -67,7 +64,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce()).unwrap(); let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64)); assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64)); assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64)); assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64));
@ -89,7 +86,7 @@ mod tests {
#[test] #[test]
fn frontier() { fn frontier() {
let frontier = new_frontier(true); let frontier = new_frontier();
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()); assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
let genesis = frontier.genesis_block(); let genesis = frontier.genesis_block();

View File

@ -0,0 +1,126 @@
// 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,6 +95,61 @@ impl<'a> Finalize for Result<GasLeft<'a>> {
} }
} }
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 {
fn as_u256(&self) -> U256;
fn from_u256(val: U256) -> Result<Self>;
fn as_usize(&self) -> usize;
fn overflow_add(self, other: Self) -> (Self, bool);
fn overflow_mul(self, other: Self) -> (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)
}
}
impl CostType for usize {
fn as_u256(&self) -> U256 {
U256::from(*self)
}
fn from_u256(val: U256) -> Result<Self> {
if U256::from(val.low_u64()) != val {
return Err(Error::OutOfGas);
}
Ok(val.low_u64() as usize)
}
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)
}
}
/// Evm interface /// Evm interface
pub trait Evm { pub trait Evm {
/// This function should be used to execute transaction. /// This function should be used to execute transaction.

View File

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

View File

@ -0,0 +1,261 @@
// 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,
}
impl<Gas: CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self {
Gasometer {
current_gas: current_gas,
}
}
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, 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::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 U256::zero() == val && &U256::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(Gas::from(gas))
},
instructions::SLOAD => {
InstructionCost::Gas(Gas::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_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!(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))), try!(Gas::from_u256(*stack.peek(2))))
},
instructions::EXTCODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
},
instructions::JUMPDEST => {
InstructionCost::Gas(Gas::from(1))
},
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!(self.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!(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.overflow_add(Gas::from(schedule.call_new_account_gas)));
};
if stack.peek(2) > &U256::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!(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 = Gas::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 = Gas::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, current_mem_size, &mem_size));
let gas = overflowing!(gas.overflow_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, current_mem_size, &mem_size));
let copy = overflowing!(add_gas_usize(copy, 31));
let copy_gas = Gas::from(schedule.copy_gas) * (copy / Gas::from(32 as usize));
let gas = overflowing!(gas.overflow_add(copy_gas));
let gas = overflowing!(gas.overflow_add(mem_gas));
Ok((gas, new_mem_size))
}
}
}
fn is_zero(&self, val: &Gas) -> bool {
&Gas::from(0) == val
}
fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}
fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result<Gas> {
if self.is_zero(&try!(Gas::from_u256(*size))) {
return Ok(Gas::from(0));
}
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(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)));
// We need to go to U512 to calculate s*s/quad_coeff_div
let b = U512::from(s.as_u256()) * U512::from(s.as_u256()) / U512::from(schedule.quad_coeff_div);
if b > U512::from(!U256::zero()) {
Err(evm::Error::OutOfGas)
} else {
Ok(overflowing!(a.overflow_add(try!(Gas::from_u256(U256::from(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 = if req_mem_size_rounded > current_mem_size {
let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded));
let current_mem_gas = try!(gas_for_mem(current_mem_size));
new_mem_gas - current_mem_gas
} else {
Gas::from(0)
};
Ok((mem_gas_cost, req_mem_size_rounded.as_usize()))
}
}
#[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, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then
assert_eq!(mem_cost, 3);
assert_eq!(mem_size, 32);
}

View File

@ -0,0 +1,150 @@
// 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;
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)
}
}
}
#[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

@ -16,12 +16,6 @@
///! 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"))] #[cfg(not(feature = "evm-debug"))]
macro_rules! evm_debug { macro_rules! evm_debug {
($x: expr) => {} ($x: expr) => {}
@ -34,6 +28,19 @@ 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 super::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
#[cfg(feature = "evm-debug")] #[cfg(feature = "evm-debug")]
fn color(instruction: Instruction, name: &'static str) -> String { fn color(instruction: Instruction, name: &'static str) -> String {
let c = instruction as usize % 6; let c = instruction as usize % 6;
@ -41,209 +48,9 @@ fn color(instruction: Instruction, name: &'static str) -> String {
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name) 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 CodePosition = usize;
type Gas = U256;
type ProgramCounter = usize; 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. /// Abstraction over raw vector of Bytes. Easier state management of PC.
struct CodeReader<'a> { struct CodeReader<'a> {
position: ProgramCounter, position: ProgramCounter,
@ -265,38 +72,33 @@ impl<'a> CodeReader<'a> {
} }
} }
#[cfg_attr(feature="dev", allow(enum_variant_names))] enum InstructionResult<Gas> {
enum InstructionCost {
Gas(U256),
GasMem(U256, U256),
GasMemCopy(U256, U256, U256)
}
enum InstructionResult {
Ok, Ok,
UseAllGas, UseAllGas,
GasLeft(U256), GasLeft(Gas),
UnusedGas(U256), UnusedGas(Gas),
JumpToPosition(U256), JumpToPosition(U256),
// gas left, init_orf, init_size // gas left, init_orf, init_size
StopExecutionNeedsReturn(U256, U256, U256), StopExecutionNeedsReturn(Gas, U256, U256),
StopExecution, StopExecution,
} }
/// Intepreter EVM implementation /// Intepreter EVM implementation
#[derive(Default)] #[derive(Default)]
pub struct Interpreter { pub struct Interpreter<Cost: CostType> {
mem: Vec<u8>, mem: Vec<u8>,
_type: PhantomData<Cost>,
} }
impl evm::Evm for Interpreter { impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> { fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> {
self.mem.clear(); self.mem.clear();
let code = &params.code.as_ref().unwrap(); 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 current_gas = params.gas; let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero()); let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
let mut reader = CodeReader { let mut reader = CodeReader {
position: 0, position: 0,
@ -305,26 +107,27 @@ impl evm::Evm for Interpreter {
while reader.position < code.len() { while reader.position < code.len() {
let instruction = code[reader.position]; let instruction = code[reader.position];
// Calculate gas cost
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &stack));
// 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; reader.position += 1;
try!(self.verify_gas(&current_gas, &gas_cost)); let info = instructions::get_info(instruction);
try!(self.verify_instruction(ext, instruction, &info, &stack));
// Calculate gas cost
let (gas_cost, 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());
try!(gasometer.verify_gas(&gas_cost));
self.mem.expand(mem_size); self.mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -= gasometer.current_gas = gasometer.current_gas - gas_cost;
evm_debug!({ evm_debug!({
println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}", println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}",
reader.position, reader.position,
color(instruction, instructions::get_info(instruction).name), color(instruction, info.name),
instruction, instruction,
gas_cost, gas_cost,
current_gas + gas_cost gasometer.current_gas + gas_cost
); );
}); });
@ -335,50 +138,44 @@ impl evm::Evm for Interpreter {
// Execute instruction // Execute instruction
let result = try!(self.exec_instruction( let result = try!(self.exec_instruction(
current_gas, &params, ext, instruction, &mut reader, &mut stack gasometer.current_gas, &params, ext, instruction, &mut reader, &mut stack
)); ));
if trace_executed { if trace_executed {
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); ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
} }
// Advance // Advance
match result { match result {
InstructionResult::Ok => {}, InstructionResult::Ok => {},
InstructionResult::UnusedGas(gas) => { InstructionResult::UnusedGas(gas) => {
current_gas = current_gas + gas; //TODO: use operator += gasometer.current_gas = gasometer.current_gas + gas;
}, },
InstructionResult::UseAllGas => { InstructionResult::UseAllGas => {
current_gas = U256::zero(); gasometer.current_gas = Cost::from(0);
}, },
InstructionResult::GasLeft(gas_left) => { InstructionResult::GasLeft(gas_left) => {
current_gas = gas_left; gasometer.current_gas = gas_left;
}, },
InstructionResult::JumpToPosition(position) => { InstructionResult::JumpToPosition(position) => {
let pos = try!(self.verify_jump(position, &valid_jump_destinations)); let pos = try!(self.verify_jump(position, &valid_jump_destinations));
reader.position = pos; reader.position = pos;
}, },
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => { InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size))); return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
}, },
InstructionResult::StopExecution => break, InstructionResult::StopExecution => break,
} }
} }
Ok(GasLeft::Known(current_gas)) Ok(GasLeft::Known(gasometer.current_gas.as_u256()))
} }
} }
impl Interpreter { impl<Cost: CostType> Interpreter<Cost> {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn get_gas_cost_mem( fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
&mut self,
ext: &evm::Ext,
instruction: Instruction,
stack: &Stack<U256>
) -> evm::Result<(U256, usize)> {
let schedule = ext.schedule(); let schedule = ext.schedule();
let info = instructions::get_info(instruction);
if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL { if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL {
return Err(evm::Error::BadInstruction { return Err(evm::Error::BadInstruction {
@ -391,119 +188,20 @@ impl Interpreter {
}); });
} }
try!(self.verify_instructions_requirements(&info, schedule.stack_limit, stack)); if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
let tier = instructions::get_tier_idx(info.tier); instruction: info.name,
let default_gas = U256::from(schedule.tier_step_gas[tier]); wanted: info.args,
on_stack: stack.size()
let cost = match instruction { })
instructions::SSTORE => { } else if stack.size() - info.args + info.ret > schedule.stack_limit {
let address = H256::from(stack.peek(0)); Err(evm::Error::OutOfStack {
let newval = stack.peek(1); instruction: info.name,
let val = U256::from(ext.storage_at(&address).as_slice()); wanted: info.ret - info.args,
limit: schedule.stack_limit
let gas = if self.is_zero(&val) && !self.is_zero(newval) { })
schedule.sstore_set_gas
} else { } else {
// Refund for below case is added when actually executing sstore Ok(())
// !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))
}
} }
} }
@ -532,53 +230,16 @@ impl Interpreter {
} }
} }
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))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction( fn exec_instruction(
&mut self, &mut self,
gas: Gas, gas: Cost,
params: &ActionParams, params: &ActionParams,
ext: &mut evm::Ext, ext: &mut evm::Ext,
instruction: Instruction, instruction: Instruction,
code: &mut CodeReader, code: &mut CodeReader,
stack: &mut Stack<U256> stack: &mut Stack<U256>
) -> evm::Result<InstructionResult> { ) -> evm::Result<InstructionResult<Cost>> {
match instruction { match instruction {
instructions::JUMP => { instructions::JUMP => {
let jump = stack.pop_back(); let jump = stack.pop_back();
@ -611,11 +272,11 @@ impl Interpreter {
return Ok(InstructionResult::Ok); return Ok(InstructionResult::Ok);
} }
let create_result = ext.create(&gas, &endowment, &contract_code); let create_result = ext.create(&gas.as_u256(), &endowment, &contract_code);
return match create_result { return match create_result {
ContractCreateResult::Created(address, gas_left) => { ContractCreateResult::Created(address, gas_left) => {
stack.push(address_to_u256(address)); stack.push(address_to_u256(address));
Ok(InstructionResult::GasLeft(gas_left)) Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
}, },
ContractCreateResult::Failed => { ContractCreateResult::Failed => {
stack.push(U256::zero()); stack.push(U256::zero());
@ -626,7 +287,7 @@ impl Interpreter {
}, },
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => { instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible"); assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
let call_gas = stack.pop_back(); let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated.");
let code_address = stack.pop_back(); let code_address = stack.pop_back();
let code_address = u256_to_address(&code_address); let code_address = u256_to_address(&code_address);
@ -642,9 +303,9 @@ impl Interpreter {
let out_size = stack.pop_back(); let out_size = stack.pop_back();
// Add stipend (only CALL|CALLCODE when value > 0) // Add stipend (only CALL|CALLCODE when value > 0)
let call_gas = call_gas + value.map_or_else(U256::zero, |val| match val > U256::zero() { let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val > U256::zero() {
true => U256::from(ext.schedule().call_stipend), true => Cost::from(ext.schedule().call_stipend),
false => U256::zero() false => Cost::from(0)
}); });
// Get sender & receive addresses, check if we have balance // Get sender & receive addresses, check if we have balance
@ -672,13 +333,13 @@ impl Interpreter {
// and we don't want to copy // and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) }; let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = self.mem.writeable_slice(out_off, out_size); let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output) ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output)
}; };
return match call_result { return match call_result {
MessageCallResult::Success(gas_left) => { MessageCallResult::Success(gas_left) => {
stack.push(U256::one()); stack.push(U256::one());
Ok(InstructionResult::UnusedGas(gas_left)) Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
}, },
MessageCallResult::Failed => { MessageCallResult::Failed => {
stack.push(U256::zero()); stack.push(U256::zero());
@ -759,7 +420,7 @@ impl Interpreter {
stack.push(U256::from(code.position - 1)); stack.push(U256::from(code.position - 1));
}, },
instructions::GAS => { instructions::GAS => {
stack.push(gas.clone()); stack.push(gas.as_u256());
}, },
instructions::ADDRESS => { instructions::ADDRESS => {
stack.push(address_to_u256(params.address.clone())); stack.push(address_to_u256(params.address.clone()));
@ -876,36 +537,6 @@ impl Interpreter {
} }
} }
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> { fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet<usize>) -> evm::Result<usize> {
let jump = jump_u.low_u64() as usize; let jump = jump_u.low_u64() as usize;
@ -1163,11 +794,6 @@ 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] #[inline]
fn u256_to_address(value: &U256) -> Address { fn u256_to_address(value: &U256) -> Address {
Address::from(H256::from(value)) Address::from(H256::from(value))
@ -1179,32 +805,9 @@ fn address_to_u256(value: Address) -> U256 {
} }
#[test] #[test]
fn test_mem_gas_cost() { fn test_find_jump_destinations() {
// given // given
let interpreter = Interpreter::default(); let interpreter = Interpreter::<U256>::default();
let schedule = evm::Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
// when
let result = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
// then
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(); let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when // when
@ -1212,49 +815,4 @@ mod tests {
// then // then
assert!(valid_jump_destinations.contains(&66)); 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

@ -0,0 +1,106 @@
// 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,8 +28,10 @@ mod jit;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
#[cfg(all(feature="benches", test))]
mod benches;
pub use self::evm::{Evm, Error, Finalize, GasLeft, Result}; pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType}; pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule; pub use self::schedule::Schedule;

View File

@ -18,18 +18,18 @@ use common::*;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug; use std::fmt::Debug;
struct FakeLogEntry { pub struct FakeLogEntry {
topics: Vec<H256>, topics: Vec<H256>,
data: Bytes data: Bytes
} }
#[derive(PartialEq, Eq, Hash, Debug)] #[derive(PartialEq, Eq, Hash, Debug)]
enum FakeCallType { pub enum FakeCallType {
Call, Create Call, Create
} }
#[derive(PartialEq, Eq, Hash, Debug)] #[derive(PartialEq, Eq, Hash, Debug)]
struct FakeCall { pub struct FakeCall {
call_type: FakeCallType, call_type: FakeCallType,
gas: U256, gas: U256,
sender_address: Option<Address>, sender_address: Option<Address>,
@ -43,7 +43,7 @@ struct FakeCall {
/// ///
/// Can't do recursive calls. /// Can't do recursive calls.
#[derive(Default)] #[derive(Default)]
struct FakeExt { pub struct FakeExt {
sstore_clears: usize, sstore_clears: usize,
depth: usize, depth: usize,
store: HashMap<H256, H256>, store: HashMap<H256, H256>,
@ -67,7 +67,7 @@ fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
} }
impl FakeExt { impl FakeExt {
fn new() -> Self { pub fn new() -> Self {
FakeExt::default() FakeExt::default()
} }
} }
@ -181,7 +181,7 @@ fn test_stack_underflow() {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let err = { let err = {
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::default()); let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::default());
test_finalize(vm.exec(params, &mut ext)).unwrap_err() test_finalize(vm.exec(params, &mut ext)).unwrap_err()
}; };
@ -208,7 +208,7 @@ fn test_add(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -228,7 +228,7 @@ fn test_sha3(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -248,7 +248,7 @@ fn test_address(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -270,7 +270,7 @@ fn test_origin(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -292,7 +292,7 @@ fn test_sender(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -327,7 +327,7 @@ fn test_extcodecopy(factory: super::Factory) {
ext.codes.insert(sender, sender_code); ext.codes.insert(sender, sender_code);
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -347,7 +347,7 @@ fn test_log_empty(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -379,7 +379,7 @@ fn test_log_sender(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -404,7 +404,7 @@ fn test_blockhash(factory: super::Factory) {
ext.blockhashes.insert(U256::zero(), blockhash.clone()); ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -426,7 +426,7 @@ fn test_calldataload(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -447,7 +447,7 @@ fn test_author(factory: super::Factory) {
ext.info.author = author; ext.info.author = author;
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -467,7 +467,7 @@ fn test_timestamp(factory: super::Factory) {
ext.info.timestamp = timestamp; ext.info.timestamp = timestamp;
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -487,7 +487,7 @@ fn test_number(factory: super::Factory) {
ext.info.number = number; ext.info.number = number;
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -507,7 +507,7 @@ fn test_difficulty(factory: super::Factory) {
ext.info.difficulty = difficulty; ext.info.difficulty = difficulty;
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -527,7 +527,7 @@ fn test_gas_limit(factory: super::Factory) {
ext.info.gas_limit = gas_limit; ext.info.gas_limit = gas_limit;
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -545,7 +545,7 @@ fn test_mul(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -563,7 +563,7 @@ fn test_sub(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -581,7 +581,7 @@ fn test_div(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -599,7 +599,7 @@ fn test_div_zero(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -617,7 +617,7 @@ fn test_mod(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -636,7 +636,7 @@ fn test_smod(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -655,7 +655,7 @@ fn test_sdiv(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -674,7 +674,7 @@ fn test_exp(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -694,7 +694,7 @@ fn test_comparison(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -715,7 +715,7 @@ fn test_signed_comparison(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -736,7 +736,7 @@ fn test_bitops(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -759,7 +759,7 @@ fn test_addmod_mulmod(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -780,7 +780,7 @@ fn test_byte(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -799,7 +799,7 @@ fn test_signextend(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -819,7 +819,7 @@ fn test_badinstruction_int() {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let err = { let err = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap_err() test_finalize(vm.exec(params, &mut ext)).unwrap_err()
}; };
@ -839,7 +839,7 @@ fn test_pop(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -859,7 +859,7 @@ fn test_extops(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -882,7 +882,7 @@ fn test_jumps(factory: super::Factory) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };
@ -911,7 +911,7 @@ fn test_calls(factory: super::Factory) {
}; };
let gas_left = { let gas_left = {
let mut vm = factory.create(); let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap() test_finalize(vm.exec(params, &mut ext)).unwrap()
}; };

View File

@ -211,7 +211,7 @@ impl<'a> Executive<'a> {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer); 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); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
return vm_factory.create().exec(params, &mut ext).finalize(ext); return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext);
} }
// Start in new thread to reset stack // Start in new thread to reset stack
@ -222,7 +222,7 @@ impl<'a> Executive<'a> {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || { scope.spawn(move || {
vm_factory.create().exec(params, &mut ext).finalize(ext) vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext)
}) })
}).join() }).join()
} }

View File

@ -319,7 +319,6 @@ mod tests {
last_hashes: vec![], last_hashes: vec![],
gas_used: 0.into(), gas_used: 0.into(),
gas_limit: 0.into(), gas_limit: 0.into(),
dao_rescue_block_gas_limit: None,
} }
} }

View File

@ -92,10 +92,10 @@ impl PartialEq for Header {
impl Default for Header { impl Default for Header {
fn default() -> Self { fn default() -> Self {
Header { Header {
parent_hash: ZERO_H256.clone(), parent_hash: H256::default(),
timestamp: 0, timestamp: 0,
number: 0, number: 0,
author: ZERO_ADDRESS.clone(), author: Address::default(),
transactions_root: SHA3_NULL_RLP, transactions_root: SHA3_NULL_RLP,
uncles_hash: SHA3_EMPTY_LIST_RLP, uncles_hash: SHA3_EMPTY_LIST_RLP,
@ -104,10 +104,10 @@ impl Default for Header {
state_root: SHA3_NULL_RLP, state_root: SHA3_NULL_RLP,
receipts_root: SHA3_NULL_RLP, receipts_root: SHA3_NULL_RLP,
log_bloom: ZERO_LOGBLOOM.clone(), log_bloom: ZERO_LOGBLOOM.clone(),
gas_used: ZERO_U256, gas_used: U256::default(),
gas_limit: ZERO_U256, gas_limit: U256::default(),
difficulty: ZERO_U256, difficulty: U256::default(),
seal: vec![], seal: vec![],
hash: RefCell::new(None), hash: RefCell::new(None),
bare_hash: RefCell::new(None), bare_hash: RefCell::new(None),

View File

@ -208,7 +208,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
&mut tracer, &mut tracer,
&mut vm_tracer, &mut vm_tracer,
); );
let mut evm = vm_factory.create(); let mut evm = vm_factory.create(params.gas);
let res = evm.exec(params, &mut ex); let res = evm.exec(params, &mut ex);
// a return in finalize will not alter callcreates // a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone(); let callcreates = ex.callcreates.clone();

View File

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

View File

@ -0,0 +1,69 @@
// 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 ethjson;
use util::{H256, MemoryDB, TrieMut, TrieSpec, TrieFactory};
fn test_trie(json: &[u8], trie: TrieSpec) -> Vec<String> {
let tests = ethjson::trie::Test::load(json).unwrap();
let factory = TrieFactory::new(trie);
let mut result = vec![];
for (name, test) in tests.into_iter() {
let mut memdb = MemoryDB::new();
let mut root = H256::default();
let mut t = factory.create(&mut memdb, &mut root);
for (key, value) in test.input.data.into_iter() {
let key: Vec<u8> = key.into();
let value: Vec<u8> = value.map_or_else(Vec::new, Into::into);
t.insert(&key, &value);
}
if *t.root() != test.root.into() {
result.push(format!("Trie test '{:?}' failed.", name));
}
}
for i in &result {
println!("FAILED: {}", i);
}
result
}
mod generic {
use util::TrieSpec;
fn do_json_test(json: &[u8]) -> Vec<String> {
super::test_trie(json, TrieSpec::Generic)
}
declare_test!{TrieTests_trietest, "TrieTests/trietest"}
declare_test!{TrieTests_trieanyorder, "TrieTests/trieanyorder"}
}
mod secure {
use util::TrieSpec;
fn do_json_test(json: &[u8]) -> Vec<String> {
super::test_trie(json, TrieSpec::Secure)
}
declare_test!{TrieTests_hex_encoded_secure, "TrieTests/hex_encoded_securetrie_test"}
declare_test!{TrieTests_trietest_secure, "TrieTests/trietest_secureTrie"}
declare_test!{TrieTests_trieanyorder_secure, "TrieTests/trieanyorder_secureTrie"}
}

View File

@ -31,6 +31,7 @@
#![cfg_attr(feature="dev", allow(needless_borrow))] #![cfg_attr(feature="dev", allow(needless_borrow))]
#![cfg_attr(feature="dev", allow(assign_op_pattern))] #![cfg_attr(feature="dev", allow(assign_op_pattern))]
#![cfg_attr(feature="benches", feature(test))]
//! Ethcore library //! Ethcore library
//! //!
@ -94,6 +95,8 @@ extern crate rayon;
extern crate hyper; extern crate hyper;
extern crate ethash; extern crate ethash;
pub extern crate ethstore; pub extern crate ethstore;
extern crate semver;
extern crate ethcore_ipc_nano as nanoipc;
#[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(test)] extern crate ethcore_devtools as devtools;
#[cfg(feature = "jit" )] extern crate evmjit; #[cfg(feature = "jit" )] extern crate evmjit;
@ -105,7 +108,6 @@ pub mod block_queue;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod ethereum; pub mod ethereum;
pub mod filter;
pub mod header; pub mod header;
pub mod service; pub mod service;
pub mod trace; pub mod trace;

View File

@ -1,3 +1,19 @@
// 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/>.
//! Extras database migrations. //! Extras database migrations.
mod v6; mod v6;

View File

@ -1,4 +1,20 @@
use util::migration::Migration; // 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::migration::SimpleMigration;
/// This migration reduces the sizes of keys and moves `ExtrasIndex` byte from back to the front. /// This migration reduces the sizes of keys and moves `ExtrasIndex` byte from back to the front.
pub struct ToV6; pub struct ToV6;
@ -17,12 +33,12 @@ impl ToV6 {
} }
} }
impl Migration for ToV6 { impl SimpleMigration for ToV6 {
fn version(&self) -> u32 { fn version(&self) -> u32 {
6 6
} }
fn simple_migrate(&self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> { fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
//// at this version all extras keys are 33 bytes long. //// at this version all extras keys are 33 bytes long.
if key.len() == 33 { if key.len() == 33 {

View File

@ -1,5 +1,21 @@
// 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/>.
//! State database migrations. //! State database migrations.
mod v7; mod v7;
pub use self::v7::ToV7; pub use self::v7::{ArchiveV7, OverlayRecentV7};

View File

@ -1,31 +1,205 @@
use util::hash::{FixedHash, H256}; // Copyright 2015, 2016 Ethcore (UK) Ltd.
use util::migration::Migration; // This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! This migration migrates the state db to use an accountdb which ensures uniqueness
//! using an address' hash as opposed to the address itself.
use std::collections::HashMap;
use util::Bytes;
use util::hash::{Address, FixedHash, H256};
use util::kvdb::Database;
use util::migration::{Batch, Config, Error, Migration, SimpleMigration};
use util::rlp::{decode, Rlp, RlpStream, Stream, View};
use util::sha3::Hashable; use util::sha3::Hashable;
/// This migration migrates the state db to use an accountdb which ensures uniqueness // attempt to migrate a key, value pair. None if migration not possible.
/// using an address' hash as opposed to the address itself. fn attempt_migrate(mut key_h: H256, val: &[u8]) -> Option<H256> {
pub struct ToV7; let val_hash = val.sha3();
impl Migration for ToV7 { if key_h != val_hash {
// this is a key which has been xor'd with an address.
// recover the address.
let address = key_h ^ val_hash;
// check that the address is actually a 20-byte value.
// the leftmost 12 bytes should be zero.
if &address[0..12] != &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] {
return None;
}
let address_hash = Address::from(address).sha3();
// create the xor'd key in place.
key_h.copy_from_slice(&*val_hash);
assert_eq!(key_h, val_hash);
{
let last_src: &[u8] = &*address_hash;
let last_dst: &mut [u8] = &mut *key_h;
for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) {
*k ^= *a;
}
}
Some(key_h)
} else {
None
}
}
/// Version for ArchiveDB.
pub struct ArchiveV7;
impl SimpleMigration for ArchiveV7 {
fn version(&self) -> u32 { fn version(&self) -> u32 {
7 7
} }
fn simple_migrate(&self, mut key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> { fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
let val_hash = value.sha3(); if key.len() != 32 {
assert!(key.len() == 32); // all keys in the state db are hashes. // metadata key, ignore.
let key_h = H256::from_slice(&key[..]); return Some((key, value));
if key_h != val_hash {
// this is a key which has been xor'd with an address.
// recover the address
let address = key_h ^ val_hash;
let address_hash = address.sha3();
let new_key = address_hash ^ val_hash;
key.copy_from_slice(&new_key[..]);
} }
// nothing to do here
let key_h = H256::from_slice(&key[..]);
if let Some(new_key) = attempt_migrate(key_h, &value[..]) {
Some((new_key[..].to_owned(), value))
} else {
Some((key, value)) Some((key, value))
} }
}
} }
// magic numbers and constants for overlay-recent at v6.
// re-written here because it may change in the journaldb module.
const V7_LATEST_ERA_KEY: &'static [u8] = &[ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
const V7_VERSION_KEY: &'static [u8] = &[ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
const DB_VERSION: u32 = 0x203;
const PADDING : [u8; 10] = [0u8; 10];
/// Version for OverlayRecent database.
/// more involved than the archive version because of journaling.
#[derive(Default)]
pub struct OverlayRecentV7 {
migrated_keys: HashMap<H256, H256>,
}
impl OverlayRecentV7 {
// walk all journal entries in the database backwards,
// replacing any known migrated keys with their counterparts
// and then committing again.
fn migrate_journal(&self, source: &Database, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
if let Some(val) = try!(source.get(V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.to_owned(), dest));
let mut era = decode::<u64>(&val);
loop {
let mut index: usize = 0;
loop {
let entry_key = {
let mut r = RlpStream::new_list(3);
r.append(&era).append(&index).append(&&PADDING[..]);
r.out()
};
if let Some(journal_raw) = try!(source.get(&entry_key).map_err(Error::Custom)) {
let rlp = Rlp::new(&journal_raw);
let id: H256 = rlp.val_at(0);
let mut inserted_keys: Vec<(H256, Bytes)> = Vec::new();
// migrate all inserted keys.
for r in rlp.at(1).iter() {
let mut key: H256 = r.val_at(0);
let v: Bytes = r.val_at(1);
if let Some(new_key) = self.migrated_keys.get(&key) {
key = *new_key;
} else if let Some(new_key) = attempt_migrate(key, &v) {
key = new_key;
}
inserted_keys.push((key, v));
}
// migrate all deleted keys.
let mut deleted_keys: Vec<H256> = rlp.val_at(2);
for old_key in &mut deleted_keys {
if let Some(new) = self.migrated_keys.get(&*old_key) {
*old_key = new.clone();
}
}
// rebuild the journal entry rlp.
let mut stream = RlpStream::new_list(3);
stream.append(&id);
stream.begin_list(inserted_keys.len());
for (k, v) in inserted_keys {
stream.begin_list(2).append(&k).append(&v);
}
stream.append(&deleted_keys);
// and insert it into the new database.
try!(batch.insert(entry_key, stream.out(), dest));
index += 1;
} else {
break;
}
}
if index == 0 || era == 0 {
break;
}
era -= 1;
}
}
batch.commit(dest)
}
}
impl Migration for OverlayRecentV7 {
fn version(&self) -> u32 { 7 }
// walk all records in the database, attempting to migrate any possible and
// keeping records of those that we do. then migrate the journal using
// this information.
fn migrate(&mut self, source: &Database, config: &Config, dest: &mut Database) -> Result<(), Error> {
let mut batch = Batch::new(config);
// check version metadata.
match try!(source.get(V7_VERSION_KEY).map_err(Error::Custom)) {
Some(ref version) if decode::<u32>(&*version) == DB_VERSION => {}
_ => return Err(Error::MigrationImpossible), // missing or wrong version
}
for (key, value) in source.iter() {
let mut key = key.into_vec();
if key.len() == 32 {
let key_h = H256::from_slice(&key[..]);
if let Some(new_key) = attempt_migrate(key_h.clone(), &value) {
self.migrated_keys.insert(key_h, new_key);
key.copy_from_slice(&new_key[..]);
}
}
try!(batch.insert(key, value.into_vec(), dest));
}
self.migrate_journal(source, batch, dest)
}
}

View File

@ -19,7 +19,7 @@ use std::sync::atomic::AtomicBool;
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use util::*; use util::*;
use util::Colour::White; use util::using_queue::{UsingQueue, GetAction};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use views::{BlockView, HeaderView}; use views::{BlockView, HeaderView};
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
@ -29,8 +29,10 @@ use transaction::SignedTransaction;
use receipt::{Receipt}; use receipt::{Receipt};
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
use miner::work_notify::WorkPoster; use miner::work_notify::WorkPoster;
use client::TransactionImportResult;
/// Different possible definitions for pending transaction set. /// Different possible definitions for pending transaction set.
#[derive(Debug)] #[derive(Debug)]
@ -198,17 +200,23 @@ impl Miner {
let hash = tx.hash(); let hash = tx.hash();
match open_block.push_transaction(tx, None) { match open_block.push_transaction(tx, None) {
Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => { Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => {
trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash); debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash);
// Exit early if gas left is smaller then min_tx_gas // Exit early if gas left is smaller then min_tx_gas
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly. let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
if gas_limit - gas_used < min_tx_gas { if gas_limit - gas_used < min_tx_gas {
break; break;
} }
}, },
Err(Error::Transaction(TransactionError::AlreadyImported)) => {} // already have transaction - ignore // Invalid nonce error can happen only if previous transaction is skipped because of gas limit.
// If there is errornous state of transaction queue it will be fixed when next block is imported.
Err(Error::Execution(ExecutionError::InvalidNonce { .. })) => {
debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?}", hash);
},
// already have transaction - ignore
Err(Error::Transaction(TransactionError::AlreadyImported)) => {},
Err(e) => { Err(e) => {
invalid_transactions.insert(hash); invalid_transactions.insert(hash);
trace!(target: "miner", debug!(target: "miner",
"Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
block_number, hash, e); block_number, hash, e);
}, },
@ -265,6 +273,10 @@ impl Miner {
let difficulty = *block.block().fields().header.difficulty(); let difficulty = *block.block().fields().header.difficulty();
let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h); let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h);
sealing_work.push(block); sealing_work.push(block);
// If push notifications are enabled we assume all work items are used.
if self.work_poster.is_some() && is_new {
sealing_work.use_last_ref();
}
(Some((pow_hash, difficulty, number)), is_new) (Some((pow_hash, difficulty, number)), is_new)
} else { } else {
(None, false) (None, false)
@ -303,6 +315,19 @@ impl Miner {
!have_work !have_work
} }
fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) ->
Vec<Result<TransactionImportResult, Error>> {
let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
};
transactions.into_iter()
.map(|tx| transaction_queue.add(tx, &fetch_account, origin))
.collect()
}
/// Are we allowed to do a non-mandatory reseal? /// Are we allowed to do a non-mandatory reseal?
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() } fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() }
} }
@ -343,7 +368,6 @@ impl MinerService for Miner {
last_hashes: last_hashes, last_hashes: last_hashes,
gas_used: U256::zero(), gas_used: U256::zero(),
gas_limit: U256::max_value(), gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: chain.dao_rescue_block_gas_limit(header.parent_hash().clone()),
}; };
// that's just a copy of the state. // that's just a copy of the state.
let mut state = block.state().clone(); let mut state = block.state().clone();
@ -466,27 +490,24 @@ impl MinerService for Miner {
self.gas_range_target.read().unwrap().1 self.gas_range_target.read().unwrap().1
} }
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) -> fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
Vec<Result<TransactionImportResult, Error>> Vec<Result<TransactionImportResult, Error>> {
where T: Fn(&Address) -> AccountDetails {
let results: Vec<Result<TransactionImportResult, Error>> = {
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let mut transaction_queue = self.transaction_queue.lock().unwrap();
transactions.into_iter() let results = self.add_transactions_to_queue(chain, transactions, TransactionOrigin::External,
.map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External)) &mut transaction_queue);
.collect()
};
if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() { if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
self.update_sealing(chain); self.update_sealing(chain);
} }
results results
} }
fn import_own_transaction<T>( fn import_own_transaction(
&self, &self,
chain: &MiningBlockChainClient, chain: &MiningBlockChainClient,
transaction: SignedTransaction, transaction: SignedTransaction,
fetch_account: T ) -> Result<TransactionImportResult, Error> {
) -> Result<TransactionImportResult, Error> where T: Fn(&Address) -> AccountDetails {
let hash = transaction.hash(); let hash = transaction.hash();
trace!(target: "own_tx", "Importing transaction: {:?}", transaction); trace!(target: "own_tx", "Importing transaction: {:?}", transaction);
@ -494,7 +515,7 @@ impl MinerService for Miner {
let imported = { let imported = {
// Be sure to release the lock before we call enable_and_prepare_sealing // Be sure to release the lock before we call enable_and_prepare_sealing
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let mut transaction_queue = self.transaction_queue.lock().unwrap();
let import = transaction_queue.add(transaction, &fetch_account, TransactionOrigin::Local); let import = self.add_transactions_to_queue(chain, vec![transaction], TransactionOrigin::Local, &mut transaction_queue).pop().unwrap();
match import { match import {
Ok(ref res) => { Ok(ref res) => {
@ -633,7 +654,7 @@ impl MinerService for Miner {
let n = sealed.header().number(); let n = sealed.header().number();
let h = sealed.header().hash(); let h = sealed.header().hash();
try!(chain.import_sealed_block(sealed)); try!(chain.import_sealed_block(sealed));
info!(target: "miner", "Mined block imported OK. #{}: {}", paint(White.bold(), format!("{}", n)), paint(White.bold(), h.hex())); info!(target: "miner", "Mined block imported OK. #{}: {}", format!("{}", n).apply(Colour::White.bold()), h.hex().apply(Colour::White.bold()));
Ok(()) Ok(())
}) })
} }
@ -645,7 +666,12 @@ impl MinerService for Miner {
// Client should send message after commit to db and inserting to chain. // Client should send message after commit to db and inserting to chain.
.expect("Expected in-chain blocks."); .expect("Expected in-chain blocks.");
let block = BlockView::new(&block); let block = BlockView::new(&block);
block.transactions() let txs = block.transactions();
// populate sender
for tx in &txs {
let _sender = tx.sender();
}
txs
} }
// 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions // 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions
@ -662,14 +688,10 @@ impl MinerService for Miner {
.par_iter() .par_iter()
.map(|h| fetch_transactions(chain, h)); .map(|h| fetch_transactions(chain, h));
out_of_chain.for_each(|txs| { out_of_chain.for_each(|txs| {
// populate sender let mut transaction_queue = self.transaction_queue.lock().unwrap();
for tx in &txs { let _ = self.add_transactions_to_queue(
let _sender = tx.sender(); chain, txs, TransactionOrigin::External, &mut transaction_queue
} );
let _ = self.import_transactions(chain, txs, |a| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
});
}); });
} }

View File

@ -33,7 +33,7 @@
//! use ethcore::miner::{Miner, MinerService}; //! use ethcore::miner::{Miner, MinerService};
//! //!
//! fn main() { //! fn main() {
//! let miner: Miner = Miner::with_spec(ethereum::new_frontier(true)); //! let miner: Miner = Miner::with_spec(ethereum::new_frontier());
//! // get status //! // get status
//! assert_eq!(miner.status().transactions_in_pending_queue, 0); //! assert_eq!(miner.status().transactions_in_pending_queue, 0);
//! //!
@ -47,9 +47,10 @@ mod external;
mod transaction_queue; mod transaction_queue;
mod work_notify; mod work_notify;
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin};
pub use self::miner::{Miner, MinerOptions, PendingSet}; pub use self::miner::{Miner, MinerOptions, PendingSet};
pub use self::external::{ExternalMiner, ExternalMinerService}; pub use self::external::{ExternalMiner, ExternalMinerService};
pub use client::TransactionImportResult;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use util::{H256, U256, Address, Bytes}; use util::{H256, U256, Address, Bytes};
@ -106,14 +107,12 @@ pub trait MinerService : Send + Sync {
fn set_tx_gas_limit(&self, limit: U256); fn set_tx_gas_limit(&self, limit: U256);
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) -> fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
Vec<Result<TransactionImportResult, Error>> Vec<Result<TransactionImportResult, Error>>;
where T: Fn(&Address) -> AccountDetails, Self: Sized;
/// Imports own (node owner) transaction to queue. /// Imports own (node owner) transaction to queue.
fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, fetch_account: T) -> fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction) ->
Result<TransactionImportResult, Error> Result<TransactionImportResult, Error>;
where T: Fn(&Address) -> AccountDetails, Self: Sized;
/// Returns hashes of transactions currently in pending /// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self) -> Vec<H256>; fn pending_transactions_hashes(&self) -> Vec<H256>;

View File

@ -87,9 +87,10 @@ use std::cmp;
use std::collections::{HashMap, BTreeSet}; use std::collections::{HashMap, BTreeSet};
use util::numbers::{Uint, U256}; use util::numbers::{Uint, U256};
use util::hash::{Address, H256}; use util::hash::{Address, H256};
use util::table::*; use util::table::Table;
use transaction::*; use transaction::*;
use error::{Error, TransactionError}; use error::{Error, TransactionError};
use client::TransactionImportResult;
/// Transaction origin /// Transaction origin
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -197,6 +198,7 @@ struct VerifiedTransaction {
/// transaction origin /// transaction origin
origin: TransactionOrigin, origin: TransactionOrigin,
} }
impl VerifiedTransaction { impl VerifiedTransaction {
fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> { fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> {
try!(transaction.sender()); try!(transaction.sender());
@ -309,15 +311,6 @@ pub struct TransactionQueueStatus {
pub future: usize, pub future: usize,
} }
#[derive(Debug, PartialEq)]
/// Represents the result of importing transaction.
pub enum TransactionImportResult {
/// Transaction was imported to current queue.
Current,
/// Transaction was imported to future queue.
Future
}
/// Details of account /// Details of account
pub struct AccountDetails { pub struct AccountDetails {
/// Most recent account nonce /// Most recent account nonce
@ -439,10 +432,10 @@ impl TransactionQueue {
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result<TransactionImportResult, Error> pub fn add<T>(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails { where T: Fn(&Address) -> AccountDetails {
trace!(target: "miner", "Importing: {:?}", tx.hash()); trace!(target: "txqueue", "Importing: {:?}", tx.hash());
if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local { if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local {
trace!(target: "miner", trace!(target: "txqueue",
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
tx.hash(), tx.hash(),
tx.gas_price, tx.gas_price,
@ -458,7 +451,7 @@ impl TransactionQueue {
try!(tx.check_low_s()); try!(tx.check_low_s());
if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit { if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit {
trace!(target: "miner", trace!(target: "txqueue",
"Dropping transaction above gas limit: {:?} ({} > min({}, {}))", "Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
tx.hash(), tx.hash(),
tx.gas, tx.gas,
@ -477,7 +470,7 @@ impl TransactionQueue {
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas; let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
if client_account.balance < cost { if client_account.balance < cost {
trace!(target: "miner", trace!(target: "txqueue",
"Dropping transaction without sufficient balance: {:?} ({} < {})", "Dropping transaction without sufficient balance: {:?} ({} < {})",
vtx.hash(), vtx.hash(),
client_account.balance, client_account.balance,
@ -565,7 +558,7 @@ impl TransactionQueue {
if k >= current_nonce { if k >= current_nonce {
self.future.insert(*sender, k, order.update_height(k, current_nonce)); self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else { } else {
trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
// Remove the transaction completely // Remove the transaction completely
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`"); self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
} }
@ -586,7 +579,7 @@ impl TransactionQueue {
if k >= current_nonce { if k >= current_nonce {
self.future.insert(*sender, k, order.update_height(k, current_nonce)); self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else { } else {
trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`"); self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`");
} }
} }
@ -674,7 +667,7 @@ impl TransactionQueue {
if self.by_hash.get(&tx.hash()).is_some() { if self.by_hash.get(&tx.hash()).is_some() {
// Transaction is already imported. // Transaction is already imported.
trace!(target: "miner", "Dropping already imported transaction: {:?}", tx.hash()); trace!(target: "txqueue", "Dropping already imported transaction: {:?}", tx.hash());
return Err(TransactionError::AlreadyImported); return Err(TransactionError::AlreadyImported);
} }
@ -691,7 +684,7 @@ impl TransactionQueue {
// nonce height would result in overflow. // nonce height would result in overflow.
if nonce < state_nonce { if nonce < state_nonce {
// Droping transaction // Droping transaction
trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce); trace!(target: "txqueue", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce);
return Err(TransactionError::Old); return Err(TransactionError::Old);
} else if nonce > next_nonce { } else if nonce > next_nonce {
// We have a gap - put to future. // We have a gap - put to future.
@ -727,7 +720,7 @@ impl TransactionQueue {
// Trigger error if the transaction we are importing was removed. // Trigger error if the transaction we are importing was removed.
try!(check_if_removed(&address, &nonce, removed)); try!(check_if_removed(&address, &nonce, removed));
trace!(target: "miner", "status: {:?}", self.status()); trace!(target: "txqueue", "status: {:?}", self.status());
Ok(TransactionImportResult::Current) Ok(TransactionImportResult::Current)
} }
@ -812,6 +805,7 @@ mod test {
use error::{Error, TransactionError}; use error::{Error, TransactionError};
use super::*; use super::*;
use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
use client::TransactionImportResult;
fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError { fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError {
match err.unwrap_err() { match err.unwrap_err() {

View File

@ -52,10 +52,10 @@ impl WorkPoster {
} }
fn create_client() -> Client<PostHandler> { fn create_client() -> Client<PostHandler> {
let client = Client::<PostHandler>::configure() Client::<PostHandler>::configure()
.keep_alive(true) .keep_alive(true)
.build().expect("Error creating HTTP client") as Client<PostHandler>; .build()
client .expect("Error creating HTTP client")
} }
pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
@ -63,8 +63,10 @@ impl WorkPoster {
let target = Ethash::difficulty_to_boundary(&difficulty); let target = Ethash::difficulty_to_boundary(&difficulty);
let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number); let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number);
let seed_hash = H256::from_slice(&seed_hash[..]); let seed_hash = H256::from_slice(&seed_hash[..]);
let body = format!(r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#, let body = format!(
pow_hash.hex(), seed_hash.hex(), target.hex(), number); r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
pow_hash.hex(), seed_hash.hex(), target.hex(), number
);
let mut client = self.client.lock().unwrap(); let mut client = self.client.lock().unwrap();
for u in &self.urls { for u in &self.urls {
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) { if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {

View File

@ -72,7 +72,7 @@ impl fmt::Display for PodState {
/// Calculate and return diff between `pre` state and `post` state. /// Calculate and return diff between `pre` state and `post` state.
pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect()) StateDiff { raw: pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect() }
} }
#[cfg(test)] #[cfg(test)]
@ -86,22 +86,22 @@ mod test {
#[test] #[test]
fn create_delete() { fn create_delete() {
let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]);
assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff(map![ assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff { raw: map![
1.into() => AccountDiff{ 1.into() => AccountDiff{
balance: Diff::Died(69.into()), balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()), nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]), code: Diff::Died(vec![]),
storage: map![], storage: map![],
} }
])); ]});
assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff(map![ assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff{ raw: map![
1.into() => AccountDiff{ 1.into() => AccountDiff{
balance: Diff::Born(69.into()), balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()), nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]), code: Diff::Born(vec![]),
storage: map![], storage: map![],
} }
])); ]});
} }
#[test] #[test]
@ -111,22 +111,22 @@ mod test {
1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]); ]);
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![ assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
2.into() => AccountDiff{ 2.into() => AccountDiff{
balance: Diff::Born(69.into()), balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()), nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]), code: Diff::Born(vec![]),
storage: map![], storage: map![],
} }
])); ]});
assert_eq!(super::diff_pod(&b, &a), StateDiff(map![ assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![
2.into() => AccountDiff{ 2.into() => AccountDiff{
balance: Diff::Died(69.into()), balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()), nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]), code: Diff::Died(vec![]),
storage: map![], storage: map![],
} }
])); ]});
} }
#[test] #[test]
@ -139,14 +139,14 @@ mod test {
1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]), 1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]),
2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![])
]); ]);
assert_eq!(super::diff_pod(&a, &b), StateDiff(map![ assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
1.into() => AccountDiff{ 1.into() => AccountDiff{
balance: Diff::Same, balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()), nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Same, code: Diff::Same,
storage: map![], storage: map![],
} }
])); ]});
} }
} }

View File

@ -17,7 +17,6 @@
//! Creates and registers client and network services. //! Creates and registers client and network services.
use util::*; use util::*;
use util::Colour::{Yellow, White};
use util::panics::*; use util::panics::*;
use spec::Spec; use spec::Spec;
use error::*; use error::*;
@ -72,7 +71,7 @@ impl ClientService {
try!(net_service.start()); try!(net_service.start());
} }
info!("Configured for {} using {} engine", paint(White.bold(), spec.name.clone()), paint(Yellow.bold(), spec.engine.name().to_owned())); info!("Configured for {} using {} engine", spec.name.clone().apply(Colour::White.bold()), spec.engine.name().apply(Colour::Yellow.bold()));
let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel())); let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel()));
panic_handler.forward_from(client.deref()); panic_handler.forward_from(client.deref());
let client_io = Arc::new(ClientIoHandler { let client_io = Arc::new(ClientIoHandler {
@ -135,16 +134,14 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
#[cfg_attr(feature="dev", allow(single_match))] #[cfg_attr(feature="dev", allow(single_match))]
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) { fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
if let UserMessage(ref message) = *net_message { match *net_message {
match *message { UserMessage(ref message) => match *message {
SyncMessage::BlockVerified => { SyncMessage::BlockVerified => { self.client.import_verified_blocks(&io.channel()); }
self.client.import_verified_blocks(&io.channel()); SyncMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(&transactions); }
_ => {} // ignore other messages
}, },
SyncMessage::NewTransactions(ref transactions) => { NetworkIoMessage::NetworkStarted(ref url) => { self.client.network_started(url); }
self.client.import_queued_transactions(&transactions); _ => {} // ignore other messages
},
_ => {}, // ignore other messages
}
} }
} }
} }

View File

@ -17,7 +17,6 @@
//! Account state encoding and decoding //! Account state encoding and decoding
use account_db::{AccountDB, AccountDBMut}; use account_db::{AccountDB, AccountDBMut};
use client::BlockChainClient;
use error::Error; use error::Error;
use util::{Bytes, HashDB, SHA3_EMPTY, TrieDB}; use util::{Bytes, HashDB, SHA3_EMPTY, TrieDB};

View File

@ -17,15 +17,15 @@
//! Snapshot creation helpers. //! Snapshot creation helpers.
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fs::File; use std::fs::{create_dir_all, File};
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::{Path, PathBuf};
use account_db::{AccountDB, AccountDBMut}; use account_db::{AccountDB, AccountDBMut};
use client::BlockChainClient; use client::BlockChainClient;
use error::Error; use error::Error;
use ids::BlockID; use ids::BlockID;
use views::BlockView; use views::{BlockView, HeaderView};
use util::{Bytes, Hashable, HashDB, JournalDB, snappy, TrieDB, TrieDBMut, TrieMut}; use util::{Bytes, Hashable, HashDB, JournalDB, snappy, TrieDB, TrieDBMut, TrieMut};
use util::hash::{FixedHash, H256}; use util::hash::{FixedHash, H256};
@ -42,6 +42,40 @@ mod block;
// Try to have chunks be around 16MB (before compression) // Try to have chunks be around 16MB (before compression)
const PREFERRED_CHUNK_SIZE: usize = 16 * 1024 * 1024; const PREFERRED_CHUNK_SIZE: usize = 16 * 1024 * 1024;
/// Take a snapshot using the given client and database, writing into `path`.
pub fn take_snapshot(client: &BlockChainClient, mut path: PathBuf, state_db: &HashDB) {
let chain_info = client.chain_info();
let genesis_hash = chain_info.genesis_hash;
let best_header_raw = client.best_block_header();
let best_header = HeaderView::new(&best_header_raw);
let state_root = best_header.state_root();
trace!(target: "snapshot", "Taking snapshot starting at block {}", best_header.number());
let _ = create_dir_all(&path);
// lock the state db while we create the state chunks.
let state_hashes = {
chunk_state(state_db, &state_root, &path).unwrap()
};
let block_hashes = chunk_blocks(client, best_header.hash(), genesis_hash, &path).unwrap();
trace!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
let manifest_data = ManifestData {
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: state_root,
};
path.push("MANIFEST");
let mut manifest_file = File::create(&path).unwrap();
manifest_file.write_all(&manifest_data.to_rlp()).unwrap();
}
// shared portion of write_chunk // shared portion of write_chunk
// returns either a (hash, compressed_size) pair or an io error. // returns either a (hash, compressed_size) pair or an io error.
fn write_chunk(raw_data: &[u8], compression_buffer: &mut Vec<u8>, path: &Path) -> Result<(H256, usize), Error> { fn write_chunk(raw_data: &[u8], compression_buffer: &mut Vec<u8>, path: &Path) -> Result<(H256, usize), Error> {

View File

@ -42,6 +42,7 @@ pub struct State {
cache: RefCell<HashMap<Address, Option<Account>>>, cache: RefCell<HashMap<Address, Option<Account>>>,
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>, snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
account_start_nonce: U256, account_start_nonce: U256,
trie_factory: TrieFactory,
} }
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
@ -50,11 +51,11 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
impl State { impl State {
/// Creates new state with empty state root /// Creates new state with empty state root
#[cfg(test)] #[cfg(test)]
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256) -> State { pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, trie_factory: TrieFactory) -> State {
let mut root = H256::new(); let mut root = H256::new();
{ {
// init trie and reset root too null // init trie and reset root too null
let _ = SecTrieDBMut::new(db.as_hashdb_mut(), &mut root); let _ = trie_factory.create(db.as_hashdb_mut(), &mut root);
} }
State { State {
@ -63,22 +64,26 @@ impl State {
cache: RefCell::new(HashMap::new()), cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()), snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce, account_start_nonce: account_start_nonce,
trie_factory: trie_factory,
} }
} }
/// Creates new state with existing state root /// Creates new state with existing state root
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256) -> Result<State, TrieError> { pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result<State, TrieError> {
if !db.as_hashdb().contains(&root) { if !db.as_hashdb().contains(&root) {
Err(TrieError::InvalidStateRoot) return Err(TrieError::InvalidStateRoot);
} else { }
Ok(State {
let state = State {
db: db, db: db,
root: root, root: root,
cache: RefCell::new(HashMap::new()), cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()), snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce, account_start_nonce: account_start_nonce,
}) trie_factory: trie_factory,
} };
Ok(state)
} }
/// Create a recoverable snaphot of this state /// Create a recoverable snaphot of this state
@ -156,7 +161,7 @@ impl State {
/// Determine whether an account exists. /// Determine whether an account exists.
pub fn exists(&self, a: &Address) -> bool { pub fn exists(&self, a: &Address) -> bool {
let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
self.cache.borrow().get(&a).unwrap_or(&None).is_some() || db.contains(&a) self.cache.borrow().get(&a).unwrap_or(&None).is_some() || db.contains(&a)
} }
@ -242,7 +247,10 @@ impl State {
for a in &addresses { for a in &addresses {
if self.code(a).map_or(false, |c| c.sha3() == broken_dao) { if self.code(a).map_or(false, |c| c.sha3() == broken_dao) {
// Figure out if the balance has been reduced. // Figure out if the balance has been reduced.
let maybe_original = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR).get(&a).map(Account::from_rlp); let maybe_original = self.trie_factory
.readonly(self.db.as_hashdb(), &self.root)
.expect(SEC_TRIE_DB_UNWRAP_STR)
.get(&a).map(Account::from_rlp);
if maybe_original.map_or(false, |original| *original.balance() > self.balance(a)) { if maybe_original.map_or(false, |original| *original.balance() > self.balance(a)) {
return Err(Error::Transaction(TransactionError::DAORescue)); return Err(Error::Transaction(TransactionError::DAORescue));
} }
@ -262,14 +270,14 @@ impl State {
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
/// `accounts` is mutable because we may need to commit the code or storage and record that. /// `accounts` is mutable because we may need to commit the code or storage and record that.
#[cfg_attr(feature="dev", allow(match_ref_pats))] #[cfg_attr(feature="dev", allow(match_ref_pats))]
pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) { pub fn commit_into(trie_factory: &TrieFactory, db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) {
// first, commit the sub trees. // first, commit the sub trees.
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
for (address, ref mut a) in accounts.iter_mut() { for (address, ref mut a) in accounts.iter_mut() {
match a { match a {
&mut&mut Some(ref mut account) => { &mut&mut Some(ref mut account) => {
let mut account_db = AccountDBMut::new(db, address); let mut account_db = AccountDBMut::new(db, address);
account.commit_storage(&mut account_db); account.commit_storage(trie_factory, &mut account_db);
account.commit_code(&mut account_db); account.commit_code(&mut account_db);
} }
&mut&mut None => {} &mut&mut None => {}
@ -277,7 +285,7 @@ impl State {
} }
{ {
let mut trie = SecTrieDBMut::from_existing(db, root).unwrap(); let mut trie = trie_factory.from_existing(db, root).unwrap();
for (address, ref a) in accounts.iter() { for (address, ref a) in accounts.iter() {
match **a { match **a {
Some(ref account) => trie.insert(address, &account.rlp()), Some(ref account) => trie.insert(address, &account.rlp()),
@ -290,7 +298,7 @@ impl State {
/// Commits our cached account changes into the trie. /// Commits our cached account changes into the trie.
pub fn commit(&mut self) { pub fn commit(&mut self) {
assert!(self.snapshots.borrow().is_empty()); assert!(self.snapshots.borrow().is_empty());
Self::commit_into(self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut()); Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut());
} }
#[cfg(test)] #[cfg(test)]
@ -340,7 +348,7 @@ impl State {
fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> { fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> {
let have_key = self.cache.borrow().contains_key(a); let have_key = self.cache.borrow().contains_key(a);
if !have_key { if !have_key {
let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
self.insert_cache(a, db.get(&a).map(Account::from_rlp)) self.insert_cache(a, db.get(&a).map(Account::from_rlp))
} }
if require_code { if require_code {
@ -361,7 +369,7 @@ impl State {
fn require_or_from<'a, F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&self, a: &Address, require_code: bool, default: F, not_default: G) -> &'a mut Account { fn require_or_from<'a, F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&self, a: &Address, require_code: bool, default: F, not_default: G) -> &'a mut Account {
let have_key = self.cache.borrow().contains_key(a); let have_key = self.cache.borrow().contains_key(a);
if !have_key { if !have_key {
let db = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
self.insert_cache(a, db.get(&a).map(Account::from_rlp)) self.insert_cache(a, db.get(&a).map(Account::from_rlp))
} else { } else {
self.note_cache(a); self.note_cache(a);
@ -396,6 +404,7 @@ impl Clone for State {
cache: RefCell::new(self.cache.borrow().clone()), cache: RefCell::new(self.cache.borrow().clone()),
snapshots: RefCell::new(self.snapshots.borrow().clone()), snapshots: RefCell::new(self.snapshots.borrow().clone()),
account_start_nonce: self.account_start_nonce.clone(), account_start_nonce: self.account_start_nonce.clone(),
trie_factory: self.trie_factory.clone(),
} }
} }
} }
@ -1179,7 +1188,7 @@ fn code_from_database() {
state.drop() state.drop()
}; };
let state = State::from_existing(db, root, U256::from(0u8)).unwrap(); let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
} }
@ -1194,7 +1203,7 @@ fn storage_at_from_database() {
state.drop() state.drop()
}; };
let s = State::from_existing(db, root, U256::from(0u8)).unwrap(); let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64))); assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64)));
} }
@ -1211,7 +1220,7 @@ fn get_from_database() {
state.drop() state.drop()
}; };
let state = State::from_existing(db, root, U256::from(0u8)).unwrap(); let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.balance(&a), U256::from(69u64)); assert_eq!(state.balance(&a), U256::from(69u64));
assert_eq!(state.nonce(&a), U256::from(1u64)); assert_eq!(state.nonce(&a), U256::from(1u64));
} }
@ -1244,7 +1253,7 @@ fn remove_from_database() {
}; };
let (root, db) = { let (root, db) = {
let mut state = State::from_existing(db, root, U256::from(0u8)).unwrap(); let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.exists(&a), true); assert_eq!(state.exists(&a), true);
assert_eq!(state.nonce(&a), U256::from(1u64)); assert_eq!(state.nonce(&a), U256::from(1u64));
state.kill_account(&a); state.kill_account(&a);
@ -1254,7 +1263,7 @@ fn remove_from_database() {
state.drop() state.drop()
}; };
let state = State::from_existing(db, root, U256::from(0u8)).unwrap(); let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
assert_eq!(state.exists(&a), false); assert_eq!(state.exists(&a), false);
assert_eq!(state.nonce(&a), U256::from(0u64)); assert_eq!(state.nonce(&a), U256::from(0u64));
} }

View File

@ -175,11 +175,11 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
let mut b = OpenBlock::new( let mut b = OpenBlock::new(
test_engine.deref(), test_engine.deref(),
&vm_factory, &vm_factory,
Default::default(),
false, false,
db, db,
&last_header, &last_header,
last_hashes.clone(), last_hashes.clone(),
None,
author.clone(), author.clone(),
(3141562.into(), 31415620.into()), (3141562.into(), 31415620.into()),
vec![] vec![]
@ -315,7 +315,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
let journal_db = get_temp_journal_db_in(temp.as_path()); let journal_db = get_temp_journal_db_in(temp.as_path());
GuardedTempResult { GuardedTempResult {
_temp: temp, _temp: temp,
result: Some(State::new(journal_db, U256::from(0u8))) result: Some(State::new(journal_db, U256::from(0), Default::default())),
} }
} }
@ -325,7 +325,7 @@ pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> {
pub fn get_temp_state_in(path: &Path) -> State { pub fn get_temp_state_in(path: &Path) -> State {
let journal_db = get_temp_journal_db_in(path); let journal_db = get_temp_journal_db_in(path);
State::new(journal_db, U256::from(0u8)) State::new(journal_db, U256::from(0), Default::default())
} }
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> { pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {

View File

@ -16,3 +16,4 @@
pub mod helpers; pub mod helpers;
mod client; mod client;
mod rpc;

71
ethcore/src/tests/rpc.rs Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Client RPC tests
use nanoipc;
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicBool};
use client::{Client, BlockChainClient, ClientConfig, RemoteClient, BlockID};
use tests::helpers::*;
use devtools::*;
use miner::Miner;
use crossbeam;
use common::IoChannel;
pub fn run_test_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
let socket_path = socket_path.to_owned();
scope.spawn(move || {
let temp = RandomTempPath::create_dir();
let client = Client::new(
ClientConfig::default(),
get_test_spec(),
temp.as_path(),
Arc::new(Miner::with_spec(get_test_spec())),
IoChannel::disconnected()).unwrap();
let mut worker = nanoipc::Worker::new(&client);
worker.add_reqrep(&socket_path).unwrap();
while !stop.load(Ordering::Relaxed) {
worker.poll();
}
});
}
#[test]
fn can_handshake() {
crossbeam::scope(|scope| {
let stop_guard = StopGuard::new();
let socket_path = "ipc:///tmp/parity-client-rpc-10.ipc";
run_test_worker(scope, stop_guard.share(), socket_path);
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
assert!(remote_client.handshake().is_ok());
})
}
#[test]
fn can_query_block() {
crossbeam::scope(|scope| {
let stop_guard = StopGuard::new();
let socket_path = "ipc:///tmp/parity-client-rpc-20.ipc";
run_test_worker(scope, stop_guard.share(), socket_path);
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
let non_existant_block = remote_client.block_header(BlockID::Number(999));
assert!(non_existant_block.is_none());
})
}

View File

@ -16,11 +16,17 @@
//! Diff between two accounts. //! Diff between two accounts.
use util::*; use util::numbers::*;
use std::cmp::*;
use std::fmt;
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use util::Bytes;
use std::collections::{VecDeque, BTreeMap};
use std::mem;
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Diff type for specifying a change (or not). /// Diff type for specifying a change (or not).
pub enum Diff<T> where T: Eq { pub enum Diff<T> where T: Eq + BinaryConvertable {
/// Both sides are the same. /// Both sides are the same.
Same, Same,
/// Left (pre, source) side doesn't include value, right side (post, destination) does. /// Left (pre, source) side doesn't include value, right side (post, destination) does.
@ -31,7 +37,7 @@ pub enum Diff<T> where T: Eq {
Died(T), Died(T),
} }
impl<T> Diff<T> where T: Eq { impl<T> Diff<T> where T: Eq + BinaryConvertable {
/// Construct new object with given `pre` and `post`. /// Construct new object with given `pre` and `post`.
pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } } pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } }
@ -45,7 +51,7 @@ impl<T> Diff<T> where T: Eq {
pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }} pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }}
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Account diff. /// Account diff.
pub struct AccountDiff { pub struct AccountDiff {
/// Change in balance, allowed to be `Diff::Same`. /// Change in balance, allowed to be `Diff::Same`.
@ -58,7 +64,7 @@ pub struct AccountDiff {
pub storage: BTreeMap<H256, Diff<H256>>, pub storage: BTreeMap<H256, Diff<H256>>,
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Change in existance type. /// Change in existance type.
// TODO: include other types of change. // TODO: include other types of change.
pub enum Existance { pub enum Existance {
@ -94,6 +100,8 @@ impl AccountDiff {
// TODO: refactor into something nicer. // TODO: refactor into something nicer.
fn interpreted_hash(u: &H256) -> String { fn interpreted_hash(u: &H256) -> String {
use util::bytes::*;
if u <= &H256::from(0xffffffff) { if u <= &H256::from(0xffffffff) {
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32()) format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
} else if u <= &H256::from(u64::max_value()) { } else if u <= &H256::from(u64::max_value()) {
@ -107,6 +115,8 @@ fn interpreted_hash(u: &H256) -> String {
impl fmt::Display for AccountDiff { impl fmt::Display for AccountDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use util::bytes::*;
match self.nonce { match self.nonce {
Diff::Born(ref x) => try!(write!(f, " non {}", x)), Diff::Born(ref x) => try!(write!(f, " non {}", x)),
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))), Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),

View File

@ -0,0 +1,44 @@
// 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/>.
//! Block import error related types
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
use error::{ImportError, BlockError, Error};
use std::convert::From;
/// Error dedicated to import block function
#[derive(Binary, Debug)]
pub enum BlockImportError {
/// Import error
Import(ImportError),
/// Block error
Block(BlockError),
/// Other error
Other(String),
}
impl From<Error> for BlockImportError {
fn from(e: Error) -> Self {
match e {
Error::Block(block_error) => BlockImportError::Block(block_error),
Error::Import(import_error) => BlockImportError::Import(import_error),
_ => BlockImportError::Other(format!("other block import error: {:?}", e)),
}
}
}

View File

@ -0,0 +1,38 @@
// 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/>.
//! Block queue info types
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
/// Block queue status
#[derive(Debug, Binary)]
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,
}

View File

@ -0,0 +1,32 @@
// 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/>.
//! Call analytics related types
use std::mem;
use ipc::binary::{BinaryConvertError};
use std::collections::VecDeque;
/// Options concerning what analytics we run on the call.
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug, Binary)]
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,
}

View File

@ -27,7 +27,7 @@ use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
/// Transaction execution receipt. /// Transaction execution receipt.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone, Binary)]
pub struct Executed { pub struct Executed {
/// Gas paid up front for execution of transaction. /// Gas paid up front for execution of transaction.
pub gas: U256, pub gas: U256,

View File

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

View File

@ -47,6 +47,7 @@ pub enum TransactionID {
} }
/// Uniquely identifies Trace. /// Uniquely identifies Trace.
#[derive(Binary)]
pub struct TraceId { pub struct TraceId {
/// Transaction /// Transaction
pub transaction: TransactionID, pub transaction: TransactionID,
@ -55,10 +56,10 @@ pub struct TraceId {
} }
/// Uniquely identifies Uncle. /// Uniquely identifies Uncle.
#[derive(Debug)] #[derive(Debug, Binary)]
pub struct UncleID ( pub struct UncleID {
/// Block id. /// Block id.
pub BlockID, pub block: BlockID,
/// Position in block. /// Position in block.
pub usize pub position: usize
); }

View File

@ -25,3 +25,9 @@ pub mod executed;
pub mod block_status; pub mod block_status;
pub mod account_diff; pub mod account_diff;
pub mod state_diff; pub mod state_diff;
pub mod block_queue_info;
pub mod filter;
pub mod trace_filter;
pub mod call_analytics;
pub mod transaction_import;
pub mod block_import_error;

View File

@ -16,24 +16,32 @@
//! State diff module. //! State diff module.
use util::*; use util::numbers::*;
use account_diff::*; use account_diff::*;
use ipc::binary::BinaryConvertError;
use std::mem;
use std::fmt;
use std::ops::*;
use std::collections::{VecDeque, BTreeMap};
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Binary)]
/// Expression for the delta between two system states. Encoded the /// Expression for the delta between two system states. Encoded the
/// delta of every altered account. /// delta of every altered account.
pub struct StateDiff (pub BTreeMap<Address, AccountDiff>); pub struct StateDiff {
/// Raw diff key-value
pub raw: BTreeMap<Address, AccountDiff>
}
impl StateDiff { impl StateDiff {
/// Get the actual data. /// Get the actual data.
pub fn get(&self) -> &BTreeMap<Address, AccountDiff> { pub fn get(&self) -> &BTreeMap<Address, AccountDiff> {
&self.0 &self.raw
} }
} }
impl fmt::Display for StateDiff { impl fmt::Display for StateDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (add, acc) in &self.0 { for (add, acc) in &self.raw {
try!(write!(f, "{} {}: {}", acc.existance(), add, acc)); try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
} }
Ok(()) Ok(())
@ -44,6 +52,6 @@ impl Deref for StateDiff {
type Target = BTreeMap<Address, AccountDiff>; type Target = BTreeMap<Address, AccountDiff>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.raw
} }
} }

View File

@ -0,0 +1,35 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trace filter related types
use std::mem;
use ipc::binary::{BinaryConvertError};
use std::collections::VecDeque;
use std::ops::Range;
use util::{Address};
use types::ids::BlockID;
/// Easy to use trace filter.
#[derive(Binary)]
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

@ -0,0 +1,52 @@
// 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/>.
//! Transaction import result related types
use ipc::binary::{BinaryConvertError, BinaryConvertable};
use std::collections::VecDeque;
use error::{TransactionError, Error};
use std::mem;
use util::Populatable;
#[derive(Debug, Clone, PartialEq)]
/// Represents the result of importing transaction.
pub enum TransactionImportResult {
/// Transaction was imported to current queue.
Current,
/// Transaction was imported to future queue.
Future
}
binary_fixed_size!(TransactionImportResult);
/// Api-level error for transaction import
#[derive(Debug, Clone, Binary)]
pub enum TransactionImportError {
/// Transaction error
Transaction(TransactionError),
/// Other error
Other(String),
}
impl From<Error> for TransactionImportError {
fn from(e: Error) -> Self {
match e {
Error::Transaction(transaction_error) => TransactionImportError::Transaction(transaction_error),
_ => TransactionImportError::Other(format!("other block import error: {:?}", e)),
}
}
}

View File

@ -17,9 +17,12 @@
//! Tree route info type definition //! Tree route info type definition
use util::numbers::H256; use util::numbers::H256;
use ipc::BinaryConvertError;
use std::collections::VecDeque;
use std::mem;
/// Represents a tree route between `from` block and `to` block: /// Represents a tree route between `from` block and `to` block:
#[derive(Debug)] #[derive(Debug, Binary)]
pub struct TreeRoute { pub struct TreeRoute {
/// A vector of hashes of all blocks, ordered from `from` to `to`. /// A vector of hashes of all blocks, ordered from `from` to `to`.
pub blocks: Vec<H256>, pub blocks: Vec<H256>,

View File

@ -139,6 +139,11 @@ impl<'a> BlockView<'a> {
pub fn uncle_at(&self, index: usize) -> Option<Header> { pub fn uncle_at(&self, index: usize) -> Option<Header> {
self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val()) self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val())
} }
/// Return nth uncle rlp.
pub fn uncle_rlp_at(&self, index: usize) -> Option<Bytes> {
self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_raw().to_vec())
}
} }
impl<'a> Hashable for BlockView<'a> { impl<'a> Hashable for BlockView<'a> {

View File

@ -22,5 +22,5 @@ aster = { version = "0.17", default-features = false }
clippy = { version = "^0.*", optional = true } clippy = { version = "^0.*", optional = true }
quasi = { version = "0.11", default-features = false } quasi = { version = "0.11", default-features = false }
quasi_macros = { version = "0.11", optional = true } quasi_macros = { version = "0.11", optional = true }
syntex = { version = "*", optional = true } syntex = { version = "0.33", optional = true }
syntex_syntax = { version = "*", optional = true } syntex_syntax = { version = "0.33", optional = true }

View File

@ -201,7 +201,10 @@ fn implement_dispatch_arm_invoke_stmt(
{ {
let _sp = ext_cx.call_site(); let _sp = ext_cx.call_site();
let mut tt = ::std::vec::Vec::new(); let mut tt = ::std::vec::Vec::new();
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace)));
if dispatch.return_type_ty.is_some() {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc")))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
@ -210,6 +213,8 @@ fn implement_dispatch_arm_invoke_stmt(
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize")))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And)));
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self")))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter()); tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter());
@ -221,12 +226,25 @@ fn implement_dispatch_arm_invoke_stmt(
} }
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
if dispatch.return_type_ty.is_some() {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap")))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
}
else {
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Semi));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Vec"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("new"))));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren)));
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren)));
}
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace)));
tt tt
})).unwrap() })).unwrap()
} }
@ -497,9 +515,9 @@ fn client_generics(builder: &aster::AstBuilder, interface_map: &InterfaceMap) ->
.build() .build()
} }
fn client_qualified_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> { fn client_qualified_ident(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap) -> P<Ty> {
let generics = client_generics(builder, interface_map); let generics = client_generics(builder, interface_map);
aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(builder)) aster::ty::TyBuilder::new().path().segment(interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item))
.with_generics(generics).build() .with_generics(generics).build()
.build() .build()
} }
@ -515,7 +533,7 @@ fn client_phantom_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMa
/// for say `Service` it generates `ServiceClient` /// for say `Service` it generates `ServiceClient`
fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) { fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) {
let generics = client_generics(builder, interface_map); let generics = client_generics(builder, interface_map);
let client_short_ident = interface_map.ident_map.client_ident(builder); let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item);
let phantom = client_phantom_ident(builder, interface_map); let phantom = client_phantom_ident(builder, interface_map);
let client_struct_item = quote_item!(cx, let client_struct_item = quote_item!(cx,
@ -547,9 +565,9 @@ fn push_with_socket_client_implementation(
push: &mut FnMut(Annotatable)) push: &mut FnMut(Annotatable))
{ {
let generics = client_generics(builder, interface_map); let generics = client_generics(builder, interface_map);
let client_ident = client_qualified_ident(builder, interface_map); let client_ident = client_qualified_ident(cx, builder, interface_map);
let where_clause = &generics.where_clause; let where_clause = &generics.where_clause;
let client_short_ident = interface_map.ident_map.client_ident(builder); let client_short_ident = interface_map.ident_map.client_ident(cx, builder, &interface_map.original_item);
let implement = quote_item!(cx, let implement = quote_item!(cx,
impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause { impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause {
@ -578,7 +596,7 @@ fn push_client_implementation(
.collect::<Vec<P<ast::ImplItem>>>(); .collect::<Vec<P<ast::ImplItem>>>();
let generics = client_generics(builder, interface_map); let generics = client_generics(builder, interface_map);
let client_ident = client_qualified_ident(builder, interface_map); let client_ident = client_qualified_ident(cx, builder, interface_map);
let where_clause = &generics.where_clause; let where_clause = &generics.where_clause;
let handshake_item = quote_impl_item!(cx, let handshake_item = quote_impl_item!(cx,
@ -682,6 +700,51 @@ fn implement_handshake_arm(
} }
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<String, ()> {
match lit.node {
ast::LitKind::Str(ref s, _) => Ok(format!("{}", s)),
_ => {
cx.span_err(
lit.span,
&format!("ipc client_ident annotation `{}` must be a string, not `{}`",
name,
::syntax::print::pprust::lit_to_string(lit)));
return Err(());
}
}
}
pub fn get_ipc_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
match attr.node.value.node {
ast::MetaItemKind::List(ref name, ref items) if name == &"ipc" => {
Some(items)
}
_ => None
}
}
fn client_ident_renamed(cx: &ExtCtxt, item: &ast::Item) -> Option<String> {
for meta_items in item.attrs().iter().filter_map(get_ipc_meta_items) {
for meta_item in meta_items {
match meta_item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"client_ident" => {
if let Ok(s) = get_str_from_lit(cx, name, lit) {
return Some(s);
}
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown client_ident container attribute `{}`",
::syntax::print::pprust::meta_item_to_string(meta_item)));
}
}
}
}
None
}
struct InterfaceMap { struct InterfaceMap {
pub original_item: Item, pub original_item: Item,
pub item: P<ast::Item>, pub item: P<ast::Item>,
@ -700,9 +763,14 @@ impl IdentMap {
builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path))) builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path)))
} }
fn client_ident(&self, builder: &aster::AstBuilder) -> Ident { fn client_ident(&self, cx: &ExtCtxt, builder: &aster::AstBuilder, item: &ast::Item) -> Ident {
if let Some(new_name) = client_ident_renamed(cx, item) {
builder.id(new_name)
}
else {
builder.id(format!("{}Client", self.original_path.segments[0].identifier)) builder.id(format!("{}Client", self.original_path.segments[0].identifier))
} }
}
fn qualified_ident(&self, builder: &aster::AstBuilder) -> Ident { fn qualified_ident(&self, builder: &aster::AstBuilder) -> Ident {
builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path).replace("<", "::<"))) builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path).replace("<", "::<")))

View File

@ -50,11 +50,36 @@ include!("lib.rs.in");
#[cfg(feature = "with-syntex")] #[cfg(feature = "with-syntex")]
pub fn register(reg: &mut syntex::Registry) { pub fn register(reg: &mut syntex::Registry) {
use syntax::{ast, fold};
#[cfg(feature = "with-syntex")]
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
struct StripAttributeFolder;
impl fold::Folder for StripAttributeFolder {
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
match attr.node.value.node {
ast::MetaItemKind::List(ref n, _) if n == &"ipc" => { return None; }
_ => {}
}
Some(attr)
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
}
reg.add_attr("feature(custom_derive)"); reg.add_attr("feature(custom_derive)");
reg.add_attr("feature(custom_attribute)"); reg.add_attr("feature(custom_attribute)");
reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation); reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation);
reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation); reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation);
reg.add_post_expansion_pass(strip_attributes);
} }
#[cfg(not(feature = "with-syntex"))] #[cfg(not(feature = "with-syntex"))]
@ -67,4 +92,6 @@ pub fn register(reg: &mut rustc_plugin::Registry) {
syntax::parse::token::intern("derive_Binary"), syntax::parse::token::intern("derive_Binary"),
syntax::ext::base::MultiDecorator( syntax::ext::base::MultiDecorator(
Box::new(serialization::expand_serialization_implementation))); Box::new(serialization::expand_serialization_implementation)));
reg.register_attribute("ipc".to_owned(), AttributeType::Normal);
} }

View File

@ -707,7 +707,12 @@ fn binary_expr_variant(
let buffer = &mut buffer[1..]; let buffer = &mut buffer[1..];
$write_expr $write_expr
}), }),
read: quote_arm!(cx, $variant_index_ident => { $read_expr } ), read: quote_arm!(cx,
$variant_index_ident => {
let buffer = &buffer[1..];
$read_expr
}
),
}) })
}, },
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, _) => {
@ -742,7 +747,12 @@ fn binary_expr_variant(
let buffer = &mut buffer[1..]; let buffer = &mut buffer[1..];
$write_expr $write_expr
}), }),
read: quote_arm!(cx, $variant_index_ident => { $read_expr } ), read: quote_arm!(cx,
$variant_index_ident => {
let buffer = &buffer[1..];
$read_expr
}
),
}) })
}, },
} }

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