merge accountdb migration
This commit is contained in:
commit
36dfa4743e
@ -33,7 +33,7 @@ env:
|
||||
global:
|
||||
# GH_TOKEN
|
||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||
- TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
|
||||
- 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}"
|
||||
- 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"
|
||||
|
127
Cargo.lock
generated
127
Cargo.lock
generated
@ -3,7 +3,7 @@ name = "parity"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
@ -70,7 +70,6 @@ dependencies = [
|
||||
"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_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]]
|
||||
@ -129,15 +128,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.0.77"
|
||||
version = "0.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "clippy_lints"
|
||||
version = "0.0.77"
|
||||
version = "0.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "cookie"
|
||||
version = "0.2.4"
|
||||
@ -225,15 +215,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "eth-secp256k1"
|
||||
version = "0.5.4"
|
||||
source = "git+https://github.com/ethcore/rust-secp256k1#b6fdd43bbcf6d46adb72a92dd1632a0fc834cbf5"
|
||||
source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
|
||||
dependencies = [
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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]]
|
||||
@ -250,13 +238,14 @@ name = "ethcore"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.3.0",
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.0",
|
||||
"ethjson 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)",
|
||||
"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)",
|
||||
"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)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -276,7 +266,7 @@ dependencies = [
|
||||
name = "ethcore-dapps"
|
||||
version = "1.3.0"
|
||||
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-util 1.3.0",
|
||||
"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_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)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
@ -338,10 +329,11 @@ dependencies = [
|
||||
name = "ethcore-rpc"
|
||||
version = "1.3.0"
|
||||
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",
|
||||
"ethcore 1.3.0",
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-util 1.3.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethsync 1.3.0",
|
||||
@ -361,7 +353,7 @@ dependencies = [
|
||||
name = "ethcore-signer"
|
||||
version = "1.3.0"
|
||||
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)",
|
||||
"ethcore-rpc 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)",
|
||||
"bigint 0.1.0",
|
||||
"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)",
|
||||
"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)",
|
||||
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
|
||||
"ethcore-devtools 1.3.0",
|
||||
"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)",
|
||||
"json-tests 0.1.0",
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha3 0.1.0",
|
||||
"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)",
|
||||
"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)",
|
||||
"using_queue 0.1.0",
|
||||
"vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -453,7 +445,7 @@ dependencies = [
|
||||
name = "ethsync"
|
||||
version = "1.3.0"
|
||||
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)",
|
||||
"ethcore 1.3.0",
|
||||
"ethcore-util 1.3.0",
|
||||
@ -506,26 +498,6 @@ name = "httparse"
|
||||
version = "1.1.2"
|
||||
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]]
|
||||
name = "hyper"
|
||||
version = "0.8.1"
|
||||
@ -579,10 +551,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "igd"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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)",
|
||||
"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)",
|
||||
@ -609,14 +581,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "jsonrpc-core"
|
||||
version = "2.0.7"
|
||||
@ -647,11 +611,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "language-tags"
|
||||
version = "0.2.2"
|
||||
@ -685,15 +644,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "mime"
|
||||
version = "0.2.0"
|
||||
@ -898,7 +848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "parity-dapps"
|
||||
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 = [
|
||||
"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)",
|
||||
@ -1154,14 +1104,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "serde"
|
||||
version = "0.7.9"
|
||||
@ -1253,6 +1195,10 @@ dependencies = [
|
||||
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "table"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "target_info"
|
||||
version = "0.1.0"
|
||||
@ -1359,16 +1305,6 @@ name = "unicode-xid"
|
||||
version = "0.0.3"
|
||||
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]]
|
||||
name = "url"
|
||||
version = "0.5.9"
|
||||
@ -1390,20 +1326,15 @@ dependencies = [
|
||||
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "using_queue"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
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]]
|
||||
name = "uuid"
|
||||
version = "0.2.1"
|
||||
|
@ -22,7 +22,7 @@ fdlimit = { path = "util/fdlimit" }
|
||||
num_cpus = "0.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "0.2.1"
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
clippy = { version = "0.0.78", optional = true}
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
ethsync = { path = "sync" }
|
||||
|
12
README.md
12
README.md
@ -1,7 +1,11 @@
|
||||
# [Parity](https://ethcore.io/parity.html)
|
||||
### Fast, light, and robust Ethereum implementation
|
||||
|
||||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/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-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
|
||||
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
|
||||
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html)
|
||||
[doc-url]: 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.
|
||||
|
||||
If you run into an issue while using parity, feel free to file one in this repository
|
||||
or hop on our [gitter chat room]([gitter-url]) to ask a question. We are glad to help!
|
||||
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
||||
|
||||
Parity's current release is 1.2. You can download it at https://ethcore.io/parity.html or follow the instructions
|
||||
below to build from source.
|
||||
|
@ -13,6 +13,7 @@ log = "0.3"
|
||||
jsonrpc-core = "2.0"
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
unicase = "1.3"
|
||||
url = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
serde = "0.7.0"
|
||||
@ -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-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true }
|
||||
mime_guess = { version = "1.6.1" }
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
clippy = { version = "0.0.78", optional = true}
|
||||
|
||||
[build-dependencies]
|
||||
serde_codegen = { version = "0.7.0", optional = true }
|
||||
|
@ -25,8 +25,8 @@ mod inner {
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
let src = Path::new("./src/api/mod.rs.in");
|
||||
let dst = Path::new(&out_dir).join("mod.rs");
|
||||
let src = Path::new("./src/api/types.rs.in");
|
||||
let dst = Path::new(&out_dir).join("types.rs");
|
||||
|
||||
let mut registry = syntex::Registry::new();
|
||||
|
||||
|
@ -15,42 +15,23 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
|
||||
|
||||
use api::response::as_json;
|
||||
use hyper::{server, net, Decoder, Encoder, Next};
|
||||
use api::types::{App, ApiError};
|
||||
use api::response::{as_json, as_json_error, ping_response};
|
||||
use handlers::extract_url;
|
||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RestApi {
|
||||
local_domain: String,
|
||||
endpoints: Arc<Endpoints>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct App {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub version: String,
|
||||
pub author: String,
|
||||
#[serde(rename="iconUrl")]
|
||||
pub icon_url: String,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn from_info(id: &str, info: &EndpointInfo) -> Self {
|
||||
App {
|
||||
id: id.to_owned(),
|
||||
name: info.name.to_owned(),
|
||||
description: info.description.to_owned(),
|
||||
version: info.version.to_owned(),
|
||||
author: info.author.to_owned(),
|
||||
icon_url: info.icon_url.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RestApi {
|
||||
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
||||
pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
||||
Box::new(RestApi {
|
||||
endpoints: endpoints
|
||||
local_domain: local_domain,
|
||||
endpoints: endpoints,
|
||||
})
|
||||
}
|
||||
|
||||
@ -63,7 +44,58 @@ impl RestApi {
|
||||
|
||||
impl Endpoint for RestApi {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,13 +16,12 @@
|
||||
|
||||
//! REST API
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
|
||||
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
|
||||
|
||||
#[cfg(feature = "serde_macros")]
|
||||
include!("mod.rs.in");
|
||||
|
||||
#[cfg(not(feature = "serde_macros"))]
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||
mod api;
|
||||
mod response;
|
||||
mod types;
|
||||
|
||||
pub use self::api::RestApi;
|
||||
pub use self::types::App;
|
||||
|
@ -16,8 +16,21 @@
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
use endpoint::{ContentHandler, Handler};
|
||||
use endpoint::Handler;
|
||||
use handlers::{ContentHandler, EchoHandler};
|
||||
|
||||
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")),
|
||||
]))
|
||||
}
|
||||
|
@ -14,11 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rustc_serialize::hex::FromHex;
|
||||
#[cfg(feature = "serde_macros")]
|
||||
include!("types.rs.in");
|
||||
|
||||
pub fn hex_or_string(s: &str) -> Vec<u8> {
|
||||
match s.starts_with("0x") {
|
||||
true => s[2..].from_hex().unwrap(),
|
||||
false => From::from(s)
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "serde_macros"))]
|
||||
include!(concat!(env!("OUT_DIR"), "/types.rs"));
|
50
dapps/src/api/types.rs.in
Normal file
50
dapps/src/api/types.rs.in
Normal 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,
|
||||
}
|
||||
|
@ -16,11 +16,7 @@
|
||||
|
||||
//! URL Endpoint traits
|
||||
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
|
||||
use std::io::Write;
|
||||
use hyper::{server, net};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
@ -42,58 +38,8 @@ pub struct EndpointInfo {
|
||||
pub trait Endpoint : Send + Sync {
|
||||
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 Handler = server::Handler<HttpStream> + Send;
|
||||
|
||||
pub struct ContentHandler {
|
||||
content: String,
|
||||
mimetype: String,
|
||||
write_pos: usize,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn new(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for ContentHandler {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(StatusCode::Ok);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let bytes = self.content.as_bytes();
|
||||
if self.write_pos == bytes.len() {
|
||||
return Next::end();
|
||||
}
|
||||
|
||||
match encoder.write(&bytes[self.write_pos..]) {
|
||||
Ok(bytes) => {
|
||||
self.write_pos += bytes;
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type Handler = server::Handler<net::HttpStream> + Send;
|
||||
|
44
dapps/src/handlers/auth.rs
Normal file
44
dapps/src/handlers/auth.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
92
dapps/src/handlers/content.rs
Normal file
92
dapps/src/handlers/content.rs
Normal 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
148
dapps/src/handlers/echo.rs
Normal 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
57
dapps/src/handlers/mod.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,9 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate url;
|
||||
extern crate url as url_lib;
|
||||
extern crate hyper;
|
||||
extern crate unicase;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate jsonrpc_core;
|
||||
@ -59,9 +60,11 @@ mod endpoint;
|
||||
mod apps;
|
||||
mod page;
|
||||
mod router;
|
||||
mod handlers;
|
||||
mod rpc;
|
||||
mod api;
|
||||
mod proxypac;
|
||||
mod url;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::net::SocketAddr;
|
||||
@ -120,7 +123,7 @@ impl Server {
|
||||
let special = Arc::new({
|
||||
let mut special = HashMap::new();
|
||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
||||
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone()));
|
||||
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone()));
|
||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||
special
|
||||
});
|
||||
|
@ -16,7 +16,8 @@
|
||||
|
||||
//! Serving ProxyPac file
|
||||
|
||||
use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath};
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::ContentHandler;
|
||||
use apps::DAPPS_DOMAIN;
|
||||
|
||||
pub struct ProxyPac;
|
||||
@ -41,7 +42,7 @@ function FindProxyForURL(url, host) {{
|
||||
}}
|
||||
"#,
|
||||
DAPPS_DOMAIN, path.host, path.port);
|
||||
Box::new(ContentHandler::new(content, "application/javascript".to_owned()))
|
||||
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,24 +16,23 @@
|
||||
|
||||
//! HTTP Authorization implementations
|
||||
|
||||
use std::io::Write;
|
||||
use std::collections::HashMap;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{server, net, header, status};
|
||||
use endpoint::Handler;
|
||||
use handlers::{AuthRequiredHandler, ContentHandler};
|
||||
|
||||
/// Authorization result
|
||||
pub enum Authorized {
|
||||
/// Authorization was successful.
|
||||
Yes,
|
||||
/// Unsuccessful authorization. Handler for further work is returned.
|
||||
No(Box<server::Handler<HttpStream> + Send>),
|
||||
No(Box<Handler>),
|
||||
}
|
||||
|
||||
/// Authorization interface
|
||||
pub trait Authorization : Send + Sync {
|
||||
/// 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
|
||||
@ -45,18 +44,22 @@ pub struct HttpBasicAuth {
|
||||
pub struct 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
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
match auth {
|
||||
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 => {
|
||||
Authorized::No(Box::new(AuthRequiredHandler))
|
||||
@ -89,7 +92,7 @@ impl HttpBasicAuth {
|
||||
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>>() {
|
||||
Some(&header::Authorization(
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -17,23 +17,18 @@
|
||||
//! Router implementation
|
||||
//! Processes request handling authorization and dispatching it to proper application.
|
||||
|
||||
mod url;
|
||||
mod redirect;
|
||||
pub mod auth;
|
||||
|
||||
use DAPPS_DOMAIN;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use url::Host;
|
||||
use hyper;
|
||||
use hyper::{server, uri, header};
|
||||
use hyper::{Next, Encoder, Decoder};
|
||||
use url::{Url, Host};
|
||||
use hyper::{self, server, Next, Encoder, Decoder};
|
||||
use hyper::net::HttpStream;
|
||||
use apps;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||
use self::url::Url;
|
||||
use handlers::{Redirection, extract_url};
|
||||
use self::auth::{Authorization, Authorized};
|
||||
use self::redirect::Redirection;
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
#[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 special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||
if url.path.len() <= 1 {
|
||||
|
@ -16,14 +16,14 @@
|
||||
|
||||
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
||||
|
||||
use url::Host;
|
||||
use url::{self};
|
||||
use url_lib::{self};
|
||||
pub use url_lib::Host;
|
||||
|
||||
/// HTTP/HTTPS URL type for Iron.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Url {
|
||||
/// Raw url of url
|
||||
pub raw: url::Url,
|
||||
pub raw: url_lib::Url,
|
||||
|
||||
/// The host field of the URL, probably a domain.
|
||||
pub host: Host,
|
||||
@ -62,14 +62,14 @@ impl Url {
|
||||
/// See: http://url.spec.whatwg.org/#special-scheme
|
||||
pub fn parse(input: &str) -> Result<Url, String> {
|
||||
// Parse the string using rust-url, then convert.
|
||||
match url::Url::parse(input) {
|
||||
match url_lib::Url::parse(input) {
|
||||
Ok(raw_url) => Url::from_generic_url(raw_url),
|
||||
Err(e) => Err(format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Url` from a `rust-url` `Url`.
|
||||
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
|
||||
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
|
||||
// Map empty usernames to None.
|
||||
let username = match raw_url.username() {
|
||||
"" => None,
|
@ -12,7 +12,7 @@ syntex = "*"
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
clippy = { version = "0.0.78", optional = true}
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
||||
|
@ -22,7 +22,7 @@ ethcore-util = { path = "../util" }
|
||||
evmjit = { path = "../evmjit", optional = true }
|
||||
ethash = { path = "../ethash" }
|
||||
num_cpus = "0.2"
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
clippy = { version = "0.0.78", optional = true}
|
||||
crossbeam = "0.2.9"
|
||||
lazy_static = "0.2"
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
@ -31,6 +31,8 @@ bloomchain = "0.1"
|
||||
"ethcore-ipc" = { path = "../ipc/rpc" }
|
||||
rayon = "0.3.1"
|
||||
ethstore = { path = "../ethstore" }
|
||||
semver = "0.2"
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/ethcore/hyper"
|
||||
@ -43,3 +45,4 @@ json-tests = []
|
||||
test-heavy = []
|
||||
dev = ["clippy"]
|
||||
default = []
|
||||
benches = []
|
||||
|
@ -30,4 +30,18 @@ fn main() {
|
||||
codegen::register(&mut registry);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": true
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"daoRescueSoftFork": true
|
||||
"frontierCompatibilityModeLimit": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0x789b0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -127,7 +127,7 @@ impl Account {
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
using it will not fail.");
|
||||
|
||||
(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()
|
||||
}
|
||||
|
||||
@ -214,8 +214,8 @@ impl Account {
|
||||
}
|
||||
|
||||
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
|
||||
pub fn commit_storage(&mut self, db: &mut AccountDBMut) {
|
||||
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root)
|
||||
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
|
||||
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
|
||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
using it will not fail.");
|
||||
@ -275,7 +275,7 @@ mod tests {
|
||||
let rlp = {
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
a.init_code(vec![]);
|
||||
a.commit_code(&mut db);
|
||||
a.rlp()
|
||||
@ -313,7 +313,7 @@ mod tests {
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(0.into(), 0x1234.into());
|
||||
assert_eq!(a.storage_root(), None);
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
|
||||
}
|
||||
|
||||
@ -323,11 +323,11 @@ mod tests {
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(0.into(), 0x1234.into());
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
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.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
use util::*;
|
||||
|
||||
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
|
||||
|
||||
// combines a key with an address hash to ensure uniqueness.
|
||||
// leaves the first 96 bits untouched in order to support partial key lookup.
|
||||
#[inline]
|
||||
fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
|
||||
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?
|
||||
|
@ -203,7 +203,6 @@ mod tests {
|
||||
timestamp: 0,
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
dao_rescue_block_gas_limit: None,
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
});
|
||||
@ -254,7 +253,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.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 seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||
assert!(b.try_seal(engine.deref(), seal).is_ok());
|
||||
|
@ -183,7 +183,6 @@ pub struct OpenBlock<'x> {
|
||||
engine: &'x Engine,
|
||||
vm_factory: &'x EvmFactory,
|
||||
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,
|
||||
@ -195,7 +194,6 @@ pub struct ClosedBlock {
|
||||
block: ExecutedBlock,
|
||||
uncle_bytes: Bytes,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
unclosed_state: State,
|
||||
}
|
||||
|
||||
@ -222,22 +220,21 @@ impl<'x> OpenBlock<'x> {
|
||||
pub fn new(
|
||||
engine: &'x Engine,
|
||||
vm_factory: &'x EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
author: Address,
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
) -> Result<Self, Error> {
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()));
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory));
|
||||
let mut r = OpenBlock {
|
||||
block: ExecutedBlock::new(state, tracing),
|
||||
engine: engine,
|
||||
vm_factory: vm_factory,
|
||||
last_hashes: last_hashes,
|
||||
dao_rescue_block_gas_limit: dao_rescue_block_gas_limit,
|
||||
};
|
||||
|
||||
r.block.base.header.parent_hash = parent.hash();
|
||||
@ -294,7 +291,6 @@ impl<'x> OpenBlock<'x> {
|
||||
/// Get the environment info concerning this block.
|
||||
pub fn env_info(&self) -> EnvInfo {
|
||||
// TODO: memoise.
|
||||
const SOFT_FORK_BLOCK: u64 = 1_800_000;
|
||||
EnvInfo {
|
||||
number: self.block.base.header.number,
|
||||
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.
|
||||
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
|
||||
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,
|
||||
uncle_bytes: uncle_bytes,
|
||||
last_hashes: s.last_hashes,
|
||||
dao_rescue_block_gas_limit: s.dao_rescue_block_gas_limit,
|
||||
unclosed_state: unclosed_state,
|
||||
}
|
||||
}
|
||||
@ -410,7 +404,6 @@ impl ClosedBlock {
|
||||
engine: engine,
|
||||
vm_factory: vm_factory,
|
||||
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>,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
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_gas_limit(*header.gas_limit());
|
||||
b.set_timestamp(header.timestamp());
|
||||
@ -508,12 +501,12 @@ pub fn enact_bytes(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header();
|
||||
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, 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
|
||||
@ -525,11 +518,11 @@ pub fn enact_verified(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
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
|
||||
@ -541,11 +534,11 @@ pub fn enact_and_seal(
|
||||
db: Box<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<SealedBlock, Error> {
|
||||
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)]
|
||||
@ -565,7 +558,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.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.seal(engine.deref(), vec![]);
|
||||
}
|
||||
@ -581,7 +574,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
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();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
@ -589,7 +582,7 @@ mod tests {
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
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);
|
||||
|
||||
@ -609,7 +602,7 @@ mod tests {
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
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();
|
||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
||||
let mut uncle2_header = Header::new();
|
||||
@ -624,7 +617,7 @@ mod tests {
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
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();
|
||||
assert_eq!(bytes, orig_bytes);
|
||||
|
@ -28,6 +28,8 @@ use service::*;
|
||||
use client::BlockStatus;
|
||||
use util::panics::*;
|
||||
|
||||
pub use types::block_queue_info::BlockQueueInfo;
|
||||
|
||||
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
|
||||
|
||||
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 {
|
||||
/// The total size of the queues.
|
||||
|
@ -17,8 +17,6 @@
|
||||
use util::numbers::{U256,H256};
|
||||
use header::BlockNumber;
|
||||
|
||||
use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap};
|
||||
|
||||
/// Brief info about inserted block.
|
||||
#[derive(Clone)]
|
||||
pub struct BlockInfo {
|
||||
@ -54,43 +52,3 @@ pub struct BranchBecomingCanonChainData {
|
||||
/// Hashes of the blocks which were invalidated.
|
||||
pub retracted: Vec<H256>,
|
||||
}
|
||||
|
||||
impl FromRawBytesVariable for BranchBecomingCanonChainData {
|
||||
fn from_bytes_variable(bytes: &[u8]) -> Result<BranchBecomingCanonChainData, FromBytesError> {
|
||||
type Tuple = (Vec<H256>, Vec<H256>, H256);
|
||||
let (enacted, retracted, ancestor) = try!(Tuple::from_bytes_variable(bytes));
|
||||
Ok(BranchBecomingCanonChainData { ancestor: ancestor, enacted: enacted, retracted: retracted })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawBytesVariable for BlockLocation {
|
||||
fn from_bytes_variable(bytes: &[u8]) -> Result<BlockLocation, FromBytesError> {
|
||||
match bytes[0] {
|
||||
0 => Ok(BlockLocation::CanonChain),
|
||||
1 => Ok(BlockLocation::Branch),
|
||||
2 => Ok(BlockLocation::BranchBecomingCanonChain(
|
||||
try!(BranchBecomingCanonChainData::from_bytes_variable(&bytes[1..bytes.len()])))),
|
||||
_ => Err(FromBytesError::UnknownMarker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytesWithMap for BranchBecomingCanonChainData {
|
||||
fn to_bytes_map(&self) -> Vec<u8> {
|
||||
(&self.enacted, &self.retracted, &self.ancestor).to_bytes_map()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytesWithMap for BlockLocation {
|
||||
fn to_bytes_map(&self) -> Vec<u8> {
|
||||
match *self {
|
||||
BlockLocation::CanonChain => vec![0u8],
|
||||
BlockLocation::Branch => vec![1u8],
|
||||
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
||||
let mut bytes = (&data.enacted, &data.retracted, &data.ancestor).to_bytes_map();
|
||||
bytes.insert(0, 2u8);
|
||||
bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ use blockchain::best_block::BestBlock;
|
||||
use types::tree_route::TreeRoute;
|
||||
use blockchain::update::ExtrasUpdate;
|
||||
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_ELEMENTS_PER_INDEX: usize = 16;
|
||||
@ -295,7 +295,22 @@ impl BlockChain {
|
||||
|
||||
// load best block
|
||||
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 => {
|
||||
// best block does not exist
|
||||
// we need to insert genesis into the cache
|
||||
|
@ -14,16 +14,34 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// 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};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||
use util::*;
|
||||
// util
|
||||
use util::numbers::*;
|
||||
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 error::{Error, ImportError, ExecutionError, BlockError, ImportResult};
|
||||
use header::{BlockNumber, Header};
|
||||
use error::{ImportError, ExecutionError, BlockError, ImportResult};
|
||||
use header::BlockNumber;
|
||||
use state::State;
|
||||
use spec::Spec;
|
||||
use engine::Engine;
|
||||
@ -35,24 +53,32 @@ use verification::{PreverifiedBlock, Verifier};
|
||||
use block::*;
|
||||
use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
||||
use blockchain::extras::TransactionAddress;
|
||||
use filter::Filter;
|
||||
use types::filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||
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 env_info::EnvInfo;
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
use receipt::LocalizedReceipt;
|
||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||
use trace;
|
||||
use 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::block_status::BlockStatus;
|
||||
use evm::Factory as EvmFactory;
|
||||
use miner::{Miner, MinerService, TransactionImportResult, AccountDetails};
|
||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||
|
||||
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
||||
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
||||
|
||||
impl fmt::Display for BlockChainInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@ -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.
|
||||
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||
pub struct Client {
|
||||
mode: Mode,
|
||||
chain: Arc<BlockChain>,
|
||||
tracedb: Arc<TraceDB<BlockChain>>,
|
||||
engine: Arc<Box<Engine>>,
|
||||
@ -95,9 +136,13 @@ pub struct Client {
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
verifier: Box<Verifier>,
|
||||
vm_factory: Arc<EvmFactory>,
|
||||
trie_factory: TrieFactory,
|
||||
miner: Arc<Miner>,
|
||||
sleep_state: Mutex<SleepState>,
|
||||
liveness: AtomicBool,
|
||||
io_channel: IoChannel<NetSyncMessage>,
|
||||
queue_transactions: AtomicUsize,
|
||||
previous_enode: Mutex<Option<String>>,
|
||||
}
|
||||
|
||||
const HISTORY: u64 = 1200;
|
||||
@ -132,9 +177,8 @@ impl Client {
|
||||
spec: Spec,
|
||||
path: &Path,
|
||||
miner: Arc<Miner>,
|
||||
message_channel: IoChannel<NetSyncMessage>)
|
||||
-> Result<Arc<Client>, ClientError>
|
||||
{
|
||||
message_channel: IoChannel<NetSyncMessage>
|
||||
) -> Result<Arc<Client>, ClientError> {
|
||||
let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
|
||||
let gb = spec.genesis_block();
|
||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
|
||||
@ -165,7 +209,11 @@ impl Client {
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
panic_handler.forward_from(&block_queue);
|
||||
|
||||
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
|
||||
let client = Client {
|
||||
sleep_state: Mutex::new(SleepState::new(awake)),
|
||||
liveness: AtomicBool::new(awake),
|
||||
mode: config.mode,
|
||||
chain: chain,
|
||||
tracedb: tracedb,
|
||||
engine: engine,
|
||||
@ -176,11 +224,12 @@ impl Client {
|
||||
panic_handler: panic_handler,
|
||||
verifier: verification::new(config.verifier_type),
|
||||
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
|
||||
trie_factory: TrieFactory::new(config.trie_spec),
|
||||
miner: miner,
|
||||
io_channel: message_channel,
|
||||
queue_transactions: AtomicUsize::new(0),
|
||||
previous_enode: Mutex::new(None),
|
||||
};
|
||||
|
||||
Ok(Arc::new(client))
|
||||
}
|
||||
|
||||
@ -234,7 +283,7 @@ impl Client {
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash.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 {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
@ -349,7 +398,7 @@ impl Client {
|
||||
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();
|
||||
// Are we committing an era?
|
||||
let ancient = if number >= HISTORY {
|
||||
@ -385,12 +434,8 @@ impl Client {
|
||||
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
|
||||
let _timer = PerfTimer::new("import_queued_transactions");
|
||||
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
|
||||
let fetch_account = |a: &Address| AccountDetails {
|
||||
nonce: self.latest_nonce(a),
|
||||
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);
|
||||
let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
|
||||
let results = self.miner.import_external_transactions(self, txs);
|
||||
results.len()
|
||||
}
|
||||
|
||||
@ -419,14 +464,18 @@ impl Client {
|
||||
|
||||
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.
|
||||
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())
|
||||
.expect("State root of best block header always valid.")
|
||||
State::from_existing(
|
||||
self.state_db.lock().unwrap().boxed_clone(),
|
||||
HeaderView::new(&self.best_block_header()).state_root(),
|
||||
self.engine.account_start_nonce(),
|
||||
self.trie_factory.clone())
|
||||
.expect("State root of best block header always valid.")
|
||||
}
|
||||
|
||||
/// Get info on the cache.
|
||||
@ -442,9 +491,41 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Tick the client.
|
||||
// TODO: manage by real events.
|
||||
pub fn tick(&self) {
|
||||
self.chain.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.
|
||||
@ -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 {
|
||||
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
|
||||
let header = self.block_header(BlockID::Latest).unwrap();
|
||||
@ -495,7 +613,6 @@ impl BlockChainClient for Client {
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
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.
|
||||
let mut state = self.state();
|
||||
@ -521,6 +638,12 @@ impl BlockChainClient for Client {
|
||||
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> {
|
||||
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))
|
||||
}
|
||||
|
||||
fn uncle(&self, id: UncleID) -> Option<Header> {
|
||||
let index = id.1;
|
||||
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index))
|
||||
fn uncle(&self, id: UncleID) -> Option<Bytes> {
|
||||
let index = id.position;
|
||||
self.block(id.block).and_then(|block| BlockView::new(&block).uncle_rlp_at(index))
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
fn import_block(&self, bytes: Bytes) -> ImportResult {
|
||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
{
|
||||
let header = BlockView::new(&bytes).header_view();
|
||||
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 {
|
||||
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 {
|
||||
@ -713,8 +836,8 @@ impl BlockChainClient for Client {
|
||||
receipt.logs.into_iter()
|
||||
.enumerate()
|
||||
.filter(|tuple| filter.matches(&tuple.1))
|
||||
.map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
.map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
block_hash: hash.clone(),
|
||||
block_number: number,
|
||||
transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new),
|
||||
@ -724,7 +847,6 @@ impl BlockChainClient for Client {
|
||||
.collect::<Vec<LocalizedLogEntry>>()
|
||||
})
|
||||
.collect::<Vec<LocalizedLogEntry>>()
|
||||
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -773,14 +895,6 @@ impl BlockChainClient for Client {
|
||||
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>) {
|
||||
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
|
||||
debug!("Ignoring {} transactions: queue is full", transactions.len());
|
||||
@ -800,44 +914,6 @@ impl BlockChainClient for Client {
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
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 {
|
||||
@ -848,11 +924,11 @@ impl MiningBlockChainClient for Client {
|
||||
let mut open_block = OpenBlock::new(
|
||||
engine,
|
||||
&self.vm_factory,
|
||||
self.trie_factory.clone(),
|
||||
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
|
||||
self.state_db.lock().unwrap().boxed_clone(),
|
||||
&self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
|
||||
self.build_last_hashes(h.clone()),
|
||||
self.dao_rescue_block_gas_limit(h.clone()),
|
||||
author,
|
||||
gas_range_target,
|
||||
extra_data,
|
||||
@ -914,3 +990,5 @@ impl MayPanic for Client {
|
||||
self.panic_handler.on_panic(closure);
|
||||
}
|
||||
}
|
||||
|
||||
impl IpcConfig for Client { }
|
||||
|
@ -14,12 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use std::time::Duration;
|
||||
pub use block_queue::BlockQueueConfig;
|
||||
pub use blockchain::Config as BlockChainConfig;
|
||||
pub use trace::{Config as TraceConfig, Switch};
|
||||
pub use evm::VMType;
|
||||
pub use verification::VerifierType;
|
||||
use util::journaldb;
|
||||
use util::trie::TrieSpec;
|
||||
|
||||
/// Client state db compaction profile
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -34,6 +36,23 @@ impl Default for DatabaseCompactionProfile {
|
||||
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.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ClientConfig {
|
||||
@ -45,6 +64,8 @@ pub struct ClientConfig {
|
||||
pub tracing: TraceConfig,
|
||||
/// VM type.
|
||||
pub vm_type: VMType,
|
||||
/// Trie type.
|
||||
pub trie_spec: TrieSpec,
|
||||
/// The JournalDB ("pruning") algorithm to use.
|
||||
pub pruning: journaldb::Algorithm,
|
||||
/// The name of the client instance.
|
||||
@ -53,6 +74,8 @@ pub struct ClientConfig {
|
||||
pub db_cache_size: Option<usize>,
|
||||
/// State db compaction profile
|
||||
pub db_compaction: DatabaseCompactionProfile,
|
||||
/// Operating mode
|
||||
pub mode: Mode,
|
||||
/// Type of block verifier used by client.
|
||||
pub verifier_type: VerifierType,
|
||||
}
|
||||
|
@ -16,22 +16,20 @@
|
||||
|
||||
//! Blockchain database client.
|
||||
|
||||
mod client;
|
||||
mod config;
|
||||
mod error;
|
||||
mod test_client;
|
||||
mod trace;
|
||||
|
||||
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 types::ids::*;
|
||||
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 env_info::{LastHashes, EnvInfo};
|
||||
|
||||
use std::path::Path;
|
||||
use util::bytes::Bytes;
|
||||
use util::hash::{Address, H256, H2048};
|
||||
use util::numbers::U256;
|
||||
@ -39,31 +37,34 @@ use util::Itertools;
|
||||
use blockchain::TreeRoute;
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use header::{BlockNumber, Header};
|
||||
use header::{BlockNumber};
|
||||
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use filter::Filter;
|
||||
use views::{HeaderView, BlockView};
|
||||
use views::{BlockView};
|
||||
use error::{ImportResult, ExecutionError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
use evm::Factory as EvmFactory;
|
||||
use miner::{TransactionImportResult};
|
||||
use error::Error as EthError;
|
||||
pub use types::call_analytics::CallAnalytics;
|
||||
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.
|
||||
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
|
||||
pub struct CallAnalytics {
|
||||
/// Make a transaction trace.
|
||||
pub transaction_tracing: bool,
|
||||
/// Make a VM trace.
|
||||
pub vm_tracing: bool,
|
||||
/// Make a diff.
|
||||
pub state_diffing: bool,
|
||||
pub mod client {
|
||||
//! Blockchain database client.
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/client.ipc.rs"));
|
||||
}
|
||||
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
pub trait BlockChainClient : Sync + Send {
|
||||
|
||||
/// Should be called by any external-facing interface when actively using the client.
|
||||
/// To minimise chatter, there's no need to call more than once every 30s.
|
||||
fn keep_alive(&self) {}
|
||||
|
||||
/// Get raw block header data by block id.
|
||||
fn block_header(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
@ -127,7 +128,7 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
|
||||
|
||||
/// 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.
|
||||
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>;
|
||||
|
||||
/// 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.
|
||||
fn queue_info(&self) -> BlockQueueInfo;
|
||||
@ -188,16 +189,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Get last hashes starting from best block.
|
||||
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.
|
||||
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
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
@ -226,28 +220,6 @@ pub trait BlockChainClient : Sync + Send {
|
||||
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
|
||||
|
@ -20,7 +20,9 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
|
||||
use util::*;
|
||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
||||
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 filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
@ -34,12 +36,9 @@ use spec::Spec;
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use executive::Executed;
|
||||
use error::{ExecutionError};
|
||||
use error::ExecutionError;
|
||||
use trace::LocalizedTrace;
|
||||
|
||||
use miner::{TransactionImportResult, AccountDetails};
|
||||
use error::Error as EthError;
|
||||
|
||||
/// Test client.
|
||||
pub struct TestBlockChainClient {
|
||||
/// 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> {
|
||||
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> {
|
||||
if let BlockID::Latest = id {
|
||||
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!();
|
||||
}
|
||||
|
||||
fn uncle(&self, _id: UncleID) -> Option<BlockHeader> {
|
||||
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@ -402,7 +409,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
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 h = header.hash();
|
||||
let number: usize = header.number as usize;
|
||||
@ -487,25 +494,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
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>) {
|
||||
// import right here
|
||||
let tx = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
|
||||
self.import_transactions(tx);
|
||||
}
|
||||
|
||||
fn take_snapshot(&self, _root_dir: &Path) {
|
||||
unimplemented!()
|
||||
let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
|
||||
self.miner.import_external_transactions(self, txs);
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
|
@ -1,13 +1,12 @@
|
||||
|
||||
//! Bridge between Tracedb and Blockchain.
|
||||
|
||||
use std::ops::Range;
|
||||
use util::{Address, H256};
|
||||
use util::{H256};
|
||||
use header::BlockNumber;
|
||||
use trace::DatabaseExtras as TraceDatabaseExtras;
|
||||
use blockchain::{BlockChain, BlockProvider};
|
||||
use blockchain::extras::TransactionAddress;
|
||||
use super::BlockID;
|
||||
pub use types::trace_filter::Filter;
|
||||
|
||||
impl TraceDatabaseExtras for BlockChain {
|
||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
||||
@ -26,13 +25,3 @@ impl TraceDatabaseExtras for BlockChain {
|
||||
.map(|tx| tx.hash())
|
||||
}
|
||||
}
|
||||
|
||||
/// Easy to use trace filter.
|
||||
pub struct Filter {
|
||||
/// Range of filtering.
|
||||
pub range: Range<BlockID>,
|
||||
/// From address.
|
||||
pub from_address: Vec<Address>,
|
||||
/// To address.
|
||||
pub to_address: Vec<Address>,
|
||||
}
|
||||
|
@ -39,9 +39,6 @@ pub struct EnvInfo {
|
||||
pub last_hashes: LastHashes,
|
||||
/// The gas used.
|
||||
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 {
|
||||
@ -54,7 +51,6 @@ impl Default for EnvInfo {
|
||||
gas_limit: 0.into(),
|
||||
last_hashes: vec![],
|
||||
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(),
|
||||
last_hashes: (1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect(),
|
||||
gas_used: U256::zero(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,10 +20,11 @@ use util::*;
|
||||
use header::BlockNumber;
|
||||
use basic_types::LogBloom;
|
||||
use client::Error as ClientError;
|
||||
|
||||
use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
||||
use types::block_import_error::BlockImportError;
|
||||
pub use types::executed::ExecutionError;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Errors concerning transaction processing.
|
||||
pub enum TransactionError {
|
||||
/// 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.
|
||||
/*#![feature(concat_idents)]
|
||||
macro_rules! assimilate {
|
||||
|
@ -39,8 +39,6 @@ pub struct EthashParams {
|
||||
pub registrar: Address,
|
||||
/// Homestead transition block number.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
/// Enable the soft-fork logic.
|
||||
pub dao_rescue_soft_fork: bool,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
@ -53,7 +51,6 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.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 {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
let mut s = 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
|
||||
Schedule::new_homestead()
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,7 +318,7 @@ mod tests {
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.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();
|
||||
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());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
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 uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
|
||||
uncle.author = uncle_author.clone();
|
||||
@ -369,7 +362,6 @@ mod tests {
|
||||
last_hashes: vec![],
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
});
|
||||
|
||||
assert!(schedule.stack_limit > 0);
|
||||
@ -382,7 +374,6 @@ mod tests {
|
||||
last_hashes: vec![],
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
});
|
||||
|
||||
assert!(!schedule.have_delegate_call);
|
||||
|
@ -33,11 +33,8 @@ use super::spec::*;
|
||||
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||
|
||||
/// Create a new Frontier mainnet chain spec.
|
||||
pub fn new_frontier(dao_rescue: bool) -> Spec {
|
||||
Spec::load(match dao_rescue {
|
||||
true => include_bytes!("../../res/ethereum/frontier_dao_rescue.json"),
|
||||
false => include_bytes!("../../res/ethereum/frontier.json"),
|
||||
})
|
||||
pub fn new_frontier() -> Spec {
|
||||
Spec::load(include_bytes!("../../res/ethereum/frontier.json"))
|
||||
}
|
||||
|
||||
/// 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 = db_result.take();
|
||||
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("0000000000000000000000000000000000000002")), U256::from(1u64));
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64));
|
||||
@ -89,7 +86,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn frontier() {
|
||||
let frontier = new_frontier(true);
|
||||
let frontier = new_frontier();
|
||||
|
||||
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
|
||||
let genesis = frontier.genesis_block();
|
||||
|
126
ethcore/src/evm/benches/mod.rs
Normal file
126
ethcore/src/evm/benches/mod.rs
Normal 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(),
|
||||
}
|
||||
}
|
@ -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
|
||||
pub trait Evm {
|
||||
/// This function should be used to execute transaction.
|
||||
|
@ -19,6 +19,7 @@
|
||||
//! TODO: consider spliting it into two separate files.
|
||||
use std::fmt;
|
||||
use evm::Evm;
|
||||
use util::{U256, Uint};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Type of EVM to use.
|
||||
@ -85,24 +86,30 @@ pub struct Factory {
|
||||
|
||||
impl Factory {
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
#[cfg(feature = "jit")]
|
||||
pub fn create(&self) -> Box<Evm> {
|
||||
pub fn create(&self, gas: U256) -> Box<Evm> {
|
||||
match self.evm {
|
||||
VMType::Jit => {
|
||||
Box::new(super::jit::JitEvm::default())
|
||||
},
|
||||
VMType::Interpreter => {
|
||||
Box::new(super::interpreter::Interpreter::default())
|
||||
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::default())
|
||||
} else {
|
||||
Box::new(super::interpreter::Interpreter::<U256>::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
#[cfg(not(feature = "jit"))]
|
||||
pub fn create(&self) -> Box<Evm> {
|
||||
pub fn create(&self, gas: U256) -> Box<Evm> {
|
||||
match self.evm {
|
||||
VMType::Interpreter => {
|
||||
Box::new(super::interpreter::Interpreter::default())
|
||||
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::default())
|
||||
} else {
|
||||
Box::new(super::interpreter::Interpreter::<U256>::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,6 +120,10 @@ impl Factory {
|
||||
evm: evm
|
||||
}
|
||||
}
|
||||
|
||||
fn can_fit_in_usize(gas: U256) -> bool {
|
||||
gas == U256::from(gas.low_u64() as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Factory {
|
||||
@ -135,7 +146,7 @@ impl Default for Factory {
|
||||
|
||||
#[test]
|
||||
fn test_create_vm() {
|
||||
let _vm = Factory::default().create();
|
||||
let _vm = Factory::default().create(U256::zero());
|
||||
}
|
||||
|
||||
/// Create tests by injecting different VM factories
|
||||
|
@ -79,7 +79,7 @@ fn test_get_log_topics() {
|
||||
assert_eq!(get_log_topics(LOG4), 4);
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum GasPriceTier {
|
||||
/// 0 Zero
|
||||
Zero,
|
||||
|
261
ethcore/src/evm/interpreter/gasometer.rs
Normal file
261
ethcore/src/evm/interpreter/gasometer.rs
Normal 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);
|
||||
}
|
150
ethcore/src/evm/interpreter/memory.rs
Normal file
150
ethcore/src/evm/interpreter/memory.rs
Normal 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));
|
||||
}
|
@ -16,12 +16,6 @@
|
||||
|
||||
///! Rust VM implementation
|
||||
|
||||
use common::*;
|
||||
use super::instructions as instructions;
|
||||
use super::instructions::{Instruction, get_info};
|
||||
use std::marker::Copy;
|
||||
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft};
|
||||
|
||||
#[cfg(not(feature = "evm-debug"))]
|
||||
macro_rules! evm_debug {
|
||||
($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")]
|
||||
fn color(instruction: Instruction, name: &'static str) -> String {
|
||||
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)
|
||||
}
|
||||
|
||||
macro_rules! overflowing {
|
||||
($x: expr) => {{
|
||||
let (v, overflow) = $x;
|
||||
if overflow { return Err(evm::Error::OutOfGas); }
|
||||
v
|
||||
}}
|
||||
}
|
||||
|
||||
type CodePosition = usize;
|
||||
type Gas = U256;
|
||||
type ProgramCounter = usize;
|
||||
|
||||
/// Stack trait with VM-friendly API
|
||||
trait Stack<T> {
|
||||
/// Returns `Stack[len(Stack) - no_from_top]`
|
||||
fn peek(&self, no_from_top: usize) -> &T;
|
||||
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
|
||||
fn swap_with_top(&mut self, no_from_top: usize);
|
||||
/// Returns true if Stack has at least `no_of_elems` elements
|
||||
fn has(&self, no_of_elems: usize) -> bool;
|
||||
/// Get element from top and remove it from Stack. Panics if stack is empty.
|
||||
fn pop_back(&mut self) -> T;
|
||||
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
|
||||
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
|
||||
/// Add element on top of the Stack
|
||||
fn push(&mut self, elem: T);
|
||||
/// Get number of elements on Stack
|
||||
fn size(&self) -> usize;
|
||||
/// Returns all data on stack.
|
||||
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
|
||||
}
|
||||
|
||||
struct VecStack<S> {
|
||||
stack: Vec<S>,
|
||||
logs: [S; instructions::MAX_NO_OF_TOPICS]
|
||||
}
|
||||
|
||||
impl<S : Copy> VecStack<S> {
|
||||
fn with_capacity(capacity: usize, zero: S) -> Self {
|
||||
VecStack {
|
||||
stack: Vec::with_capacity(capacity),
|
||||
logs: [zero; instructions::MAX_NO_OF_TOPICS]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S : fmt::Display> Stack<S> for VecStack<S> {
|
||||
fn peek(&self, no_from_top: usize) -> &S {
|
||||
&self.stack[self.stack.len() - no_from_top - 1]
|
||||
}
|
||||
|
||||
fn swap_with_top(&mut self, no_from_top: usize) {
|
||||
let len = self.stack.len();
|
||||
self.stack.swap(len - no_from_top - 1, len - 1);
|
||||
}
|
||||
|
||||
fn has(&self, no_of_elems: usize) -> bool {
|
||||
self.stack.len() >= no_of_elems
|
||||
}
|
||||
|
||||
fn pop_back(&mut self) -> S {
|
||||
let val = self.stack.pop();
|
||||
match val {
|
||||
Some(x) => {
|
||||
evm_debug!({
|
||||
println!(" POP: {}", x)
|
||||
});
|
||||
x
|
||||
},
|
||||
None => panic!("Tried to pop from empty stack.")
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
|
||||
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
|
||||
|
||||
for i in 0..no_of_elems {
|
||||
self.logs[i] = self.pop_back();
|
||||
}
|
||||
&self.logs[0..no_of_elems]
|
||||
}
|
||||
|
||||
fn push(&mut self, elem: S) {
|
||||
evm_debug!({
|
||||
println!(" PUSH: {}", elem)
|
||||
});
|
||||
self.stack.push(elem);
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
|
||||
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
|
||||
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
|
||||
}
|
||||
}
|
||||
|
||||
trait Memory {
|
||||
/// Retrieve current size of the memory
|
||||
fn size(&self) -> usize;
|
||||
/// Resize (shrink or expand) the memory to specified size (fills 0)
|
||||
fn resize(&mut self, new_size: usize);
|
||||
/// Resize the memory only if its smaller
|
||||
fn expand(&mut self, new_size: usize);
|
||||
/// Write single byte to memory
|
||||
fn write_byte(&mut self, offset: U256, value: U256);
|
||||
/// Write a word to memory. Does not resize memory!
|
||||
fn write(&mut self, offset: U256, value: U256);
|
||||
/// Read a word from memory
|
||||
fn read(&self, offset: U256) -> U256;
|
||||
/// Write slice of bytes to memory. Does not resize memory!
|
||||
fn write_slice(&mut self, offset: U256, &[u8]);
|
||||
/// Retrieve part of the memory between offset and offset + size
|
||||
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
|
||||
/// Retrieve writeable part of memory
|
||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
|
||||
fn dump(&self);
|
||||
}
|
||||
|
||||
/// Checks whether offset and size is valid memory range
|
||||
fn is_valid_range(off: usize, size: usize) -> bool {
|
||||
// When size is zero we haven't actually expanded the memory
|
||||
let overflow = off.overflowing_add(size).1;
|
||||
size > 0 && !overflow
|
||||
}
|
||||
|
||||
impl Memory for Vec<u8> {
|
||||
fn dump(&self) {
|
||||
println!("MemoryDump:");
|
||||
for i in self.iter() {
|
||||
println!("{:02x} ", i);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
|
||||
let off = init_off_u.low_u64() as usize;
|
||||
let size = init_size_u.low_u64() as usize;
|
||||
if !is_valid_range(off, size) {
|
||||
&self[0..0]
|
||||
} else {
|
||||
&self[off..off+size]
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, offset: U256) -> U256 {
|
||||
let off = offset.low_u64() as usize;
|
||||
U256::from(&self[off..off+32])
|
||||
}
|
||||
|
||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
|
||||
let off = offset.low_u64() as usize;
|
||||
let s = size.low_u64() as usize;
|
||||
if !is_valid_range(off, s) {
|
||||
&mut self[0..0]
|
||||
} else {
|
||||
&mut self[off..off+s]
|
||||
}
|
||||
}
|
||||
|
||||
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
|
||||
let off = offset.low_u64() as usize;
|
||||
|
||||
// TODO [todr] Optimize?
|
||||
for pos in off..off+slice.len() {
|
||||
self[pos] = slice[pos - off];
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: U256, value: U256) {
|
||||
let off = offset.low_u64() as usize;
|
||||
let mut val = value;
|
||||
|
||||
let end = off + 32;
|
||||
for pos in 0..32 {
|
||||
self[end - pos - 1] = val.low_u64() as u8;
|
||||
val = val >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
fn write_byte(&mut self, offset: U256, value: U256) {
|
||||
let off = offset.low_u64() as usize;
|
||||
let val = value.low_u64() as u64;
|
||||
self[off] = val as u8;
|
||||
}
|
||||
|
||||
fn resize(&mut self, new_size: usize) {
|
||||
self.resize(new_size, 0);
|
||||
}
|
||||
|
||||
fn expand(&mut self, size: usize) {
|
||||
if size > self.len() {
|
||||
Memory::resize(self, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over raw vector of Bytes. Easier state management of PC.
|
||||
struct CodeReader<'a> {
|
||||
position: ProgramCounter,
|
||||
@ -265,38 +72,33 @@ impl<'a> CodeReader<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
||||
enum InstructionCost {
|
||||
Gas(U256),
|
||||
GasMem(U256, U256),
|
||||
GasMemCopy(U256, U256, U256)
|
||||
}
|
||||
|
||||
enum InstructionResult {
|
||||
enum InstructionResult<Gas> {
|
||||
Ok,
|
||||
UseAllGas,
|
||||
GasLeft(U256),
|
||||
UnusedGas(U256),
|
||||
GasLeft(Gas),
|
||||
UnusedGas(Gas),
|
||||
JumpToPosition(U256),
|
||||
// gas left, init_orf, init_size
|
||||
StopExecutionNeedsReturn(U256, U256, U256),
|
||||
StopExecutionNeedsReturn(Gas, U256, U256),
|
||||
StopExecution,
|
||||
}
|
||||
|
||||
|
||||
/// Intepreter EVM implementation
|
||||
#[derive(Default)]
|
||||
pub struct Interpreter {
|
||||
pub struct Interpreter<Cost: CostType> {
|
||||
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> {
|
||||
self.mem.clear();
|
||||
|
||||
let code = ¶ms.code.as_ref().unwrap();
|
||||
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 reader = CodeReader {
|
||||
position: 0,
|
||||
@ -305,26 +107,27 @@ impl evm::Evm for Interpreter {
|
||||
|
||||
while reader.position < code.len() {
|
||||
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;
|
||||
|
||||
try!(self.verify_gas(¤t_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);
|
||||
current_gas = current_gas - gas_cost; //TODO: use operator -=
|
||||
gasometer.current_gas = gasometer.current_gas - gas_cost;
|
||||
|
||||
evm_debug!({
|
||||
println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}",
|
||||
reader.position,
|
||||
color(instruction, instructions::get_info(instruction).name),
|
||||
color(instruction, info.name),
|
||||
instruction,
|
||||
gas_cost,
|
||||
current_gas + gas_cost
|
||||
gasometer.current_gas + gas_cost
|
||||
);
|
||||
});
|
||||
|
||||
@ -335,50 +138,44 @@ impl evm::Evm for Interpreter {
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
current_gas, ¶ms, ext, instruction, &mut reader, &mut stack
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack
|
||||
));
|
||||
|
||||
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
|
||||
match result {
|
||||
InstructionResult::Ok => {},
|
||||
InstructionResult::UnusedGas(gas) => {
|
||||
current_gas = current_gas + gas; //TODO: use operator +=
|
||||
gasometer.current_gas = gasometer.current_gas + gas;
|
||||
},
|
||||
InstructionResult::UseAllGas => {
|
||||
current_gas = U256::zero();
|
||||
gasometer.current_gas = Cost::from(0);
|
||||
},
|
||||
InstructionResult::GasLeft(gas_left) => {
|
||||
current_gas = gas_left;
|
||||
gasometer.current_gas = gas_left;
|
||||
},
|
||||
InstructionResult::JumpToPosition(position) => {
|
||||
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
||||
reader.position = pos;
|
||||
},
|
||||
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
|
||||
return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size)));
|
||||
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
||||
},
|
||||
InstructionResult::StopExecution => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(GasLeft::Known(current_gas))
|
||||
Ok(GasLeft::Known(gasometer.current_gas.as_u256()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||
fn get_gas_cost_mem(
|
||||
&mut self,
|
||||
ext: &evm::Ext,
|
||||
instruction: Instruction,
|
||||
stack: &Stack<U256>
|
||||
) -> evm::Result<(U256, usize)> {
|
||||
impl<Cost: CostType> Interpreter<Cost> {
|
||||
|
||||
fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
|
||||
let schedule = ext.schedule();
|
||||
let info = instructions::get_info(instruction);
|
||||
|
||||
if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL {
|
||||
return Err(evm::Error::BadInstruction {
|
||||
@ -391,119 +188,20 @@ impl Interpreter {
|
||||
});
|
||||
}
|
||||
|
||||
try!(self.verify_instructions_requirements(&info, schedule.stack_limit, stack));
|
||||
|
||||
let tier = instructions::get_tier_idx(info.tier);
|
||||
let default_gas = U256::from(schedule.tier_step_gas[tier]);
|
||||
|
||||
let cost = match instruction {
|
||||
instructions::SSTORE => {
|
||||
let address = H256::from(stack.peek(0));
|
||||
let newval = stack.peek(1);
|
||||
let val = U256::from(ext.storage_at(&address).as_slice());
|
||||
|
||||
let gas = if self.is_zero(&val) && !self.is_zero(newval) {
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// Refund for below case is added when actually executing sstore
|
||||
// !self.is_zero(&val) && self.is_zero(newval)
|
||||
schedule.sstore_reset_gas
|
||||
};
|
||||
InstructionCost::Gas(U256::from(gas))
|
||||
},
|
||||
instructions::SLOAD => {
|
||||
InstructionCost::Gas(U256::from(schedule.sload_gas))
|
||||
},
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32)))
|
||||
},
|
||||
instructions::MSTORE8 => {
|
||||
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1)))
|
||||
},
|
||||
instructions::RETURN => {
|
||||
InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
|
||||
},
|
||||
instructions::SHA3 => {
|
||||
let w = overflowing!(add_u256_usize(stack.peek(1), 31));
|
||||
let words = w >> 5;
|
||||
let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words);
|
||||
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
|
||||
},
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), stack.peek(2).clone())
|
||||
},
|
||||
instructions::EXTCODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), stack.peek(3).clone())
|
||||
},
|
||||
instructions::JUMPDEST => {
|
||||
InstructionCost::Gas(U256::one())
|
||||
},
|
||||
instructions::LOG0...instructions::LOG4 => {
|
||||
let no_of_topics = instructions::get_log_topics(instruction);
|
||||
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
|
||||
|
||||
let data_gas = overflowing!(stack.peek(1).overflowing_mul(U256::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflowing_add(U256::from(log_gas)));
|
||||
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1))))
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
|
||||
let mem = cmp::max(
|
||||
try!(self.mem_needed(stack.peek(5), stack.peek(6))),
|
||||
try!(self.mem_needed(stack.peek(3), stack.peek(4)))
|
||||
);
|
||||
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
|
||||
if instruction == instructions::CALL && !ext.exists(&address) {
|
||||
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_new_account_gas)));
|
||||
};
|
||||
|
||||
if stack.peek(2).clone() > U256::zero() {
|
||||
gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_value_transfer_gas)));
|
||||
};
|
||||
|
||||
InstructionCost::GasMem(gas,mem)
|
||||
},
|
||||
instructions::DELEGATECALL => {
|
||||
let gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas));
|
||||
let mem = cmp::max(
|
||||
try!(self.mem_needed(stack.peek(4), stack.peek(5))),
|
||||
try!(self.mem_needed(stack.peek(2), stack.peek(3)))
|
||||
);
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
},
|
||||
instructions::CREATE => {
|
||||
let gas = U256::from(schedule.create_gas);
|
||||
let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2)));
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
},
|
||||
instructions::EXP => {
|
||||
let expon = stack.peek(1);
|
||||
let bytes = ((expon.bits() + 7) / 8) as usize;
|
||||
let gas = U256::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||
InstructionCost::Gas(gas)
|
||||
},
|
||||
_ => InstructionCost::Gas(default_gas)
|
||||
};
|
||||
|
||||
match cost {
|
||||
InstructionCost::Gas(gas) => {
|
||||
Ok((gas, 0))
|
||||
},
|
||||
InstructionCost::GasMem(gas, mem_size) => {
|
||||
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
|
||||
let gas = overflowing!(gas.overflowing_add(mem_gas));
|
||||
Ok((gas, new_mem_size))
|
||||
},
|
||||
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size));
|
||||
let copy = overflowing!(add_u256_usize(©, 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))
|
||||
}
|
||||
if !stack.has(info.args) {
|
||||
Err(evm::Error::StackUnderflow {
|
||||
instruction: info.name,
|
||||
wanted: info.args,
|
||||
on_stack: stack.size()
|
||||
})
|
||||
} else if stack.size() - info.args + info.ret > schedule.stack_limit {
|
||||
Err(evm::Error::OutOfStack {
|
||||
instruction: info.name,
|
||||
wanted: info.ret - info.args,
|
||||
limit: schedule.stack_limit
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))]
|
||||
fn exec_instruction(
|
||||
&mut self,
|
||||
gas: Gas,
|
||||
gas: Cost,
|
||||
params: &ActionParams,
|
||||
ext: &mut evm::Ext,
|
||||
instruction: Instruction,
|
||||
code: &mut CodeReader,
|
||||
stack: &mut Stack<U256>
|
||||
) -> evm::Result<InstructionResult> {
|
||||
) -> evm::Result<InstructionResult<Cost>> {
|
||||
match instruction {
|
||||
instructions::JUMP => {
|
||||
let jump = stack.pop_back();
|
||||
@ -611,11 +272,11 @@ impl Interpreter {
|
||||
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 {
|
||||
ContractCreateResult::Created(address, gas_left) => {
|
||||
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 => {
|
||||
stack.push(U256::zero());
|
||||
@ -626,7 +287,7 @@ impl Interpreter {
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
||||
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 = u256_to_address(&code_address);
|
||||
|
||||
@ -642,9 +303,9 @@ impl Interpreter {
|
||||
let out_size = stack.pop_back();
|
||||
|
||||
// Add stipend (only CALL|CALLCODE when value > 0)
|
||||
let call_gas = call_gas + value.map_or_else(U256::zero, |val| match val > U256::zero() {
|
||||
true => U256::from(ext.schedule().call_stipend),
|
||||
false => U256::zero()
|
||||
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val > U256::zero() {
|
||||
true => Cost::from(ext.schedule().call_stipend),
|
||||
false => Cost::from(0)
|
||||
});
|
||||
|
||||
// Get sender & receive addresses, check if we have balance
|
||||
@ -672,13 +333,13 @@ impl Interpreter {
|
||||
// and we don't want to copy
|
||||
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
|
||||
let output = self.mem.writeable_slice(out_off, out_size);
|
||||
ext.call(&call_gas, 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 {
|
||||
MessageCallResult::Success(gas_left) => {
|
||||
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 => {
|
||||
stack.push(U256::zero());
|
||||
@ -759,7 +420,7 @@ impl Interpreter {
|
||||
stack.push(U256::from(code.position - 1));
|
||||
},
|
||||
instructions::GAS => {
|
||||
stack.push(gas.clone());
|
||||
stack.push(gas.as_u256());
|
||||
},
|
||||
instructions::ADDRESS => {
|
||||
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> {
|
||||
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]
|
||||
fn u256_to_address(value: &U256) -> Address {
|
||||
Address::from(H256::from(value))
|
||||
@ -1179,82 +805,14 @@ fn address_to_u256(value: Address) -> U256 {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_gas_cost() {
|
||||
fn test_find_jump_destinations() {
|
||||
// given
|
||||
let interpreter = Interpreter::default();
|
||||
let schedule = evm::Schedule::default();
|
||||
let current_mem_size = 5;
|
||||
let mem_size = !U256::zero();
|
||||
let interpreter = Interpreter::<U256>::default();
|
||||
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
|
||||
|
||||
// when
|
||||
let result = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
|
||||
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
|
||||
|
||||
// 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();
|
||||
|
||||
// when
|
||||
let valid_jump_destinations = interpreter.find_jump_destinations(&code);
|
||||
|
||||
// then
|
||||
assert!(valid_jump_destinations.contains(&66));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_mem_cost() {
|
||||
// given
|
||||
let interpreter = Interpreter::default();
|
||||
let schedule = evm::Schedule::default();
|
||||
let current_mem_size = 0;
|
||||
let mem_size = U256::from(5);
|
||||
|
||||
// when
|
||||
let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(mem_cost, U256::from(3));
|
||||
assert_eq!(mem_size, 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_read_and_write() {
|
||||
// given
|
||||
let mem: &mut super::Memory = &mut vec![];
|
||||
mem.resize(0x80 + 32);
|
||||
|
||||
// when
|
||||
mem.write(U256::from(0x80), U256::from(0xabcdef));
|
||||
|
||||
// then
|
||||
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_read_and_write_byte() {
|
||||
// given
|
||||
let mem: &mut super::Memory = &mut vec![];
|
||||
mem.resize(32);
|
||||
|
||||
// when
|
||||
mem.write_byte(U256::from(0x1d), U256::from(0xab));
|
||||
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
|
||||
mem.write_byte(U256::from(0x1f), U256::from(0xef));
|
||||
|
||||
// then
|
||||
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
|
||||
}
|
||||
assert!(valid_jump_destinations.contains(&66));
|
||||
}
|
106
ethcore/src/evm/interpreter/stack.rs
Normal file
106
ethcore/src/evm/interpreter/stack.rs
Normal 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()]
|
||||
}
|
||||
}
|
||||
|
@ -28,8 +28,10 @@ mod jit;
|
||||
|
||||
#[cfg(test)]
|
||||
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::factory::{Factory, VMType};
|
||||
pub use self::schedule::Schedule;
|
||||
|
@ -18,18 +18,18 @@ use common::*;
|
||||
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
|
||||
use std::fmt::Debug;
|
||||
|
||||
struct FakeLogEntry {
|
||||
pub struct FakeLogEntry {
|
||||
topics: Vec<H256>,
|
||||
data: Bytes
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
enum FakeCallType {
|
||||
pub enum FakeCallType {
|
||||
Call, Create
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
struct FakeCall {
|
||||
pub struct FakeCall {
|
||||
call_type: FakeCallType,
|
||||
gas: U256,
|
||||
sender_address: Option<Address>,
|
||||
@ -43,7 +43,7 @@ struct FakeCall {
|
||||
///
|
||||
/// Can't do recursive calls.
|
||||
#[derive(Default)]
|
||||
struct FakeExt {
|
||||
pub struct FakeExt {
|
||||
sstore_clears: usize,
|
||||
depth: usize,
|
||||
store: HashMap<H256, H256>,
|
||||
@ -67,7 +67,7 @@ fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
|
||||
}
|
||||
|
||||
impl FakeExt {
|
||||
fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
FakeExt::default()
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@ fn test_stack_underflow() {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let err = {
|
||||
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::default());
|
||||
let mut vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::default());
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -228,7 +228,7 @@ fn test_sha3(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -248,7 +248,7 @@ fn test_address(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -270,7 +270,7 @@ fn test_origin(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -292,7 +292,7 @@ fn test_sender(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -327,7 +327,7 @@ fn test_extcodecopy(factory: super::Factory) {
|
||||
ext.codes.insert(sender, sender_code);
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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());
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -426,7 +426,7 @@ fn test_calldataload(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -447,7 +447,7 @@ fn test_author(factory: super::Factory) {
|
||||
ext.info.author = author;
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -467,7 +467,7 @@ fn test_timestamp(factory: super::Factory) {
|
||||
ext.info.timestamp = timestamp;
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -487,7 +487,7 @@ fn test_number(factory: super::Factory) {
|
||||
ext.info.number = number;
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -507,7 +507,7 @@ fn test_difficulty(factory: super::Factory) {
|
||||
ext.info.difficulty = difficulty;
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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;
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -545,7 +545,7 @@ fn test_mul(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -563,7 +563,7 @@ fn test_sub(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -581,7 +581,7 @@ fn test_div(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -617,7 +617,7 @@ fn test_mod(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -636,7 +636,7 @@ fn test_smod(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -655,7 +655,7 @@ fn test_sdiv(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -674,7 +674,7 @@ fn test_exp(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -694,7 +694,7 @@ fn test_comparison(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -736,7 +736,7 @@ fn test_bitops(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -780,7 +780,7 @@ fn test_byte(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -799,7 +799,7 @@ fn test_signextend(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -819,7 +819,7 @@ fn test_badinstruction_int() {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let err = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
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 gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -859,7 +859,7 @@ fn test_extops(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -882,7 +882,7 @@ fn test_jumps(factory: super::Factory) {
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
@ -911,7 +911,7 @@ fn test_calls(factory: super::Factory) {
|
||||
};
|
||||
|
||||
let gas_left = {
|
||||
let mut vm = factory.create();
|
||||
let mut vm = factory.create(params.gas);
|
||||
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
|
@ -211,7 +211,7 @@ impl<'a> Executive<'a> {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
||||
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
|
||||
@ -222,7 +222,7 @@ impl<'a> Executive<'a> {
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
|
||||
scope.spawn(move || {
|
||||
vm_factory.create().exec(params, &mut ext).finalize(ext)
|
||||
vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext)
|
||||
})
|
||||
}).join()
|
||||
}
|
||||
|
@ -319,7 +319,6 @@ mod tests {
|
||||
last_hashes: vec![],
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,10 +92,10 @@ impl PartialEq for Header {
|
||||
impl Default for Header {
|
||||
fn default() -> Self {
|
||||
Header {
|
||||
parent_hash: ZERO_H256.clone(),
|
||||
parent_hash: H256::default(),
|
||||
timestamp: 0,
|
||||
number: 0,
|
||||
author: ZERO_ADDRESS.clone(),
|
||||
author: Address::default(),
|
||||
|
||||
transactions_root: SHA3_NULL_RLP,
|
||||
uncles_hash: SHA3_EMPTY_LIST_RLP,
|
||||
@ -104,10 +104,10 @@ impl Default for Header {
|
||||
state_root: SHA3_NULL_RLP,
|
||||
receipts_root: SHA3_NULL_RLP,
|
||||
log_bloom: ZERO_LOGBLOOM.clone(),
|
||||
gas_used: ZERO_U256,
|
||||
gas_limit: ZERO_U256,
|
||||
gas_used: U256::default(),
|
||||
gas_limit: U256::default(),
|
||||
|
||||
difficulty: ZERO_U256,
|
||||
difficulty: U256::default(),
|
||||
seal: vec![],
|
||||
hash: RefCell::new(None),
|
||||
bare_hash: RefCell::new(None),
|
||||
|
@ -208,7 +208,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
|
||||
&mut tracer,
|
||||
&mut vm_tracer,
|
||||
);
|
||||
let mut evm = vm_factory.create();
|
||||
let mut evm = vm_factory.create(params.gas);
|
||||
let res = evm.exec(params, &mut ex);
|
||||
// a return in finalize will not alter callcreates
|
||||
let callcreates = ex.callcreates.clone();
|
||||
|
@ -23,3 +23,4 @@ mod state;
|
||||
mod chain;
|
||||
mod homestead_state;
|
||||
mod homestead_chain;
|
||||
mod trie;
|
||||
|
69
ethcore/src/json_tests/trie.rs
Normal file
69
ethcore/src/json_tests/trie.rs
Normal 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"}
|
||||
}
|
@ -31,6 +31,7 @@
|
||||
#![cfg_attr(feature="dev", allow(needless_borrow))]
|
||||
#![cfg_attr(feature="dev", allow(assign_op_pattern))]
|
||||
|
||||
#![cfg_attr(feature="benches", feature(test))]
|
||||
|
||||
//! Ethcore library
|
||||
//!
|
||||
@ -94,6 +95,8 @@ extern crate rayon;
|
||||
extern crate hyper;
|
||||
extern crate ethash;
|
||||
pub extern crate ethstore;
|
||||
extern crate semver;
|
||||
extern crate ethcore_ipc_nano as nanoipc;
|
||||
|
||||
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||
@ -105,7 +108,6 @@ pub mod block_queue;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod ethereum;
|
||||
pub mod filter;
|
||||
pub mod header;
|
||||
pub mod service;
|
||||
pub mod trace;
|
||||
|
@ -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.
|
||||
|
||||
mod v6;
|
||||
|
@ -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.
|
||||
pub struct ToV6;
|
||||
@ -17,12 +33,12 @@ impl ToV6 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Migration for ToV6 {
|
||||
impl SimpleMigration for ToV6 {
|
||||
fn version(&self) -> u32 {
|
||||
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.
|
||||
if key.len() == 33 {
|
||||
|
@ -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.
|
||||
|
||||
mod v7;
|
||||
|
||||
pub use self::v7::ToV7;
|
||||
pub use self::v7::{ArchiveV7, OverlayRecentV7};
|
@ -1,31 +1,205 @@
|
||||
use util::hash::{FixedHash, H256};
|
||||
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/>.
|
||||
|
||||
//! 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;
|
||||
|
||||
/// This migration migrates the state db to use an accountdb which ensures uniqueness
|
||||
/// using an address' hash as opposed to the address itself.
|
||||
pub struct ToV7;
|
||||
// attempt to migrate a key, value pair. None if migration not possible.
|
||||
fn attempt_migrate(mut key_h: H256, val: &[u8]) -> Option<H256> {
|
||||
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 {
|
||||
7
|
||||
}
|
||||
|
||||
fn simple_migrate(&self, mut key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
let val_hash = value.sha3();
|
||||
assert!(key.len() == 32); // all keys in the state db are hashes.
|
||||
let key_h = H256::from_slice(&key[..]);
|
||||
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[..]);
|
||||
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
|
||||
if key.len() != 32 {
|
||||
// metadata key, ignore.
|
||||
return Some((key, value));
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
// nothing to do here
|
||||
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)
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
use util::*;
|
||||
use util::Colour::White;
|
||||
use util::using_queue::{UsingQueue, GetAction};
|
||||
use account_provider::AccountProvider;
|
||||
use views::{BlockView, HeaderView};
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||
@ -29,8 +29,10 @@ use transaction::SignedTransaction;
|
||||
use receipt::{Receipt};
|
||||
use spec::Spec;
|
||||
use engine::Engine;
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
use miner::work_notify::WorkPoster;
|
||||
use client::TransactionImportResult;
|
||||
|
||||
|
||||
/// Different possible definitions for pending transaction set.
|
||||
#[derive(Debug)]
|
||||
@ -198,17 +200,23 @@ impl Miner {
|
||||
let hash = tx.hash();
|
||||
match open_block.push_transaction(tx, None) {
|
||||
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
|
||||
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
|
||||
if gas_limit - gas_used < min_tx_gas {
|
||||
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) => {
|
||||
invalid_transactions.insert(hash);
|
||||
trace!(target: "miner",
|
||||
debug!(target: "miner",
|
||||
"Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
|
||||
block_number, hash, e);
|
||||
},
|
||||
@ -265,6 +273,10 @@ impl Miner {
|
||||
let difficulty = *block.block().fields().header.difficulty();
|
||||
let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h);
|
||||
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)
|
||||
} else {
|
||||
(None, false)
|
||||
@ -303,6 +315,19 @@ impl Miner {
|
||||
!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?
|
||||
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,
|
||||
gas_used: U256::zero(),
|
||||
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.
|
||||
let mut state = block.state().clone();
|
||||
@ -466,27 +490,24 @@ impl MinerService for Miner {
|
||||
self.gas_range_target.read().unwrap().1
|
||||
}
|
||||
|
||||
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
|
||||
Vec<Result<TransactionImportResult, Error>>
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
let results: Vec<Result<TransactionImportResult, Error>> = {
|
||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||
transactions.into_iter()
|
||||
.map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External))
|
||||
.collect()
|
||||
};
|
||||
if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
|
||||
fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
|
||||
Vec<Result<TransactionImportResult, Error>> {
|
||||
|
||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||
let results = self.add_transactions_to_queue(chain, transactions, TransactionOrigin::External,
|
||||
&mut transaction_queue);
|
||||
|
||||
if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
|
||||
self.update_sealing(chain);
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
fn import_own_transaction<T>(
|
||||
fn import_own_transaction(
|
||||
&self,
|
||||
chain: &MiningBlockChainClient,
|
||||
transaction: SignedTransaction,
|
||||
fetch_account: T
|
||||
) -> Result<TransactionImportResult, Error> where T: Fn(&Address) -> AccountDetails {
|
||||
) -> Result<TransactionImportResult, Error> {
|
||||
|
||||
let hash = transaction.hash();
|
||||
trace!(target: "own_tx", "Importing transaction: {:?}", transaction);
|
||||
@ -494,7 +515,7 @@ impl MinerService for Miner {
|
||||
let imported = {
|
||||
// Be sure to release the lock before we call enable_and_prepare_sealing
|
||||
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 {
|
||||
Ok(ref res) => {
|
||||
@ -633,7 +654,7 @@ impl MinerService for Miner {
|
||||
let n = sealed.header().number();
|
||||
let h = sealed.header().hash();
|
||||
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(())
|
||||
})
|
||||
}
|
||||
@ -645,7 +666,12 @@ impl MinerService for Miner {
|
||||
// Client should send message after commit to db and inserting to chain.
|
||||
.expect("Expected in-chain blocks.");
|
||||
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
|
||||
@ -662,14 +688,10 @@ impl MinerService for Miner {
|
||||
.par_iter()
|
||||
.map(|h| fetch_transactions(chain, h));
|
||||
out_of_chain.for_each(|txs| {
|
||||
// populate sender
|
||||
for tx in &txs {
|
||||
let _sender = tx.sender();
|
||||
}
|
||||
let _ = self.import_transactions(chain, txs, |a| AccountDetails {
|
||||
nonce: chain.latest_nonce(a),
|
||||
balance: chain.latest_balance(a),
|
||||
});
|
||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||
let _ = self.add_transactions_to_queue(
|
||||
chain, txs, TransactionOrigin::External, &mut transaction_queue
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
//! use ethcore::miner::{Miner, MinerService};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let miner: Miner = Miner::with_spec(ethereum::new_frontier(true));
|
||||
//! let miner: Miner = Miner::with_spec(ethereum::new_frontier());
|
||||
//! // get status
|
||||
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
|
||||
//!
|
||||
@ -47,9 +47,10 @@ mod external;
|
||||
mod transaction_queue;
|
||||
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::external::{ExternalMiner, ExternalMinerService};
|
||||
pub use client::TransactionImportResult;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::{H256, U256, Address, Bytes};
|
||||
@ -106,14 +107,12 @@ pub trait MinerService : Send + Sync {
|
||||
fn set_tx_gas_limit(&self, limit: U256);
|
||||
|
||||
/// Imports transactions to transaction queue.
|
||||
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
|
||||
Vec<Result<TransactionImportResult, Error>>
|
||||
where T: Fn(&Address) -> AccountDetails, Self: Sized;
|
||||
fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
|
||||
Vec<Result<TransactionImportResult, Error>>;
|
||||
|
||||
/// Imports own (node owner) transaction to queue.
|
||||
fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, fetch_account: T) ->
|
||||
Result<TransactionImportResult, Error>
|
||||
where T: Fn(&Address) -> AccountDetails, Self: Sized;
|
||||
fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction) ->
|
||||
Result<TransactionImportResult, Error>;
|
||||
|
||||
/// Returns hashes of transactions currently in pending
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256>;
|
||||
|
@ -87,9 +87,10 @@ use std::cmp;
|
||||
use std::collections::{HashMap, BTreeSet};
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::hash::{Address, H256};
|
||||
use util::table::*;
|
||||
use util::table::Table;
|
||||
use transaction::*;
|
||||
use error::{Error, TransactionError};
|
||||
use client::TransactionImportResult;
|
||||
|
||||
/// Transaction origin
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@ -197,6 +198,7 @@ struct VerifiedTransaction {
|
||||
/// transaction origin
|
||||
origin: TransactionOrigin,
|
||||
}
|
||||
|
||||
impl VerifiedTransaction {
|
||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> {
|
||||
try!(transaction.sender());
|
||||
@ -309,15 +311,6 @@ pub struct TransactionQueueStatus {
|
||||
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
|
||||
pub struct AccountDetails {
|
||||
/// 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>
|
||||
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 {
|
||||
trace!(target: "miner",
|
||||
trace!(target: "txqueue",
|
||||
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
|
||||
tx.hash(),
|
||||
tx.gas_price,
|
||||
@ -458,7 +451,7 @@ impl TransactionQueue {
|
||||
try!(tx.check_low_s());
|
||||
|
||||
if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit {
|
||||
trace!(target: "miner",
|
||||
trace!(target: "txqueue",
|
||||
"Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
|
||||
tx.hash(),
|
||||
tx.gas,
|
||||
@ -477,7 +470,7 @@ impl TransactionQueue {
|
||||
|
||||
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
|
||||
if client_account.balance < cost {
|
||||
trace!(target: "miner",
|
||||
trace!(target: "txqueue",
|
||||
"Dropping transaction without sufficient balance: {:?} ({} < {})",
|
||||
vtx.hash(),
|
||||
client_account.balance,
|
||||
@ -565,7 +558,7 @@ impl TransactionQueue {
|
||||
if k >= current_nonce {
|
||||
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
||||
} 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
|
||||
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 {
|
||||
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
||||
} 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`");
|
||||
}
|
||||
}
|
||||
@ -674,7 +667,7 @@ impl TransactionQueue {
|
||||
|
||||
if self.by_hash.get(&tx.hash()).is_some() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -691,7 +684,7 @@ impl TransactionQueue {
|
||||
// nonce height would result in overflow.
|
||||
if nonce < state_nonce {
|
||||
// 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);
|
||||
} else if nonce > next_nonce {
|
||||
// We have a gap - put to future.
|
||||
@ -727,7 +720,7 @@ impl TransactionQueue {
|
||||
// Trigger error if the transaction we are importing was removed.
|
||||
try!(check_if_removed(&address, &nonce, removed));
|
||||
|
||||
trace!(target: "miner", "status: {:?}", self.status());
|
||||
trace!(target: "txqueue", "status: {:?}", self.status());
|
||||
Ok(TransactionImportResult::Current)
|
||||
}
|
||||
|
||||
@ -812,6 +805,7 @@ mod test {
|
||||
use error::{Error, TransactionError};
|
||||
use super::*;
|
||||
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
||||
use client::TransactionImportResult;
|
||||
|
||||
fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError {
|
||||
match err.unwrap_err() {
|
||||
|
@ -52,10 +52,10 @@ impl WorkPoster {
|
||||
}
|
||||
|
||||
fn create_client() -> Client<PostHandler> {
|
||||
let client = Client::<PostHandler>::configure()
|
||||
Client::<PostHandler>::configure()
|
||||
.keep_alive(true)
|
||||
.build().expect("Error creating HTTP client") as Client<PostHandler>;
|
||||
client
|
||||
.build()
|
||||
.expect("Error creating HTTP client")
|
||||
}
|
||||
|
||||
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 seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number);
|
||||
let seed_hash = H256::from_slice(&seed_hash[..]);
|
||||
let body = format!(r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
|
||||
pow_hash.hex(), seed_hash.hex(), target.hex(), number);
|
||||
let body = format!(
|
||||
r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
|
||||
pow_hash.hex(), seed_hash.hex(), target.hex(), number
|
||||
);
|
||||
let mut client = self.client.lock().unwrap();
|
||||
for u in &self.urls {
|
||||
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
|
||||
@ -104,12 +106,12 @@ impl hyper::client::Handler<HttpStream> for PostHandler {
|
||||
}
|
||||
|
||||
fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder<HttpStream>) -> Next {
|
||||
Next::end()
|
||||
Next::end()
|
||||
}
|
||||
|
||||
fn on_error(&mut self, err: hyper::Error) -> Next {
|
||||
fn on_error(&mut self, err: hyper::Error) -> Next {
|
||||
trace!("Error posting work data: {}", err);
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ impl fmt::Display for PodState {
|
||||
|
||||
/// Calculate and return diff between `pre` state and `post` state.
|
||||
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)]
|
||||
@ -86,22 +86,22 @@ mod test {
|
||||
#[test]
|
||||
fn create_delete() {
|
||||
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{
|
||||
balance: Diff::Died(69.into()),
|
||||
nonce: Diff::Died(0.into()),
|
||||
code: Diff::Died(vec![]),
|
||||
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{
|
||||
balance: Diff::Born(69.into()),
|
||||
nonce: Diff::Born(0.into()),
|
||||
code: Diff::Born(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
]));
|
||||
]});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -111,22 +111,22 @@ mod test {
|
||||
1.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{
|
||||
balance: Diff::Born(69.into()),
|
||||
nonce: Diff::Born(0.into()),
|
||||
code: Diff::Born(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
]));
|
||||
assert_eq!(super::diff_pod(&b, &a), StateDiff(map![
|
||||
]});
|
||||
assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![
|
||||
2.into() => AccountDiff{
|
||||
balance: Diff::Died(69.into()),
|
||||
nonce: Diff::Died(0.into()),
|
||||
code: Diff::Died(vec![]),
|
||||
storage: map![],
|
||||
}
|
||||
]));
|
||||
]});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -139,14 +139,14 @@ mod test {
|
||||
1.into() => PodAccount::new(69.into(), 1.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{
|
||||
balance: Diff::Same,
|
||||
nonce: Diff::Changed(0.into(), 1.into()),
|
||||
code: Diff::Same,
|
||||
storage: map![],
|
||||
}
|
||||
]));
|
||||
]});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
//! Creates and registers client and network services.
|
||||
|
||||
use util::*;
|
||||
use util::Colour::{Yellow, White};
|
||||
use util::panics::*;
|
||||
use spec::Spec;
|
||||
use error::*;
|
||||
@ -72,7 +71,7 @@ impl ClientService {
|
||||
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()));
|
||||
panic_handler.forward_from(client.deref());
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
@ -135,16 +134,14 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
||||
|
||||
#[cfg_attr(feature="dev", allow(single_match))]
|
||||
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
|
||||
if let UserMessage(ref message) = *net_message {
|
||||
match *message {
|
||||
SyncMessage::BlockVerified => {
|
||||
self.client.import_verified_blocks(&io.channel());
|
||||
},
|
||||
SyncMessage::NewTransactions(ref transactions) => {
|
||||
self.client.import_queued_transactions(&transactions);
|
||||
},
|
||||
_ => {}, // ignore other messages
|
||||
}
|
||||
match *net_message {
|
||||
UserMessage(ref message) => match *message {
|
||||
SyncMessage::BlockVerified => { self.client.import_verified_blocks(&io.channel()); }
|
||||
SyncMessage::NewTransactions(ref transactions) => { self.client.import_queued_transactions(&transactions); }
|
||||
_ => {} // ignore other messages
|
||||
},
|
||||
NetworkIoMessage::NetworkStarted(ref url) => { self.client.network_started(url); }
|
||||
_ => {} // ignore other messages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
//! Account state encoding and decoding
|
||||
|
||||
use account_db::{AccountDB, AccountDBMut};
|
||||
use client::BlockChainClient;
|
||||
use error::Error;
|
||||
|
||||
use util::{Bytes, HashDB, SHA3_EMPTY, TrieDB};
|
||||
|
@ -17,15 +17,15 @@
|
||||
//! Snapshot creation helpers.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use account_db::{AccountDB, AccountDBMut};
|
||||
use client::BlockChainClient;
|
||||
use error::Error;
|
||||
use ids::BlockID;
|
||||
use views::BlockView;
|
||||
use views::{BlockView, HeaderView};
|
||||
|
||||
use util::{Bytes, Hashable, HashDB, JournalDB, snappy, TrieDB, TrieDBMut, TrieMut};
|
||||
use util::hash::{FixedHash, H256};
|
||||
@ -42,6 +42,40 @@ mod block;
|
||||
// Try to have chunks be around 16MB (before compression)
|
||||
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
|
||||
// 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> {
|
||||
|
@ -42,6 +42,7 @@ pub struct State {
|
||||
cache: RefCell<HashMap<Address, Option<Account>>>,
|
||||
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
|
||||
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. \
|
||||
@ -50,11 +51,11 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
|
||||
impl State {
|
||||
/// Creates new state with empty state root
|
||||
#[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();
|
||||
{
|
||||
// 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 {
|
||||
@ -63,22 +64,26 @@ impl State {
|
||||
cache: RefCell::new(HashMap::new()),
|
||||
snapshots: RefCell::new(Vec::new()),
|
||||
account_start_nonce: account_start_nonce,
|
||||
trie_factory: trie_factory,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
Err(TrieError::InvalidStateRoot)
|
||||
} else {
|
||||
Ok(State {
|
||||
db: db,
|
||||
root: root,
|
||||
cache: RefCell::new(HashMap::new()),
|
||||
snapshots: RefCell::new(Vec::new()),
|
||||
account_start_nonce: account_start_nonce,
|
||||
})
|
||||
return Err(TrieError::InvalidStateRoot);
|
||||
}
|
||||
|
||||
let state = State {
|
||||
db: db,
|
||||
root: root,
|
||||
cache: RefCell::new(HashMap::new()),
|
||||
snapshots: RefCell::new(Vec::new()),
|
||||
account_start_nonce: account_start_nonce,
|
||||
trie_factory: trie_factory,
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Create a recoverable snaphot of this state
|
||||
@ -156,7 +161,7 @@ impl State {
|
||||
|
||||
/// Determine whether an account exists.
|
||||
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)
|
||||
}
|
||||
|
||||
@ -242,7 +247,10 @@ impl State {
|
||||
for a in &addresses {
|
||||
if self.code(a).map_or(false, |c| c.sha3() == broken_dao) {
|
||||
// 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)) {
|
||||
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.
|
||||
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
||||
#[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.
|
||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match a {
|
||||
&mut&mut Some(ref mut account) => {
|
||||
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);
|
||||
}
|
||||
&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() {
|
||||
match **a {
|
||||
Some(ref account) => trie.insert(address, &account.rlp()),
|
||||
@ -290,7 +298,7 @@ impl State {
|
||||
/// Commits our cached account changes into the trie.
|
||||
pub fn commit(&mut self) {
|
||||
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)]
|
||||
@ -340,7 +348,7 @@ impl State {
|
||||
fn get<'a>(&'a self, a: &Address, require_code: bool) -> &'a Option<Account> {
|
||||
let have_key = self.cache.borrow().contains_key(a);
|
||||
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))
|
||||
}
|
||||
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 {
|
||||
let have_key = self.cache.borrow().contains_key(a);
|
||||
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))
|
||||
} else {
|
||||
self.note_cache(a);
|
||||
@ -396,6 +404,7 @@ impl Clone for State {
|
||||
cache: RefCell::new(self.cache.borrow().clone()),
|
||||
snapshots: RefCell::new(self.snapshots.borrow().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()
|
||||
};
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
@ -1194,7 +1203,7 @@ fn storage_at_from_database() {
|
||||
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)));
|
||||
}
|
||||
|
||||
@ -1211,7 +1220,7 @@ fn get_from_database() {
|
||||
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.nonce(&a), U256::from(1u64));
|
||||
}
|
||||
@ -1244,7 +1253,7 @@ fn remove_from_database() {
|
||||
};
|
||||
|
||||
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.nonce(&a), U256::from(1u64));
|
||||
state.kill_account(&a);
|
||||
@ -1254,7 +1263,7 @@ fn remove_from_database() {
|
||||
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.nonce(&a), U256::from(0u64));
|
||||
}
|
||||
|
@ -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(
|
||||
test_engine.deref(),
|
||||
&vm_factory,
|
||||
Default::default(),
|
||||
false,
|
||||
db,
|
||||
&last_header,
|
||||
last_hashes.clone(),
|
||||
None,
|
||||
author.clone(),
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![]
|
||||
@ -315,7 +315,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
|
||||
let journal_db = get_temp_journal_db_in(temp.as_path());
|
||||
GuardedTempResult {
|
||||
_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 {
|
||||
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> {
|
||||
|
@ -16,3 +16,4 @@
|
||||
|
||||
pub mod helpers;
|
||||
mod client;
|
||||
mod rpc;
|
||||
|
71
ethcore/src/tests/rpc.rs
Normal file
71
ethcore/src/tests/rpc.rs
Normal 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());
|
||||
})
|
||||
}
|
@ -16,11 +16,17 @@
|
||||
|
||||
//! 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).
|
||||
pub enum Diff<T> where T: Eq {
|
||||
pub enum Diff<T> where T: Eq + BinaryConvertable {
|
||||
/// Both sides are the same.
|
||||
Same,
|
||||
/// 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),
|
||||
}
|
||||
|
||||
impl<T> Diff<T> where T: Eq {
|
||||
impl<T> Diff<T> where T: Eq + BinaryConvertable {
|
||||
/// 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) } }
|
||||
|
||||
@ -45,7 +51,7 @@ impl<T> Diff<T> where T: Eq {
|
||||
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.
|
||||
pub struct AccountDiff {
|
||||
/// Change in balance, allowed to be `Diff::Same`.
|
||||
@ -58,7 +64,7 @@ pub struct AccountDiff {
|
||||
pub storage: BTreeMap<H256, Diff<H256>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Binary)]
|
||||
/// Change in existance type.
|
||||
// TODO: include other types of change.
|
||||
pub enum Existance {
|
||||
@ -94,6 +100,8 @@ impl AccountDiff {
|
||||
|
||||
// TODO: refactor into something nicer.
|
||||
fn interpreted_hash(u: &H256) -> String {
|
||||
use util::bytes::*;
|
||||
|
||||
if u <= &H256::from(0xffffffff) {
|
||||
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()) {
|
||||
@ -107,6 +115,8 @@ fn interpreted_hash(u: &H256) -> String {
|
||||
|
||||
impl fmt::Display for AccountDiff {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use util::bytes::*;
|
||||
|
||||
match self.nonce {
|
||||
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))),
|
||||
|
44
ethcore/src/types/block_import_error.rs
Normal file
44
ethcore/src/types/block_import_error.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
38
ethcore/src/types/block_queue_info.rs
Normal file
38
ethcore/src/types/block_queue_info.rs
Normal 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,
|
||||
}
|
32
ethcore/src/types/call_analytics.rs
Normal file
32
ethcore/src/types/call_analytics.rs
Normal 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,
|
||||
}
|
@ -27,7 +27,7 @@ use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Transaction execution receipt.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Binary)]
|
||||
pub struct Executed {
|
||||
/// Gas paid up front for execution of transaction.
|
||||
pub gas: U256,
|
||||
|
@ -20,8 +20,12 @@ use util::hash::*;
|
||||
use util::sha3::*;
|
||||
use client::BlockID;
|
||||
use log_entry::LogEntry;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Blockchain Filter.
|
||||
#[derive(Binary)]
|
||||
pub struct Filter {
|
||||
/// Blockchain will be searched from this block.
|
||||
pub from_block: BlockID,
|
||||
@ -39,12 +43,17 @@ pub struct Filter {
|
||||
///
|
||||
/// If None, match all.
|
||||
/// 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 {
|
||||
fn clone(&self) -> Self {
|
||||
let mut topics = [None, None, None, None];
|
||||
let mut topics = [
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
];
|
||||
for i in 0..4 {
|
||||
topics[i] = self.topics[i].clone();
|
||||
}
|
||||
@ -53,7 +62,7 @@ impl Clone for Filter {
|
||||
from_block: self.from_block.clone(),
|
||||
to_block: self.to_block.clone(),
|
||||
address: self.address.clone(),
|
||||
topics: topics
|
||||
topics: topics[..].to_vec()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +80,7 @@ impl Filter {
|
||||
_ => vec![H2048::new()]
|
||||
};
|
||||
|
||||
self.topics.iter().fold(blooms, | bs, topic | match *topic {
|
||||
self.topics.iter().fold(blooms, |bs, topic| match *topic {
|
||||
None => bs,
|
||||
Some(ref topics) => bs.into_iter().flat_map(|bloom| {
|
||||
topics.into_iter().map(|topic| {
|
||||
@ -111,7 +120,7 @@ mod tests {
|
||||
from_block: BlockID::Earliest,
|
||||
to_block: BlockID::Latest,
|
||||
address: None,
|
||||
topics: [None, None, None, None]
|
||||
topics: vec![None, None, None, None],
|
||||
};
|
||||
|
||||
let possibilities = none_filter.bloom_possibilities();
|
||||
@ -126,9 +135,11 @@ mod tests {
|
||||
from_block: BlockID::Earliest,
|
||||
to_block: BlockID::Latest,
|
||||
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
||||
topics: [
|
||||
topics: vec![
|
||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
||||
None, None, None
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
]
|
||||
};
|
||||
|
||||
@ -142,10 +153,11 @@ mod tests {
|
||||
from_block: BlockID::Earliest,
|
||||
to_block: BlockID::Latest,
|
||||
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,
|
||||
]
|
||||
};
|
||||
|
||||
@ -162,7 +174,7 @@ mod tests {
|
||||
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
|
||||
Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
|
||||
]),
|
||||
topics: [
|
||||
topics: vec![
|
||||
Some(vec![
|
||||
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
|
||||
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()
|
||||
@ -188,10 +200,11 @@ mod tests {
|
||||
from_block: BlockID::Earliest,
|
||||
to_block: BlockID::Latest,
|
||||
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
|
||||
topics: [
|
||||
topics: vec![
|
||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
|
||||
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]),
|
||||
None, None
|
||||
None,
|
||||
None,
|
||||
]
|
||||
};
|
||||
|
@ -47,6 +47,7 @@ pub enum TransactionID {
|
||||
}
|
||||
|
||||
/// Uniquely identifies Trace.
|
||||
#[derive(Binary)]
|
||||
pub struct TraceId {
|
||||
/// Transaction
|
||||
pub transaction: TransactionID,
|
||||
@ -55,10 +56,10 @@ pub struct TraceId {
|
||||
}
|
||||
|
||||
/// Uniquely identifies Uncle.
|
||||
#[derive(Debug)]
|
||||
pub struct UncleID (
|
||||
#[derive(Debug, Binary)]
|
||||
pub struct UncleID {
|
||||
/// Block id.
|
||||
pub BlockID,
|
||||
pub block: BlockID,
|
||||
/// Position in block.
|
||||
pub usize
|
||||
);
|
||||
pub position: usize
|
||||
}
|
||||
|
@ -25,3 +25,9 @@ pub mod executed;
|
||||
pub mod block_status;
|
||||
pub mod account_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;
|
||||
|
@ -16,24 +16,32 @@
|
||||
|
||||
//! State diff module.
|
||||
|
||||
use util::*;
|
||||
use util::numbers::*;
|
||||
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
|
||||
/// 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 {
|
||||
/// Get the actual data.
|
||||
pub fn get(&self) -> &BTreeMap<Address, AccountDiff> {
|
||||
&self.0
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StateDiff {
|
||||
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));
|
||||
}
|
||||
Ok(())
|
||||
@ -44,6 +52,6 @@ impl Deref for StateDiff {
|
||||
type Target = BTreeMap<Address, AccountDiff>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
35
ethcore/src/types/trace_filter.rs
Normal file
35
ethcore/src/types/trace_filter.rs
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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>,
|
||||
}
|
52
ethcore/src/types/transaction_import.rs
Normal file
52
ethcore/src/types/transaction_import.rs
Normal 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)),
|
||||
}
|
||||
}
|
||||
}
|
@ -17,9 +17,12 @@
|
||||
//! Tree route info type definition
|
||||
|
||||
use util::numbers::H256;
|
||||
use ipc::BinaryConvertError;
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
|
||||
/// Represents a tree route between `from` block and `to` block:
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Binary)]
|
||||
pub struct TreeRoute {
|
||||
/// A vector of hashes of all blocks, ordered from `from` to `to`.
|
||||
pub blocks: Vec<H256>,
|
||||
|
@ -139,6 +139,11 @@ impl<'a> BlockView<'a> {
|
||||
pub fn uncle_at(&self, index: usize) -> Option<Header> {
|
||||
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> {
|
||||
|
@ -22,5 +22,5 @@ aster = { version = "0.17", default-features = false }
|
||||
clippy = { version = "^0.*", optional = true }
|
||||
quasi = { version = "0.11", default-features = false }
|
||||
quasi_macros = { version = "0.11", optional = true }
|
||||
syntex = { version = "*", optional = true }
|
||||
syntex_syntax = { version = "*", optional = true }
|
||||
syntex = { version = "0.33", optional = true }
|
||||
syntex_syntax = { version = "0.33", optional = true }
|
||||
|
@ -201,15 +201,20 @@ fn implement_dispatch_arm_invoke_stmt(
|
||||
{
|
||||
let _sp = ext_cx.call_site();
|
||||
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::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::ModSep));
|
||||
tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary"))));
|
||||
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("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::BinOp(::syntax::parse::token::And)));
|
||||
|
||||
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::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::Ident(ext_cx.ident_of("binary"))));
|
||||
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("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::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::Dot));
|
||||
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)));
|
||||
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::OpenDelim(::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::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::OpenDelim(::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
|
||||
})).unwrap()
|
||||
}
|
||||
@ -497,9 +515,9 @@ fn client_generics(builder: &aster::AstBuilder, interface_map: &InterfaceMap) ->
|
||||
.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);
|
||||
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()
|
||||
.build()
|
||||
}
|
||||
@ -515,7 +533,7 @@ fn client_phantom_ident(builder: &aster::AstBuilder, interface_map: &InterfaceMa
|
||||
/// for say `Service` it generates `ServiceClient`
|
||||
fn push_client_struct(cx: &ExtCtxt, builder: &aster::AstBuilder, interface_map: &InterfaceMap, push: &mut FnMut(Annotatable)) {
|
||||
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 client_struct_item = quote_item!(cx,
|
||||
@ -547,9 +565,9 @@ fn push_with_socket_client_implementation(
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
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 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,
|
||||
impl $generics ::ipc::WithSocket<S> for $client_ident $where_clause {
|
||||
@ -578,7 +596,7 @@ fn push_client_implementation(
|
||||
.collect::<Vec<P<ast::ImplItem>>>();
|
||||
|
||||
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 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 {
|
||||
pub original_item: Item,
|
||||
pub item: P<ast::Item>,
|
||||
@ -700,8 +763,13 @@ impl IdentMap {
|
||||
builder.id(format!("{}", ::syntax::print::pprust::path_to_string(&self.original_path)))
|
||||
}
|
||||
|
||||
fn client_ident(&self, builder: &aster::AstBuilder) -> Ident {
|
||||
builder.id(format!("{}Client", self.original_path.segments[0].identifier))
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
fn qualified_ident(&self, builder: &aster::AstBuilder) -> Ident {
|
||||
|
@ -50,11 +50,36 @@ include!("lib.rs.in");
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
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_attribute)");
|
||||
|
||||
reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation);
|
||||
reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation);
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
@ -67,4 +92,6 @@ pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
syntax::parse::token::intern("derive_Binary"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(serialization::expand_serialization_implementation)));
|
||||
|
||||
reg.register_attribute("ipc".to_owned(), AttributeType::Normal);
|
||||
}
|
||||
|
@ -707,7 +707,12 @@ fn binary_expr_variant(
|
||||
let buffer = &mut buffer[1..];
|
||||
$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, _) => {
|
||||
@ -742,7 +747,12 @@ fn binary_expr_variant(
|
||||
let buffer = &mut buffer[1..];
|
||||
$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
Loading…
Reference in New Issue
Block a user