Merge branch 'master' of github.com:ethcore/parity into fatdb

This commit is contained in:
debris 2016-07-01 10:17:08 +02:00
commit 9f5abd01b5
80 changed files with 1724 additions and 886 deletions

104
Cargo.lock generated
View File

@ -20,7 +20,7 @@ dependencies = [
"ethsync 1.3.0", "ethsync 1.3.0",
"fdlimit 0.1.0", "fdlimit 0.1.0",
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"json-ipc-server 0.2.3 (git+https://github.com/ethcore/json-ipc-server.git)", "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -164,7 +164,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -261,6 +261,7 @@ dependencies = [
"ethjson 0.1.0", "ethjson 0.1.0",
"ethstore 0.1.0", "ethstore 0.1.0",
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -278,21 +279,21 @@ dependencies = [
"clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-rpc 1.3.0", "ethcore-rpc 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"hyper 0.9.3 (git+https://github.com/ethcore/hyper)", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
"parity-dapps-builtins 0.5.1 (git+https://github.com/ethcore/parity-dapps-builtins-rs.git)", "parity-dapps-builtins 0.5.2 (git+https://github.com/ethcore/parity-dapps-builtins-rs.git)",
"parity-dapps-status 0.5.0 (git+https://github.com/ethcore/parity-dapps-status-rs.git)", "parity-dapps-status 0.5.1 (git+https://github.com/ethcore/parity-dapps-status-rs.git)",
"parity-dapps-wallet 0.6.1 (git+https://github.com/ethcore/parity-dapps-wallet-rs.git)", "parity-dapps-wallet 0.6.1 (git+https://github.com/ethcore/parity-dapps-wallet-rs.git)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -344,7 +345,7 @@ dependencies = [
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethsync 1.3.0", "ethsync 1.3.0",
"json-ipc-server 0.2.3 (git+https://github.com/ethcore/json-ipc-server.git)", "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -366,16 +367,17 @@ dependencies = [
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-minimal-sysui 0.1.0 (git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git)", "parity-minimal-sysui 0.2.0 (git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"ws 0.5.1 (git+https://github.com/ethcore/ws-rs.git)", "ws 0.5.0 (git+https://github.com/ethcore/ws-rs.git?branch=stable)",
] ]
[[package]] [[package]]
name = "ethcore-util" name = "ethcore-util"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 0.1.0", "bigint 0.1.0",
"chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
@ -546,23 +548,22 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.9.3" version = "0.9.4"
source = "git+https://github.com/ethcore/hyper#dbb4cf160ebf242f7f0459d547c40e9e6877ccf4" source = "git+https://github.com/ethcore/hyper#9e346c1d4bc30cd4142dea9d8a0b117d30858ca4"
dependencies = [ dependencies = [
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rotor 0.6.3 (git+https://github.com/ethcore/rotor)",
"rotor 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"spmc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -595,8 +596,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "json-ipc-server" name = "json-ipc-server"
version = "0.2.3" version = "0.2.4"
source = "git+https://github.com/ethcore/json-ipc-server.git#bfe16b66b2e9412d153b1ea53bc078d74037da7f" source = "git+https://github.com/ethcore/json-ipc-server.git#902b031b8f50a59ecb4f389cbec1d264a98556bc"
dependencies = [ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -630,9 +631,9 @@ dependencies = [
[[package]] [[package]]
name = "jsonrpc-http-server" name = "jsonrpc-http-server"
version = "5.1.0" version = "5.1.0"
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#0c99d308bc15e8fae50642eff77a3e1fd7610652" source = "git+https://github.com/ethcore/jsonrpc-http-server.git#e59c2fbaca499620874023755cb91f05b858ffe7"
dependencies = [ dependencies = [
"hyper 0.9.3 (git+https://github.com/ethcore/hyper)", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -712,26 +713,10 @@ dependencies = [
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "mio"
version = "0.5.0"
source = "git+https://github.com/carllerche/mio.git#f4aa49a9d2c4507fb33a4533d5238e0365f67c99"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.5.0-pre (git+https://github.com/carllerche/nix-rust?rev=c4257f8a76)",
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.5.1" version = "0.5.1"
source = "git+https://github.com/ethcore/mio?branch=v0.5.x#1fc881771fb8c2517317b4f805d7b88235be422b" source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd"
dependencies = [ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -801,15 +786,6 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "nix"
version = "0.5.0-pre"
source = "git+https://github.com/carllerche/nix-rust?rev=c4257f8a76#c4257f8a76b69b0d2e9a001d83e4bef67c03b23f"
dependencies = [
"bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.5.0" version = "0.5.0"
@ -935,16 +911,16 @@ dependencies = [
[[package]] [[package]]
name = "parity-dapps-builtins" name = "parity-dapps-builtins"
version = "0.5.1" version = "0.5.2"
source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#7408838e8ca3b57c6b0cf5da2e31e0e275959955" source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#01af2091d5d70dfe0aecbfd96308f0ae79fc61e6"
dependencies = [ dependencies = [
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
] ]
[[package]] [[package]]
name = "parity-dapps-status" name = "parity-dapps-status"
version = "0.5.0" version = "0.5.1"
source = "git+https://github.com/ethcore/parity-dapps-status-rs.git#0cdd3512004e403aff7da3b8c16ba0bf5d6c911c" source = "git+https://github.com/ethcore/parity-dapps-status-rs.git#110ef2e66142ec8dc15fc40b8ddda5ed3bcfc1fb"
dependencies = [ dependencies = [
"parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)",
] ]
@ -959,8 +935,8 @@ dependencies = [
[[package]] [[package]]
name = "parity-minimal-sysui" name = "parity-minimal-sysui"
version = "0.1.0" version = "0.2.0"
source = "git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git#cc5ea4bd786982f0509a8d3d5deb4217c659af85" source = "git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git#996c9f3f0ebedc727aecb4ece191154e956ae292"
[[package]] [[package]]
name = "phf" name = "phf"
@ -1057,7 +1033,7 @@ dependencies = [
[[package]] [[package]]
name = "quick-error" name = "quick-error"
version = "0.2.2" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -1103,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rocksdb" name = "rocksdb"
version = "0.4.5" version = "0.4.5"
source = "git+https://github.com/ethcore/rust-rocksdb#6f3c68f5f075433d206be4af6a620651cd9f8541" source = "git+https://github.com/ethcore/rust-rocksdb#9be41e05923616dfa28741c58b22776d479751e6"
dependencies = [ dependencies = [
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
@ -1112,7 +1088,7 @@ dependencies = [
[[package]] [[package]]
name = "rocksdb-sys" name = "rocksdb-sys"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/ethcore/rust-rocksdb#6f3c68f5f075433d206be4af6a620651cd9f8541" source = "git+https://github.com/ethcore/rust-rocksdb#9be41e05923616dfa28741c58b22776d479751e6"
dependencies = [ dependencies = [
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1121,12 +1097,11 @@ dependencies = [
[[package]] [[package]]
name = "rotor" name = "rotor"
version = "0.6.3" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/ethcore/rotor#e63d45137b2eb66d1e085a7c6321a5db8b187576"
dependencies = [ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
"quick-error 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1249,7 +1224,7 @@ dependencies = [
[[package]] [[package]]
name = "spmc" name = "spmc"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -1408,7 +1383,7 @@ dependencies = [
[[package]] [[package]]
name = "url" name = "url"
version = "1.0.0" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1472,16 +1447,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "ws" name = "ws"
version = "0.5.1" version = "0.5.0"
source = "git+https://github.com/ethcore/ws-rs.git#d5745df8ea1ab82cd2b844f15ca1ac759e7aa9f5" source = "git+https://github.com/ethcore/ws-rs.git?branch=stable#a876fc115c3ef50a17c8822c9bd2f6e94473e005"
dependencies = [ dependencies = [
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.0 (git+https://github.com/carllerche/mio.git)", "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]

View File

@ -1,13 +1,27 @@
environment: environment:
matrix: matrix:
- TARGET: x86_64-pc-windows-msvc - TARGET: x86_64-pc-windows-msvc
cert:
secure: ESPpYVVAMG1fbJx6kq4ct/g9SQTXac4Hs6xXr6Oh4Zrk2dwYglNjxmzErdPnvu7gs/gekzrJ6KEQHYRc+5+4dKg6rRADQ681NLVx9vOggBs=
certpass:
secure: 0BgXJqxq9Ei34/hZ7121FQ==
keyfile: C:\users\appveyor\Certificates.p12
branches:
only:
- master
- /^beta-.*$/
- /^stable-.*$/
- /^beta$/
- /^stable$/
install: install:
- git submodule update --init --recursive - git submodule update --init --recursive
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-x86_64-pc-windows-msvc.exe" - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-x86_64-pc-windows-msvc.exe"
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll - ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe - ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe
- rust-1.9.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - rust-1.9.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
- rustc -V - rustc -V
- cargo -V - cargo -V
@ -18,7 +32,10 @@ test_script:
after_test: after_test:
- cargo build --verbose --release - cargo build --verbose --release
- ps: if($env:cert) { Start-FileDownload $env:cert -FileName $env:keyfile }
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass target\release\parity.exe }
- makensis.exe nsis\installer.nsi - makensis.exe nsis\installer.nsi
- ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass nsis\installer.exe }
artifacts: artifacts:
- path: nsis\installer.exe - path: nsis\installer.exe

View File

@ -22,8 +22,8 @@ ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
parity-dapps = { git = "https://github.com/ethcore/parity-dapps-rs.git", version = "0.3" } parity-dapps = { git = "https://github.com/ethcore/parity-dapps-rs.git", version = "0.3" }
# List of apps # List of apps
parity-dapps-status = { git = "https://github.com/ethcore/parity-dapps-status-rs.git", version = "0.5.0" } parity-dapps-status = { git = "https://github.com/ethcore/parity-dapps-status-rs.git", version = "0.5.1" }
parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtins-rs.git", version = "0.5.0" } parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtins-rs.git", version = "0.5.2" }
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.6.0", optional = true } parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.6.0", optional = true }
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true } parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true }
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true } parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true }

View File

@ -42,11 +42,11 @@ pub struct EndpointInfo {
pub trait Endpoint : Send + Sync { pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<&EndpointInfo> { None } fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream>>; fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream> + Send>;
} }
pub type Endpoints = BTreeMap<String, Box<Endpoint>>; pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
pub type Handler = server::Handler<HttpStream>; pub type Handler = server::Handler<HttpStream> + Send;
pub struct ContentHandler { pub struct ContentHandler {
content: String, content: String,
@ -65,7 +65,7 @@ impl ContentHandler {
} }
impl server::Handler<HttpStream> for ContentHandler { impl server::Handler<HttpStream> for ContentHandler {
fn on_request(&mut self, _request: server::Request) -> Next { fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write() Next::write()
} }

View File

@ -132,9 +132,16 @@ impl Server {
special.clone(), special.clone(),
authorization.clone(), authorization.clone(),
)) ))
.map(|l| Server { .map(|(l, srv)| {
server: Some(l),
panic_handler: panic_handler, ::std::thread::spawn(move || {
srv.run();
});
Server {
server: Some(l),
panic_handler: panic_handler,
}
}) })
.map_err(ServerError::from) .map_err(ServerError::from)
} }

View File

@ -86,7 +86,7 @@ impl<T: Dapp> PageHandler<T> {
} }
impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> { impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
fn on_request(&mut self, req: server::Request) -> Next { fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
self.file = match *req.uri() { self.file = match *req.uri() {
RequestUri::AbsolutePath(ref path) => { RequestUri::AbsolutePath(ref path) => {
self.app.file(&self.extract_path(path)) self.app.file(&self.extract_path(path))

View File

@ -27,13 +27,13 @@ pub enum Authorized {
/// Authorization was successful. /// Authorization was successful.
Yes, Yes,
/// Unsuccessful authorization. Handler for further work is returned. /// Unsuccessful authorization. Handler for further work is returned.
No(Box<server::Handler<HttpStream>>), No(Box<server::Handler<HttpStream> + Send>),
} }
/// Authorization interface /// Authorization interface
pub trait Authorization : Send + Sync { pub trait Authorization : Send + Sync {
/// Checks if authorization is valid. /// Checks if authorization is valid.
fn is_authorized(&self, req: &server::Request)-> Authorized; fn is_authorized(&self, req: &server::Request<HttpStream>)-> Authorized;
} }
/// HTTP Basic Authorization handler /// HTTP Basic Authorization handler
@ -45,13 +45,13 @@ pub struct HttpBasicAuth {
pub struct NoAuth; pub struct NoAuth;
impl Authorization for NoAuth { impl Authorization for NoAuth {
fn is_authorized(&self, _req: &server::Request)-> Authorized { fn is_authorized(&self, _req: &server::Request<HttpStream>)-> Authorized {
Authorized::Yes Authorized::Yes
} }
} }
impl Authorization for HttpBasicAuth { impl Authorization for HttpBasicAuth {
fn is_authorized(&self, req: &server::Request) -> Authorized { fn is_authorized(&self, req: &server::Request<HttpStream>) -> Authorized {
let auth = self.check_auth(&req); let auth = self.check_auth(&req);
match auth { match auth {
@ -89,7 +89,7 @@ impl HttpBasicAuth {
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password) self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
} }
fn check_auth(&self, req: &server::Request) -> Access { fn check_auth(&self, req: &server::Request<HttpStream>) -> Access {
match req.headers().get::<header::Authorization<header::Basic>>() { match req.headers().get::<header::Authorization<header::Basic>>() {
Some(&header::Authorization( Some(&header::Authorization(
header::Basic { ref username, password: Some(ref password) } header::Basic { ref username, password: Some(ref password) }
@ -105,7 +105,7 @@ pub struct UnauthorizedHandler {
} }
impl server::Handler<HttpStream> for UnauthorizedHandler { impl server::Handler<HttpStream> for UnauthorizedHandler {
fn on_request(&mut self, _request: server::Request) -> Next { fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write() Next::write()
} }
@ -141,7 +141,7 @@ impl server::Handler<HttpStream> for UnauthorizedHandler {
pub struct AuthRequiredHandler; pub struct AuthRequiredHandler;
impl server::Handler<HttpStream> for AuthRequiredHandler { impl server::Handler<HttpStream> for AuthRequiredHandler {
fn on_request(&mut self, _request: server::Request) -> Next { fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write() Next::write()
} }

View File

@ -49,12 +49,12 @@ pub struct Router<A: Authorization + 'static> {
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>, special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>, authorization: Arc<A>,
handler: Box<server::Handler<HttpStream>>, handler: Box<server::Handler<HttpStream> + Send>,
} }
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> { impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
fn on_request(&mut self, req: server::Request) -> Next { fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
// Check authorization // Check authorization
let auth = self.authorization.is_authorized(&req); let auth = self.authorization.is_authorized(&req);
@ -124,7 +124,7 @@ impl<A: Authorization> Router<A> {
} }
} }
fn extract_url(req: &server::Request) -> Option<Url> { fn extract_url(req: &server::Request<HttpStream>) -> Option<Url> {
match *req.uri() { match *req.uri() {
uri::RequestUri::AbsoluteUri(ref url) => { uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) { match Url::from_generic_url(url.clone()) {

View File

@ -33,7 +33,7 @@ impl Redirection {
} }
impl server::Handler<HttpStream> for Redirection { impl server::Handler<HttpStream> for Redirection {
fn on_request(&mut self, _request: server::Request) -> Next { fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write() Next::write()
} }

View File

@ -20,6 +20,8 @@ g++ -v
# build parity # build parity
RUN git clone https://github.com/ethcore/parity && \ RUN git clone https://github.com/ethcore/parity && \
cd parity&&\ cd parity&&\
git checkout beta && \
git pull && \
ls -a&&\ ls -a&&\
cargo build --release --verbose && \ cargo build --release --verbose && \
ls /build/parity/target/release/parity && \ ls /build/parity/target/release/parity && \

View File

@ -36,6 +36,8 @@ ENV CC aarch64-linux-gnu-gcc
# build parity # build parity
RUN git clone https://github.com/ethcore/parity && \ RUN git clone https://github.com/ethcore/parity && \
cd parity && \ cd parity && \
git checkout beta && \
git pull && \
mkdir -p .cargo && \ mkdir -p .cargo && \
echo '[target.aarch64-unknown-linux-gnu]\n\ echo '[target.aarch64-unknown-linux-gnu]\n\
linker = "aarch64-linux-gnu-gcc"\n'\ linker = "aarch64-linux-gnu-gcc"\n'\

View File

@ -36,6 +36,8 @@ ENV CC arm-linux-gnueabihf-gcc
# build parity # build parity
RUN git clone https://github.com/ethcore/parity && \ RUN git clone https://github.com/ethcore/parity && \
cd parity && \ cd parity && \
git checkout beta && \
git pull && \
mkdir -p .cargo && \ mkdir -p .cargo && \
echo '[target.armv7-unknown-linux-gnueabihf]\n\ echo '[target.armv7-unknown-linux-gnueabihf]\n\
linker = "arm-linux-gnueabihf-gcc"\n'\ linker = "arm-linux-gnueabihf-gcc"\n'\

View File

@ -47,6 +47,8 @@ g++ -v
# build parity # build parity
RUN git clone https://github.com/ethcore/parity && \ RUN git clone https://github.com/ethcore/parity && \
cd parity && \ cd parity && \
git checkout beta && \
git pull && \
cargo build --release --features ethcore/jit --verbose && \ cargo build --release --features ethcore/jit --verbose && \
ls /build/parity/target/release/parity && \ ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity strip /build/parity/target/release/parity

View File

@ -27,7 +27,9 @@ g++ -v
# build parity # build parity
RUN git clone https://github.com/ethcore/parity && \ RUN git clone https://github.com/ethcore/parity && \
cd parity && \ cd parity && \
git checkout beta && \
git pull && \
cargo build --release --verbose && \ cargo build --release --verbose && \
ls /build/parity/target/release/parity && \ ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity strip /build/parity/target/release/parity
RUN file /build/parity/target/release/parity RUN file /build/parity/target/release/parity

View File

@ -32,6 +32,10 @@ bloomchain = "0.1"
rayon = "0.3.1" rayon = "0.3.1"
ethstore = { path = "../ethstore" } ethstore = { path = "../ethstore" }
[dependencies.hyper]
git = "https://github.com/ethcore/hyper"
default-features = false
[features] [features]
jit = ["evmjit"] jit = ["evmjit"]
evm-debug = [] evm-debug = []

View File

@ -108,6 +108,12 @@ impl Account {
self.code_cache = code; self.code_cache = code;
} }
/// Reset this account's code to the given code.
pub fn reset_code(&mut self, code: Bytes) {
self.code_hash = None;
self.init_code(code);
}
/// Set (and cache) the contents of the trie's storage at `key` to `value`. /// Set (and cache) the contents of the trie's storage at `key` to `value`.
pub fn set_storage(&mut self, key: H256, value: H256) { pub fn set_storage(&mut self, key: H256, value: H256) {
self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value)); self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
@ -336,6 +342,21 @@ mod tests {
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
} }
#[test]
fn reset_code() {
let mut a = Account::new_contract(69.into(), 0.into());
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.init_code(vec![0x55, 0x44, 0xffu8]);
assert_eq!(a.code_hash(), SHA3_EMPTY);
a.commit_code(&mut db);
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
a.reset_code(vec![0x55]);
assert_eq!(a.code_hash(), SHA3_EMPTY);
a.commit_code(&mut db);
assert_eq!(a.code_hash().hex(), "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be");
}
#[test] #[test]
fn rlpio() { fn rlpio() {
let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new());
@ -348,7 +369,6 @@ mod tests {
#[test] #[test]
fn new_account() { fn new_account() {
let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new());
assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
assert_eq!(a.balance(), &U256::from(69u8)); assert_eq!(a.balance(), &U256::from(69u8));
@ -359,7 +379,6 @@ mod tests {
#[test] #[test]
fn create_account() { fn create_account() {
let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new());
assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
} }

View File

@ -164,6 +164,12 @@ pub trait IsBlock {
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles } fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
} }
/// Trait for a object that has a state database.
pub trait Drain {
/// Drop this object and return the underlieing database.
fn drain(self) -> Box<JournalDB>;
}
impl IsBlock for ExecutedBlock { impl IsBlock for ExecutedBlock {
fn block(&self) -> &ExecutedBlock { self } fn block(&self) -> &ExecutedBlock { self }
} }
@ -436,9 +442,11 @@ impl LockedBlock {
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }), _ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
} }
} }
}
impl Drain for LockedBlock {
/// Drop this object and return the underlieing database. /// Drop this object and return the underlieing database.
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 } fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
} }
impl SealedBlock { impl SealedBlock {
@ -450,9 +458,11 @@ impl SealedBlock {
block_rlp.append_raw(&self.uncle_bytes, 1); block_rlp.append_raw(&self.uncle_bytes, 1);
block_rlp.out() block_rlp.out()
} }
}
impl Drain for SealedBlock {
/// Drop this object and return the underlieing database. /// Drop this object and return the underlieing database.
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 } fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
} }
impl IsBlock for SealedBlock { impl IsBlock for SealedBlock {

View File

@ -16,7 +16,6 @@
//! Blockchain database client. //! Blockchain database client.
use std::marker::PhantomData;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use util::*; use util::*;
@ -30,7 +29,8 @@ use engine::Engine;
use views::HeaderView; use views::HeaderView;
use service::{NetSyncMessage, SyncMessage}; use service::{NetSyncMessage, SyncMessage};
use env_info::LastHashes; use env_info::LastHashes;
use verification::*; use verification;
use verification::{PreverifiedBlock, Verifier};
use block::*; use block::*;
use transaction::{LocalizedTransaction, SignedTransaction, Action}; use transaction::{LocalizedTransaction, SignedTransaction, Action};
use blockchain::extras::TransactionAddress; use blockchain::extras::TransactionAddress;
@ -38,7 +38,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics}; use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics};
use client::Error as ClientError; use client::Error as ClientError;
use env_info::EnvInfo; use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address}; use executive::{Executive, Executed, TransactOptions, contract_address};
@ -83,7 +83,7 @@ impl ClientReport {
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
pub struct Client<V = CanonVerifier> where V: Verifier { pub struct Client {
chain: Arc<BlockChain>, chain: Arc<BlockChain>,
tracedb: Arc<TraceDB<BlockChain>>, tracedb: Arc<TraceDB<BlockChain>>,
engine: Arc<Box<Engine>>, engine: Arc<Box<Engine>>,
@ -92,7 +92,7 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
report: RwLock<ClientReport>, report: RwLock<ClientReport>,
import_lock: Mutex<()>, import_lock: Mutex<()>,
panic_handler: Arc<PanicHandler>, panic_handler: Arc<PanicHandler>,
verifier: PhantomData<V>, verifier: Box<Verifier>,
vm_factory: Arc<EvmFactory>, vm_factory: Arc<EvmFactory>,
miner: Arc<Miner>, miner: Arc<Miner>,
io_channel: IoChannel<NetSyncMessage>, io_channel: IoChannel<NetSyncMessage>,
@ -107,13 +107,6 @@ const HISTORY: u64 = 1200;
// of which you actually want force an upgrade. // of which you actually want force an upgrade.
const CLIENT_DB_VER_STR: &'static str = "5.3"; const CLIENT_DB_VER_STR: &'static str = "5.3";
impl Client<CanonVerifier> {
/// Create a new client with given spec and DB path.
pub fn new(config: ClientConfig, spec: Spec, path: &Path, miner: Arc<Miner>, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, ClientError> {
Client::<CanonVerifier>::new_with_verifier(config, spec, path, miner, message_channel)
}
}
/// Get the path for the databases given the root path and information on the databases. /// Get the path for the databases given the root path and information on the databases.
pub fn get_db_path(path: &Path, pruning: journaldb::Algorithm, genesis_hash: H256) -> PathBuf { pub fn get_db_path(path: &Path, pruning: journaldb::Algorithm, genesis_hash: H256) -> PathBuf {
let mut dir = path.to_path_buf(); let mut dir = path.to_path_buf();
@ -131,25 +124,35 @@ pub fn append_path(path: &Path, item: &str) -> String {
p.to_str().unwrap().to_owned() p.to_str().unwrap().to_owned()
} }
impl<V> Client<V> where V: Verifier { impl Client {
/// Create a new client with given spec and DB path and custom verifier. /// Create a new client with given spec and DB path and custom verifier.
pub fn new_with_verifier( pub fn new(
config: ClientConfig, config: ClientConfig,
spec: Spec, spec: Spec,
path: &Path, path: &Path,
miner: Arc<Miner>, miner: Arc<Miner>,
message_channel: IoChannel<NetSyncMessage>) message_channel: IoChannel<NetSyncMessage>)
-> Result<Arc<Client<V>>, ClientError> -> Result<Arc<Client>, ClientError>
{ {
let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
let gb = spec.genesis_block(); let gb = spec.genesis_block();
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone()))); let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone())));
let mut state_db_config = match config.db_cache_size {
None => DatabaseConfig::default(),
Some(cache_size) => DatabaseConfig::with_cache(cache_size),
};
if config.db_compaction == DatabaseCompactionProfile::HDD {
state_db_config = state_db_config.compaction(CompactionProfile::hdd());
}
let mut state_db = journaldb::new( let mut state_db = journaldb::new(
&append_path(&path, "state"), &append_path(&path, "state"),
config.pruning, config.pruning,
config.db_cache_size); state_db_config
);
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) { if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB"); state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
@ -170,7 +173,7 @@ impl<V> Client<V> where V: Verifier {
report: RwLock::new(Default::default()), report: RwLock::new(Default::default()),
import_lock: Mutex::new(()), import_lock: Mutex::new(()),
panic_handler: panic_handler, panic_handler: panic_handler,
verifier: PhantomData, verifier: verification::new(config.verifier_type),
vm_factory: Arc::new(EvmFactory::new(config.vm_type)), vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
miner: miner, miner: miner,
io_channel: message_channel, io_channel: message_channel,
@ -212,7 +215,7 @@ impl<V> Client<V> where V: Verifier {
} }
// Verify Block Family // Verify Block Family
let verify_family_result = V::verify_block_family(&header, &block.bytes, engine, self.chain.deref()); let verify_family_result = self.verifier.verify_block_family(&header, &block.bytes, engine, self.chain.deref());
if let Err(e) = verify_family_result { if let Err(e) = verify_family_result {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
@ -238,7 +241,7 @@ impl<V> Client<V> where V: Verifier {
// Final Verification // Final Verification
let locked_block = enact_result.unwrap(); let locked_block = enact_result.unwrap();
if let Err(e) = V::verify_block_final(&header, locked_block.block().header()) { if let Err(e) = self.verifier.verify_block_final(&header, locked_block.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
} }
@ -246,7 +249,7 @@ impl<V> Client<V> where V: Verifier {
Ok(locked_block) Ok(locked_block)
} }
fn calculate_enacted_retracted(&self, import_results: Vec<ImportRoute>) -> (Vec<H256>, Vec<H256>) { fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
fn map_to_vec(map: Vec<(H256, bool)>) -> Vec<H256> { fn map_to_vec(map: Vec<(H256, bool)>) -> Vec<H256> {
map.into_iter().map(|(k, _v)| k).collect() map.into_iter().map(|(k, _v)| k).collect()
} }
@ -256,12 +259,12 @@ impl<V> Client<V> where V: Verifier {
// could be retracted in import `k+1`. This is why to understand if after all inserts // could be retracted in import `k+1`. This is why to understand if after all inserts
// the block is enacted or retracted we iterate over all routes and at the end final state // the block is enacted or retracted we iterate over all routes and at the end final state
// will be in the hashmap // will be in the hashmap
let map = import_results.into_iter().fold(HashMap::new(), |mut map, route| { let map = import_results.iter().fold(HashMap::new(), |mut map, route| {
for hash in route.enacted { for hash in &route.enacted {
map.insert(hash, true); map.insert(hash.clone(), true);
} }
for hash in route.retracted { for hash in &route.retracted {
map.insert(hash, false); map.insert(hash.clone(), false);
} }
map map
}); });
@ -296,38 +299,12 @@ impl<V> Client<V> where V: Verifier {
let closed_block = self.check_and_close_block(&block); let closed_block = self.check_and_close_block(&block);
if let Err(_) = closed_block { if let Err(_) = closed_block {
invalid_blocks.insert(header.hash()); invalid_blocks.insert(header.hash());
break; continue;
} }
let closed_block = closed_block.unwrap();
imported_blocks.push(header.hash()); imported_blocks.push(header.hash());
// Are we committing an era? let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
let ancient = if header.number() >= HISTORY {
let n = header.number() - HISTORY;
Some((n, self.chain.block_hash(n).unwrap()))
} else {
None
};
// Commit results
let closed_block = closed_block.unwrap();
let receipts = closed_block.block().receipts().clone();
let traces = From::from(closed_block.block().traces().clone().unwrap_or_else(Vec::new));
closed_block.drain()
.commit(header.number(), &header.hash(), ancient)
.expect("State DB commit failed.");
// And update the chain after commit to prevent race conditions
// (when something is in chain but you are not able to fetch details)
let route = self.chain.insert_block(&block.bytes, receipts);
self.tracedb.import(TraceImportRequest {
traces: traces,
block_hash: header.hash(),
block_number: header.number(),
enacted: route.enacted.clone(),
retracted: route.retracted.len()
});
import_results.push(route); import_results.push(route);
self.report.write().unwrap().accrue_block(&block); self.report.write().unwrap().accrue_block(&block);
@ -348,7 +325,7 @@ impl<V> Client<V> where V: Verifier {
{ {
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() { if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
let (enacted, retracted) = self.calculate_enacted_retracted(import_results); let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
if self.queue_info().is_empty() { if self.queue_info().is_empty() {
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted); self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
@ -359,19 +336,50 @@ impl<V> Client<V> where V: Verifier {
invalid: invalid_blocks, invalid: invalid_blocks,
enacted: enacted, enacted: enacted,
retracted: retracted, retracted: retracted,
})).unwrap(); sealed: Vec::new(),
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
} }
} }
{ if self.chain_info().best_block_hash != original_best {
if self.chain_info().best_block_hash != original_best { self.miner.update_sealing(self);
self.miner.update_sealing(self);
}
} }
imported imported
} }
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain {
let number = block.header().number();
// Are we committing an era?
let ancient = if number >= HISTORY {
let n = number - HISTORY;
Some((n, self.chain.block_hash(n).unwrap()))
} else {
None
};
// Commit results
let receipts = block.receipts().clone();
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
// CHECK! I *think* this is fine, even if the state_root is equal to another
// already-imported block of the same number.
// TODO: Prove it with a test.
block.drain().commit(number, hash, ancient).expect("State DB commit failed.");
// And update the chain after commit to prevent race conditions
// (when something is in chain but you are not able to fetch details)
let route = self.chain.insert_block(block_data, receipts);
self.tracedb.import(TraceImportRequest {
traces: traces,
block_hash: hash.clone(),
block_number: number,
enacted: route.enacted.clone(),
retracted: route.retracted.len()
});
route
}
/// Import transactions from the IO queue /// Import transactions from the IO queue
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize { pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
let _timer = PerfTimer::new("import_queued_transactions"); let _timer = PerfTimer::new("import_queued_transactions");
@ -473,7 +481,7 @@ impl<V> Client<V> where V: Verifier {
} }
} }
impl<V> BlockChainClient for Client<V> where V: Verifier { impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap(); let header = self.block_header(BlockID::Latest).unwrap();
let view = HeaderView::new(&header); let view = HeaderView::new(&header);
@ -512,9 +520,6 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
ret ret
} }
fn vm_factory(&self) -> &EvmFactory {
&self.vm_factory
}
fn block_header(&self, id: BlockID) -> Option<Bytes> { fn block_header(&self, id: BlockID) -> Option<Bytes> {
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec())) Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
@ -791,12 +796,12 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
} }
} }
fn all_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.all_transactions() self.miner.pending_transactions()
} }
} }
impl<V> MiningBlockChainClient for Client<V> where V: Verifier { impl MiningBlockChainClient for Client {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = self.engine.deref().deref(); let engine = self.engine.deref().deref();
let h = self.chain.best_block_hash(); let h = self.chain.best_block_hash();
@ -826,6 +831,43 @@ impl<V> MiningBlockChainClient for Client<V> where V: Verifier {
open_block open_block
} }
fn vm_factory(&self) -> &EvmFactory {
&self.vm_factory
}
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
let _import_lock = self.import_lock.lock();
let _timer = PerfTimer::new("import_sealed_block");
let original_best = self.chain_info().best_block_hash;
let h = block.header().hash();
let number = block.header().number();
let block_data = block.rlp_bytes();
let route = self.commit_block(block, &h, &block_data);
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
{
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
self.io_channel.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
imported: vec![h.clone()],
invalid: vec![],
enacted: enacted,
retracted: retracted,
sealed: vec![h.clone()],
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
}
if self.chain_info().best_block_hash != original_best {
self.miner.update_sealing(self);
}
Ok(h)
}
} }
impl MayPanic for Client { impl MayPanic for Client {

View File

@ -18,8 +18,22 @@ pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig; pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch}; pub use trace::{Config as TraceConfig, Switch};
pub use evm::VMType; pub use evm::VMType;
pub use verification::VerifierType;
use util::journaldb; use util::journaldb;
/// Client state db compaction profile
#[derive(Debug, PartialEq)]
pub enum DatabaseCompactionProfile {
/// Default compaction profile
Default,
/// HDD or other slow storage io compaction profile
HDD,
}
impl Default for DatabaseCompactionProfile {
fn default() -> Self { DatabaseCompactionProfile::Default }
}
/// Client configuration. Includes configs for all sub-systems. /// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ClientConfig { pub struct ClientConfig {
@ -37,4 +51,8 @@ pub struct ClientConfig {
pub name: String, pub name: String,
/// State db cache-size if not default /// State db cache-size if not default
pub db_cache_size: Option<usize>, pub db_cache_size: Option<usize>,
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Type of block verifier used by client.
pub verifier_type: VerifierType,
} }

View File

@ -23,7 +23,7 @@ mod test_client;
mod trace; mod trace;
pub use self::client::*; pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig, Switch, VMType}; pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::error::Error; pub use self::error::Error;
pub use types::ids::*; pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::test_client::{TestBlockChainClient, EachBlockWith};
@ -37,7 +37,7 @@ use util::numbers::U256;
use util::Itertools; use util::Itertools;
use blockchain::TreeRoute; use blockchain::TreeRoute;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::OpenBlock; use block::{OpenBlock, SealedBlock};
use header::{BlockNumber, Header}; use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction}; use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -172,9 +172,6 @@ pub trait BlockChainClient : Sync + Send {
// TODO: should be able to accept blockchain location for call. // TODO: should be able to accept blockchain location for call.
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>; fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;
/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;
/// Returns traces matching given filter. /// Returns traces matching given filter.
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>; fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;
@ -197,7 +194,7 @@ pub trait BlockChainClient : Sync + Send {
fn queue_transactions(&self, transactions: Vec<Bytes>); fn queue_transactions(&self, transactions: Vec<Bytes>);
/// list all transactions /// list all transactions
fn all_transactions(&self) -> Vec<SignedTransaction>; fn pending_transactions(&self) -> Vec<SignedTransaction>;
/// Get the gas price distribution. /// Get the gas price distribution.
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> { fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
@ -245,7 +242,7 @@ pub trait BlockChainClient : Sync + Send {
} else { } else {
None None
} }
} }
} }
/// Extended client interface used for mining /// Extended client interface used for mining
@ -253,4 +250,10 @@ pub trait MiningBlockChainClient : BlockChainClient {
/// Returns OpenBlock prepared for closing. /// Returns OpenBlock prepared for closing.
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes)
-> OpenBlock; -> OpenBlock;
/// Returns EvmFactory.
fn vm_factory(&self) -> &EvmFactory;
/// Import sealed block. Skips all verifications.
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
} }

View File

@ -29,9 +29,10 @@ use blockchain::extras::BlockReceipts;
use error::{ImportResult}; use error::{ImportResult};
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
use miner::{Miner, MinerService}; use miner::{Miner, MinerService};
use spec::Spec;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::OpenBlock; use block::{OpenBlock, SealedBlock};
use executive::Executed; use executive::Executed;
use error::{ExecutionError}; use error::{ExecutionError};
use trace::LocalizedTrace; use trace::LocalizedTrace;
@ -105,7 +106,7 @@ impl TestBlockChainClient {
execution_result: RwLock::new(None), execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()), receipts: RwLock::new(HashMap::new()),
queue_size: AtomicUsize::new(0), queue_size: AtomicUsize::new(0),
miner: Arc::new(Miner::default()), miner: Arc::new(Miner::with_spec(Spec::new_test())),
}; };
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone(); client.genesis_hash = client.last_hash.read().unwrap().clone();
@ -243,6 +244,14 @@ impl MiningBlockChainClient for TestBlockChainClient {
fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock { fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock {
unimplemented!(); unimplemented!();
} }
fn vm_factory(&self) -> &EvmFactory {
unimplemented!();
}
fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult {
unimplemented!();
}
} }
impl BlockChainClient for TestBlockChainClient { impl BlockChainClient for TestBlockChainClient {
@ -462,10 +471,6 @@ impl BlockChainClient for TestBlockChainClient {
} }
} }
fn vm_factory(&self) -> &EvmFactory {
unimplemented!();
}
fn filter_traces(&self, _filter: TraceFilter) -> Option<Vec<LocalizedTrace>> { fn filter_traces(&self, _filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {
unimplemented!(); unimplemented!();
} }
@ -499,7 +504,7 @@ impl BlockChainClient for TestBlockChainClient {
self.import_transactions(tx); self.import_transactions(tx);
} }
fn all_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.all_transactions() self.miner.pending_transactions()
} }
} }

View File

@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate ethash; use ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
use common::*; use common::*;
use block::*; use block::*;
use spec::CommonParams; use spec::CommonParams;

View File

@ -22,6 +22,7 @@ use tests::helpers::*;
use devtools::*; use devtools::*;
use spec::Genesis; use spec::Genesis;
use ethjson; use ethjson;
use ethjson::blockchain::BlockChain;
use miner::Miner; use miner::Miner;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> { pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
@ -41,20 +42,28 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
flush!(" - {}...", name); flush!(" - {}...", name);
let mut spec = match era { let spec = |blockchain: &BlockChain| {
ChainEra::Frontier => ethereum::new_frontier_test(), let genesis = Genesis::from(blockchain.genesis());
ChainEra::Homestead => ethereum::new_homestead_test(), let state = From::from(blockchain.pre_state.clone());
let mut spec = match era {
ChainEra::Frontier => ethereum::new_frontier_test(),
ChainEra::Homestead => ethereum::new_homestead_test(),
};
spec.set_genesis_state(state);
spec.overwrite_genesis_params(genesis);
assert!(spec.is_state_root_valid());
spec
}; };
let genesis = Genesis::from(blockchain.genesis());
let state = From::from(blockchain.pre_state.clone());
spec.set_genesis_state(state);
spec.overwrite_genesis_params(genesis);
assert!(spec.is_state_root_valid());
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
{ {
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let client = Client::new(
ClientConfig::default(),
spec(&blockchain),
temp.as_path(),
Arc::new(Miner::with_spec(spec(&blockchain))),
IoChannel::disconnected()
).unwrap();
for b in &blockchain.blocks_rlp() { for b in &blockchain.blocks_rlp() {
if Block::is_good(&b) { if Block::is_good(&b) {
let _ = client.import_block(b.clone()); let _ = client.import_block(b.clone());

View File

@ -91,6 +91,8 @@ extern crate ethjson;
extern crate bloomchain; extern crate bloomchain;
#[macro_use] extern crate ethcore_ipc as ipc; #[macro_use] extern crate ethcore_ipc as ipc;
extern crate rayon; extern crate rayon;
extern crate hyper;
extern crate ethash;
pub extern crate ethstore; pub extern crate ethstore;
#[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(test)] extern crate ethcore_devtools as devtools;

View File

@ -16,8 +16,10 @@
use rayon::prelude::*; use rayon::prelude::*;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::time::{Instant, Duration};
use util::*; use util::*;
use util::Colour::White;
use account_provider::AccountProvider; use account_provider::AccountProvider;
use views::{BlockView, HeaderView}; use views::{BlockView, HeaderView};
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
@ -28,6 +30,61 @@ use receipt::{Receipt};
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
use miner::work_notify::WorkPoster;
/// Different possible definitions for pending transaction set.
#[derive(Debug)]
pub enum PendingSet {
/// Always just the transactions in the queue. These have had only cheap checks.
AlwaysQueue,
/// Always just the transactions in the sealing block. These have had full checks but
/// may be empty if the node is not actively mining or has force_sealing enabled.
AlwaysSealing,
/// Try the sealing block, but if it is not currently sealing, fallback to the queue.
SealingOrElseQueue,
}
/// Configures the behaviour of the miner.
#[derive(Debug)]
pub struct MinerOptions {
/// URLs to notify when there is new work.
pub new_work_notify: Vec<String>,
/// Force the miner to reseal, even when nobody has asked for work.
pub force_sealing: bool,
/// Reseal on receipt of new external transactions.
pub reseal_on_external_tx: bool,
/// Reseal on receipt of new local transactions.
pub reseal_on_own_tx: bool,
/// Minimum period between transaction-inspired reseals.
pub reseal_min_period: Duration,
/// Maximum amount of gas to bother considering for block insertion.
pub tx_gas_limit: U256,
/// Maximum size of the transaction queue.
pub tx_queue_size: usize,
/// Whether we should fallback to providing all the queue's transactions or just pending.
pub pending_set: PendingSet,
/// How many historical work packages can we store before running out?
pub work_queue_size: usize,
/// Can we submit two different solutions for the same block and expect both to result in an import?
pub enable_resubmission: bool,
}
impl Default for MinerOptions {
fn default() -> Self {
MinerOptions {
new_work_notify: vec![],
force_sealing: false,
reseal_on_external_tx: true,
reseal_on_own_tx: true,
tx_gas_limit: !U256::zero(),
tx_queue_size: 1024,
pending_set: PendingSet::AlwaysQueue,
reseal_min_period: Duration::from_secs(0),
work_queue_size: 20,
enable_resubmission: true,
}
}
}
/// Keeps track of transactions using priority queue and holds currently mined block. /// Keeps track of transactions using priority queue and holds currently mined block.
pub struct Miner { pub struct Miner {
@ -36,8 +93,9 @@ pub struct Miner {
sealing_work: Mutex<UsingQueue<ClosedBlock>>, sealing_work: Mutex<UsingQueue<ClosedBlock>>,
// for sealing... // for sealing...
force_sealing: bool, options: MinerOptions,
sealing_enabled: AtomicBool, sealing_enabled: AtomicBool,
next_allowed_reseal: Mutex<Instant>,
sealing_block_last_request: Mutex<u64>, sealing_block_last_request: Mutex<u64>,
gas_range_target: RwLock<(U256, U256)>, gas_range_target: RwLock<(U256, U256)>,
author: RwLock<Address>, author: RwLock<Address>,
@ -45,55 +103,44 @@ pub struct Miner {
spec: Spec, spec: Spec,
accounts: Option<Arc<AccountProvider>>, accounts: Option<Arc<AccountProvider>>,
} work_poster: Option<WorkPoster>,
impl Default for Miner {
fn default() -> Miner {
Miner {
transaction_queue: Mutex::new(TransactionQueue::new()),
force_sealing: false,
sealing_enabled: AtomicBool::new(false),
sealing_block_last_request: Mutex::new(0),
sealing_work: Mutex::new(UsingQueue::new(5)),
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
author: RwLock::new(Address::default()),
extra_data: RwLock::new(Vec::new()),
accounts: None,
spec: Spec::new_test(),
}
}
} }
impl Miner { impl Miner {
/// Creates new instance of miner /// Creates new instance of miner without accounts, but with given spec.
pub fn new(force_sealing: bool, spec: Spec) -> Arc<Miner> { pub fn with_spec(spec: Spec) -> Miner {
Arc::new(Miner { Miner {
transaction_queue: Mutex::new(TransactionQueue::new()), transaction_queue: Mutex::new(TransactionQueue::new()),
force_sealing: force_sealing, options: Default::default(),
sealing_enabled: AtomicBool::new(force_sealing), sealing_enabled: AtomicBool::new(false),
next_allowed_reseal: Mutex::new(Instant::now()),
sealing_block_last_request: Mutex::new(0), sealing_block_last_request: Mutex::new(0),
sealing_work: Mutex::new(UsingQueue::new(5)), sealing_work: Mutex::new(UsingQueue::new(20)),
gas_range_target: RwLock::new((U256::zero(), U256::zero())), gas_range_target: RwLock::new((U256::zero(), U256::zero())),
author: RwLock::new(Address::default()), author: RwLock::new(Address::default()),
extra_data: RwLock::new(Vec::new()), extra_data: RwLock::new(Vec::new()),
accounts: None, accounts: None,
spec: spec, spec: spec,
}) work_poster: None,
}
} }
/// Creates new instance of miner /// Creates new instance of miner
pub fn with_accounts(force_sealing: bool, spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> { pub fn new(options: MinerOptions, spec: Spec, accounts: Option<Arc<AccountProvider>>) -> Arc<Miner> {
let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None };
Arc::new(Miner { Arc::new(Miner {
transaction_queue: Mutex::new(TransactionQueue::new()), transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)),
force_sealing: force_sealing, sealing_enabled: AtomicBool::new(options.force_sealing || !options.new_work_notify.is_empty()),
sealing_enabled: AtomicBool::new(force_sealing), next_allowed_reseal: Mutex::new(Instant::now()),
sealing_block_last_request: Mutex::new(0), sealing_block_last_request: Mutex::new(0),
sealing_work: Mutex::new(UsingQueue::new(5)), sealing_work: Mutex::new(UsingQueue::new(options.work_queue_size)),
gas_range_target: RwLock::new((U256::zero(), U256::zero())), gas_range_target: RwLock::new((U256::zero(), U256::zero())),
author: RwLock::new(Address::default()), author: RwLock::new(Address::default()),
extra_data: RwLock::new(Vec::new()), extra_data: RwLock::new(Vec::new()),
accounts: Some(accounts), options: options,
accounts: accounts,
spec: spec, spec: spec,
work_poster: work_poster,
}) })
} }
@ -101,15 +148,20 @@ impl Miner {
self.spec.engine.deref() self.spec.engine.deref()
} }
fn forced_sealing(&self) -> bool {
self.options.force_sealing || !self.options.new_work_notify.is_empty()
}
/// Prepares new block for sealing including top transactions from queue. /// Prepares new block for sealing including top transactions from queue.
#[cfg_attr(feature="dev", allow(match_same_arms))] #[cfg_attr(feature="dev", allow(match_same_arms))]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
fn prepare_sealing(&self, chain: &MiningBlockChainClient) { fn prepare_sealing(&self, chain: &MiningBlockChainClient) {
trace!(target: "miner", "prepare_sealing: entering"); trace!(target: "miner", "prepare_sealing: entering");
let (transactions, mut open_block) = { let (transactions, mut open_block, original_work_hash) = {
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()}; let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
let mut sealing_work = self.sealing_work.lock().unwrap(); let mut sealing_work = self.sealing_work.lock().unwrap();
let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash());
let best_hash = chain.best_block_header().sha3(); let best_hash = chain.best_block_header().sha3();
/* /*
// check to see if last ClosedBlock in would_seals is actually same parent block. // check to see if last ClosedBlock in would_seals is actually same parent block.
@ -136,7 +188,7 @@ impl Miner {
) )
} }
}; };
(transactions, open_block) (transactions, open_block, last_work_hash)
}; };
let mut invalid_transactions = HashSet::new(); let mut invalid_transactions = HashSet::new();
@ -202,13 +254,27 @@ impl Miner {
} }
} }
let mut sealing_work = self.sealing_work.lock().unwrap(); let (work, is_new) = {
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) { let mut sealing_work = self.sealing_work.lock().unwrap();
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash()); let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash());
sealing_work.push(block); trace!(target: "miner", "Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().fields().header.hash());
let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) {
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
let pow_hash = block.block().fields().header.hash();
let number = block.block().fields().header.number();
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);
(Some((pow_hash, difficulty, number)), is_new)
} else {
(None, false)
};
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash()));
(work, is_new)
};
if is_new {
work.map(|(pow_hash, difficulty, number)| self.work_poster.as_ref().map(|ref p| p.notify(pow_hash, difficulty, number)));
} }
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash()));
} }
fn update_gas_limit(&self, chain: &MiningBlockChainClient) { fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
@ -236,6 +302,9 @@ impl Miner {
// Return if // Return if
!have_work !have_work
} }
/// Are we allowed to do a non-mandatory reseal?
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() }
} }
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
@ -373,6 +442,10 @@ impl MinerService for Miner {
self.transaction_queue.lock().unwrap().set_limit(limit) self.transaction_queue.lock().unwrap().set_limit(limit)
} }
fn set_tx_gas_limit(&self, limit: U256) {
self.transaction_queue.lock().unwrap().set_tx_gas_limit(limit)
}
/// Get the author that we will seal blocks as. /// Get the author that we will seal blocks as.
fn author(&self) -> Address { fn author(&self) -> Address {
*self.author.read().unwrap() *self.author.read().unwrap()
@ -402,7 +475,7 @@ impl MinerService for Miner {
.map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External)) .map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External))
.collect() .collect()
}; };
if !results.is_empty() { if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
self.update_sealing(chain); self.update_sealing(chain);
} }
results results
@ -437,7 +510,7 @@ impl MinerService for Miner {
import import
}; };
if imported.is_ok() { if imported.is_ok() && self.options.reseal_on_own_tx && self.tx_reseal_allowed() {
// Make sure to do it after transaction is imported and lock is droped. // Make sure to do it after transaction is imported and lock is droped.
// We need to create pending block and enable sealing // We need to create pending block and enable sealing
let prepared = self.enable_and_prepare_sealing(chain); let prepared = self.enable_and_prepare_sealing(chain);
@ -451,26 +524,6 @@ impl MinerService for Miner {
imported imported
} }
fn pending_transactions_hashes(&self) -> Vec<H256> {
let queue = self.transaction_queue.lock().unwrap();
match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) {
(true, Some(pending)) => pending.transactions().iter().map(|t| t.hash()).collect(),
_ => {
queue.pending_hashes()
}
}
}
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
let queue = self.transaction_queue.lock().unwrap();
match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) {
(true, Some(pending)) => pending.transactions().iter().find(|t| &t.hash() == hash).cloned(),
_ => {
queue.find(hash)
}
}
}
fn all_transactions(&self) -> Vec<SignedTransaction> { fn all_transactions(&self) -> Vec<SignedTransaction> {
let queue = self.transaction_queue.lock().unwrap(); let queue = self.transaction_queue.lock().unwrap();
queue.top_transactions() queue.top_transactions()
@ -478,12 +531,41 @@ impl MinerService for Miner {
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {
let queue = self.transaction_queue.lock().unwrap(); let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap();
// TODO: should only use the sealing_work when it's current (it could be an old block) // TODO: should only use the sealing_work when it's current (it could be an old block)
match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) { let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
(true, Some(pending)) => pending.transactions().clone(), true => sw.peek_last_ref(),
_ => { false => None,
queue.top_transactions() };
} match (&self.options.pending_set, sealing_set) {
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.top_transactions(),
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().clone()),
}
}
fn pending_transactions_hashes(&self) -> Vec<H256> {
let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap();
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
true => sw.peek_last_ref(),
false => None,
};
match (&self.options.pending_set, sealing_set) {
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.pending_hashes(),
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().iter().map(|t| t.hash()).collect()),
}
}
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
let queue = self.transaction_queue.lock().unwrap();
let sw = self.sealing_work.lock().unwrap();
let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) {
true => sw.peek_last_ref(),
false => None,
};
match (&self.options.pending_set, sealing_set) {
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.find(hash),
(_, sealing) => sealing.and_then(|s| s.transactions().iter().find(|t| &t.hash() == hash).cloned()),
} }
} }
@ -511,7 +593,7 @@ impl MinerService for Miner {
let current_no = chain.chain_info().best_block_number; let current_no = chain.chain_info().best_block_number;
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions(); let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
let last_request = *self.sealing_block_last_request.lock().unwrap(); let last_request = *self.sealing_block_last_request.lock().unwrap();
let should_disable_sealing = !self.force_sealing let should_disable_sealing = !self.forced_sealing()
&& !has_local_transactions && !has_local_transactions
&& current_no > last_request && current_no > last_request
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS; && current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS;
@ -521,6 +603,7 @@ impl MinerService for Miner {
self.sealing_enabled.store(false, atomic::Ordering::Relaxed); self.sealing_enabled.store(false, atomic::Ordering::Relaxed);
self.sealing_work.lock().unwrap().reset(); self.sealing_work.lock().unwrap().reset();
} else { } else {
*self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period;
self.prepare_sealing(chain); self.prepare_sealing(chain);
} }
} }
@ -537,26 +620,22 @@ impl MinerService for Miner {
} }
fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> { fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
if let Some(b) = self.sealing_work.lock().unwrap().take_used_if(|b| &b.hash() == &pow_hash) { let result = if let Some(b) = self.sealing_work.lock().unwrap().get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) {
match b.lock().try_seal(self.engine(), seal) { b.lock().try_seal(self.engine(), seal).or_else(|_| {
Err(_) => { warn!(target: "miner", "Mined solution rejected: Invalid.");
info!(target: "miner", "Mined block rejected, PoW was invalid."); Err(Error::PowInvalid)
Err(Error::PowInvalid) })
}
Ok(sealed) => {
info!(target: "miner", "New block mined, hash: {}", sealed.header().hash());
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
let b = sealed.rlp_bytes();
let h = b.sha3();
try!(chain.import_block(b));
info!("Block {} submitted and imported.", h);
Ok(())
}
}
} else { } else {
info!(target: "miner", "Mined block rejected, PoW hash invalid or out of date."); warn!(target: "miner", "Mined solution rejected: Block unknown or out of date.");
Err(Error::PowHashInvalid) Err(Error::PowHashInvalid)
} };
result.and_then(|sealed| {
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()));
Ok(())
})
} }
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
@ -626,6 +705,7 @@ mod tests {
use util::*; use util::*;
use client::{TestBlockChainClient, EachBlockWith}; use client::{TestBlockChainClient, EachBlockWith};
use block::*; use block::*;
use spec::Spec;
// TODO [ToDr] To uncomment` when TestBlockChainClient can actually return a ClosedBlock. // TODO [ToDr] To uncomment` when TestBlockChainClient can actually return a ClosedBlock.
#[ignore] #[ignore]
@ -633,7 +713,7 @@ mod tests {
fn should_prepare_block_to_seal() { fn should_prepare_block_to_seal() {
// given // given
let client = TestBlockChainClient::default(); let client = TestBlockChainClient::default();
let miner = Miner::default(); let miner = Miner::with_spec(Spec::new_test());
// when // when
let sealing_work = miner.map_sealing_work(&client, |_| ()); let sealing_work = miner.map_sealing_work(&client, |_| ());
@ -645,7 +725,7 @@ mod tests {
fn should_still_work_after_a_couple_of_blocks() { fn should_still_work_after_a_couple_of_blocks() {
// given // given
let client = TestBlockChainClient::default(); let client = TestBlockChainClient::default();
let miner = Miner::default(); let miner = Miner::with_spec(Spec::new_test());
let res = miner.map_sealing_work(&client, |b| b.block().fields().header.hash()); let res = miner.map_sealing_work(&client, |b| b.block().fields().header.hash());
assert!(res.is_some()); assert!(res.is_some());

View File

@ -28,11 +28,12 @@
//! extern crate ethcore; //! extern crate ethcore;
//! use std::env; //! use std::env;
//! use util::network::{NetworkService, NetworkConfiguration}; //! use util::network::{NetworkService, NetworkConfiguration};
//! use ethcore::ethereum;
//! use ethcore::client::{Client, ClientConfig}; //! use ethcore::client::{Client, ClientConfig};
//! use ethcore::miner::{Miner, MinerService}; //! use ethcore::miner::{Miner, MinerService};
//! //!
//! fn main() { //! fn main() {
//! let miner: Miner = Miner::default(); //! let miner: Miner = Miner::with_spec(ethereum::new_frontier(true));
//! // get status //! // get status
//! assert_eq!(miner.status().transactions_in_pending_queue, 0); //! assert_eq!(miner.status().transactions_in_pending_queue, 0);
//! //!
@ -44,9 +45,10 @@
mod miner; mod miner;
mod external; mod external;
mod transaction_queue; mod transaction_queue;
mod work_notify;
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
pub use self::miner::{Miner}; pub use self::miner::{Miner, MinerOptions, PendingSet};
pub use self::external::{ExternalMiner, ExternalMinerService}; pub use self::external::{ExternalMiner, ExternalMinerService};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -100,6 +102,9 @@ pub trait MinerService : Send + Sync {
/// Set maximal number of transactions kept in the queue (both current and future). /// Set maximal number of transactions kept in the queue (both current and future).
fn set_transactions_limit(&self, limit: usize); fn set_transactions_limit(&self, limit: usize);
/// Set maximum amount of gas allowed for any single transaction to mine.
fn set_tx_gas_limit(&self, limit: U256);
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) -> fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) ->
Vec<Result<TransactionImportResult, Error>> Vec<Result<TransactionImportResult, Error>>

View File

@ -334,6 +334,8 @@ const GAS_LIMIT_HYSTERESIS: usize = 10; // %
pub struct TransactionQueue { pub struct TransactionQueue {
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0) /// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
minimal_gas_price: U256, minimal_gas_price: U256,
/// The maximum amount of gas any individual transaction may use.
tx_gas_limit: U256,
/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0) /// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0)
gas_limit: U256, gas_limit: U256,
/// Priority queue for transactions that can go to block /// Priority queue for transactions that can go to block
@ -355,11 +357,11 @@ impl Default for TransactionQueue {
impl TransactionQueue { impl TransactionQueue {
/// Creates new instance of this Queue /// Creates new instance of this Queue
pub fn new() -> Self { pub fn new() -> Self {
Self::with_limit(1024) Self::with_limits(1024, !U256::zero())
} }
/// Create new instance of this Queue with specified limits /// Create new instance of this Queue with specified limits
pub fn with_limit(limit: usize) -> Self { pub fn with_limits(limit: usize, tx_gas_limit: U256) -> Self {
let current = TransactionSet { let current = TransactionSet {
by_priority: BTreeSet::new(), by_priority: BTreeSet::new(),
by_address: Table::new(), by_address: Table::new(),
@ -374,6 +376,7 @@ impl TransactionQueue {
TransactionQueue { TransactionQueue {
minimal_gas_price: U256::zero(), minimal_gas_price: U256::zero(),
tx_gas_limit: tx_gas_limit,
gas_limit: !U256::zero(), gas_limit: !U256::zero(),
current: current, current: current,
future: future, future: future,
@ -418,6 +421,12 @@ impl TransactionQueue {
}; };
} }
/// Set the new limit for the amount of gas any individual transaction may have.
/// Any transaction already imported to the queue is not affected.
pub fn set_tx_gas_limit(&mut self, limit: U256) {
self.tx_gas_limit = limit;
}
/// Returns current status for this queue /// Returns current status for this queue
pub fn status(&self) -> TransactionQueueStatus { pub fn status(&self) -> TransactionQueueStatus {
TransactionQueueStatus { TransactionQueueStatus {
@ -432,10 +441,12 @@ impl TransactionQueue {
trace!(target: "miner", "Importing: {:?}", tx.hash()); trace!(target: "miner", "Importing: {:?}", tx.hash());
if tx.gas_price < self.minimal_gas_price { if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local {
trace!(target: "miner", trace!(target: "miner",
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
tx.hash(), tx.gas_price, self.minimal_gas_price tx.hash(),
tx.gas_price,
self.minimal_gas_price
); );
return Err(Error::Transaction(TransactionError::InsufficientGasPrice { return Err(Error::Transaction(TransactionError::InsufficientGasPrice {
@ -446,10 +457,13 @@ impl TransactionQueue {
try!(tx.check_low_s()); try!(tx.check_low_s());
if tx.gas > self.gas_limit { if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit {
trace!(target: "miner", trace!(target: "miner",
"Dropping transaction above gas limit: {:?} ({} > {})", "Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
tx.hash(), tx.gas, self.gas_limit tx.hash(),
tx.gas,
self.gas_limit,
self.tx_gas_limit
); );
return Err(Error::Transaction(TransactionError::GasLimitExceeded { return Err(Error::Transaction(TransactionError::GasLimitExceeded {
@ -463,8 +477,13 @@ impl TransactionQueue {
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas; let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
if client_account.balance < cost { if client_account.balance < cost {
trace!(target: "miner", "Dropping transaction without sufficient balance: {:?} ({} < {})", trace!(target: "miner",
vtx.hash(), client_account.balance, cost); "Dropping transaction without sufficient balance: {:?} ({} < {})",
vtx.hash(),
client_account.balance,
cost
);
return Err(Error::Transaction(TransactionError::InsufficientBalance { return Err(Error::Transaction(TransactionError::InsufficientBalance {
cost: cost, cost: cost,
balance: client_account.balance balance: client_account.balance
@ -1036,7 +1055,7 @@ mod test {
} }
#[test] #[test]
fn should_not_import_transaction_below_min_gas_price_threshold() { fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::new();
let tx = new_tx(); let tx = new_tx();
@ -1055,6 +1074,23 @@ mod test {
assert_eq!(stats.future, 0); assert_eq!(stats.future, 0);
} }
#[test]
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
// given
let mut txq = TransactionQueue::new();
let tx = new_tx();
txq.set_minimal_gas_price(tx.gas_price + U256::one());
// when
let res = txq.add(tx, &default_nonce, TransactionOrigin::Local);
// then
assert_eq!(res.unwrap(), TransactionImportResult::Current);
let stats = txq.status();
assert_eq!(stats.pending, 1);
assert_eq!(stats.future, 0);
}
#[test] #[test]
fn should_reject_incorectly_signed_transaction() { fn should_reject_incorectly_signed_transaction() {
// given // given
@ -1288,7 +1324,7 @@ mod test {
#[test] #[test]
fn should_drop_old_transactions_when_hitting_the_limit() { fn should_drop_old_transactions_when_hitting_the_limit() {
// given // given
let mut txq = TransactionQueue::with_limit(1); let mut txq = TransactionQueue::with_limits(1, !U256::zero());
let (tx, tx2) = new_txs(U256::one()); let (tx, tx2) = new_txs(U256::one());
let sender = tx.sender().unwrap(); let sender = tx.sender().unwrap();
let nonce = tx.nonce; let nonce = tx.nonce;
@ -1310,7 +1346,7 @@ mod test {
#[test] #[test]
fn should_return_correct_nonces_when_dropped_because_of_limit() { fn should_return_correct_nonces_when_dropped_because_of_limit() {
// given // given
let mut txq = TransactionQueue::with_limit(2); let mut txq = TransactionQueue::with_limits(2, !U256::zero());
let tx = new_tx(); let tx = new_tx();
let (tx1, tx2) = new_txs(U256::one()); let (tx1, tx2) = new_txs(U256::one());
let sender = tx1.sender().unwrap(); let sender = tx1.sender().unwrap();
@ -1331,7 +1367,7 @@ mod test {
#[test] #[test]
fn should_limit_future_transactions() { fn should_limit_future_transactions() {
let mut txq = TransactionQueue::with_limit(1); let mut txq = TransactionQueue::with_limits(1, !U256::zero());
txq.current.set_limit(10); txq.current.set_limit(10);
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(4), U256::from(1)); let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(4), U256::from(1));
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(4), U256::from(2)); let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(4), U256::from(2));
@ -1591,7 +1627,7 @@ mod test {
#[test] #[test]
fn should_keep_right_order_in_future() { fn should_keep_right_order_in_future() {
// given // given
let mut txq = TransactionQueue::with_limit(1); let mut txq = TransactionQueue::with_limits(1, !U256::zero());
let (tx1, tx2) = new_txs(U256::from(1)); let (tx1, tx2) = new_txs(U256::from(1));
let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance: let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance:
default_nonce(a).balance }; default_nonce(a).balance };

View File

@ -0,0 +1,115 @@
// 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/>.
extern crate hyper;
use hyper::header::ContentType;
use hyper::method::Method;
use hyper::client::{Request, Response, Client};
use hyper::{Next};
use hyper::net::HttpStream;
use ethash::SeedHashCompute;
use hyper::Url;
use util::*;
use ethereum::ethash::Ethash;
pub struct WorkPoster {
urls: Vec<Url>,
client: Mutex<Client<PostHandler>>,
seed_compute: Mutex<SeedHashCompute>,
}
impl WorkPoster {
pub fn new(urls: &[String]) -> Self {
let urls = urls.into_iter().filter_map(|u| {
match Url::parse(&u) {
Ok(url) => Some(url),
Err(e) => {
warn!("Error parsing URL {} : {}", u, e);
None
}
}
}).collect();
let client = WorkPoster::create_client();
WorkPoster {
client: Mutex::new(client),
urls: urls,
seed_compute: Mutex::new(SeedHashCompute::new()),
}
}
fn create_client() -> Client<PostHandler> {
let client = Client::<PostHandler>::configure()
.keep_alive(true)
.build().expect("Error creating HTTP client") as Client<PostHandler>;
client
}
pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
// TODO: move this to engine
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 mut client = self.client.lock().unwrap();
for u in &self.urls {
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
warn!("Error sending HTTP notification to {} : {}, retrying", u, e);
// TODO: remove this once https://github.com/hyperium/hyper/issues/848 is fixed
*client = WorkPoster::create_client();
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
warn!("Error sending HTTP notification to {} : {}", u, e);
}
}
}
}
}
struct PostHandler {
body: String,
}
impl hyper::client::Handler<HttpStream> for PostHandler {
fn on_request(&mut self, request: &mut Request) -> Next {
request.set_method(Method::Post);
request.headers_mut().set(ContentType::json());
Next::write()
}
fn on_request_writable(&mut self, encoder: &mut hyper::Encoder<HttpStream>) -> Next {
if let Err(e) = encoder.write_all(self.body.as_bytes()) {
trace!("Error posting work data: {}", e);
}
encoder.close();
Next::read()
}
fn on_response(&mut self, _response: Response) -> Next {
Next::end()
}
fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder<HttpStream>) -> Next {
Next::end()
}
fn on_error(&mut self, err: hyper::Error) -> Next {
trace!("Error posting work data: {}", err);
Next::end()
}
}

View File

@ -17,6 +17,7 @@
//! Creates and registers client and network services. //! Creates and registers client and network services.
use util::*; use util::*;
use util::Colour::{Yellow, White};
use util::panics::*; use util::panics::*;
use spec::Spec; use spec::Spec;
use error::*; use error::*;
@ -36,6 +37,8 @@ pub enum SyncMessage {
retracted: Vec<H256>, retracted: Vec<H256>,
/// Hashes of blocks that are now included in cannonical chain /// Hashes of blocks that are now included in cannonical chain
enacted: Vec<H256>, enacted: Vec<H256>,
/// Hashes of blocks that are sealed by this node
sealed: Vec<H256>,
}, },
/// Best Block Hash in chain has been changed /// Best Block Hash in chain has been changed
NewChainHead, NewChainHead,
@ -69,8 +72,7 @@ impl ClientService {
try!(net_service.start()); try!(net_service.start());
} }
info!("Starting {}", net_service.host_info()); 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, spec.engine.name());
let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel())); let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel()));
panic_handler.forward_from(client.deref()); panic_handler.forward_from(client.deref());
let client_io = Arc::new(ClientIoHandler { let client_io = Arc::new(ClientIoHandler {
@ -159,9 +161,15 @@ mod tests {
#[test] #[test]
fn it_can_be_started() { fn it_can_be_started() {
let spec = get_test_spec();
let temp_path = RandomTempPath::new(); let temp_path = RandomTempPath::new();
let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path(), Arc::new(Miner::default()), false); let service = ClientService::start(
ClientConfig::default(),
get_test_spec(),
NetworkConfiguration::new_local(),
&temp_path.as_path(),
Arc::new(Miner::with_spec(get_test_spec())),
false
);
assert!(service.is_ok()); assert!(service.is_ok());
} }
} }

View File

@ -208,12 +208,17 @@ impl State {
self.require(a, false).set_storage(key, value) self.require(a, false).set_storage(key, value)
} }
/// Initialise the code of account `a` so that it is `value` for `key`. /// Initialise the code of account `a` so that it is `code`.
/// NOTE: Account should have been created with `new_contract`. /// NOTE: Account should have been created with `new_contract`.
pub fn init_code(&mut self, a: &Address, code: Bytes) { pub fn init_code(&mut self, a: &Address, code: Bytes) {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code); self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code);
} }
/// Reset the code of account `a` so that it is `code`.
pub fn reset_code(&mut self, a: &Address, code: Bytes) {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).reset_code(code);
}
/// Execute a given transaction. /// Execute a given transaction.
/// This will change the state accordingly. /// This will change the state accordingly.
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult {

View File

@ -24,7 +24,7 @@ use miner::Miner;
#[test] #[test]
fn imports_from_empty() { fn imports_from_empty() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap();
client.import_verified_blocks(&IoChannel::disconnected()); client.import_verified_blocks(&IoChannel::disconnected());
client.flush_queue(); client.flush_queue();
} }
@ -42,7 +42,7 @@ fn returns_state_root_basic() {
#[test] #[test]
fn imports_good_block() { fn imports_good_block() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap();
let good_block = get_good_dummy_block(); let good_block = get_good_dummy_block();
if let Err(_) = client.import_block(good_block) { if let Err(_) = client.import_block(good_block) {
panic!("error importing block being good by definition"); panic!("error importing block being good by definition");
@ -57,7 +57,7 @@ fn imports_good_block() {
#[test] #[test]
fn query_none_block() { fn query_none_block() {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap();
let non_existant = client.block_header(BlockID::Number(188)); let non_existant = client.block_header(BlockID::Number(188));
assert!(non_existant.is_none()); assert!(non_existant.is_none());

View File

@ -17,7 +17,7 @@
use client::{BlockChainClient, Client, ClientConfig}; use client::{BlockChainClient, Client, ClientConfig};
use common::*; use common::*;
use spec::*; use spec::*;
use block::{OpenBlock}; use block::{OpenBlock, Drain};
use blockchain::{BlockChain, Config as BlockChainConfig}; use blockchain::{BlockChain, Config as BlockChainConfig};
use state::*; use state::*;
use evm::Schedule; use evm::Schedule;
@ -151,7 +151,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let test_spec = get_test_spec(); let test_spec = get_test_spec();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap();
let test_engine = &test_spec.engine; let test_engine = &test_spec.engine;
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
@ -250,7 +250,7 @@ pub fn push_blocks_to_client(client: &Arc<Client>, timestamp_salt: u64, starting
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> { pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap();
for block in &blocks { for block in &blocks {
if let Err(_) = client.import_block(block.clone()) { if let Err(_) = client.import_block(block.clone()) {
panic!("panic importing block which is well-formed"); panic!("panic importing block which is well-formed");
@ -303,7 +303,7 @@ pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> { pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
let temp = RandomTempPath::new(); let temp = RandomTempPath::new();
let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge, None); let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge, DatabaseConfig::default());
GuardedTempResult { GuardedTempResult {
_temp: temp, _temp: temp,
result: Some(journal_db) result: Some(journal_db)
@ -320,7 +320,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
} }
pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> { pub fn get_temp_journal_db_in(path: &Path) -> Box<JournalDB> {
journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge, None) journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge, DatabaseConfig::default())
} }
pub fn get_temp_state_in(path: &Path) -> State { pub fn get_temp_state_in(path: &Path) -> State {

View File

@ -24,11 +24,11 @@ use super::verification;
pub struct CanonVerifier; pub struct CanonVerifier;
impl Verifier for CanonVerifier { impl Verifier for CanonVerifier {
fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> { fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
verification::verify_block_family(header, bytes, engine, bc) verification::verify_block_family(header, bytes, engine, bc)
} }
fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> { fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> {
verification::verify_block_final(expected, got) verification::verify_block_final(expected, got)
} }
} }

View File

@ -17,11 +17,32 @@
pub mod verification; pub mod verification;
pub mod verifier; pub mod verifier;
mod canon_verifier; mod canon_verifier;
#[cfg(test)]
mod noop_verifier; mod noop_verifier;
pub use self::verification::*; pub use self::verification::*;
pub use self::verifier::Verifier; pub use self::verifier::Verifier;
pub use self::canon_verifier::CanonVerifier; pub use self::canon_verifier::CanonVerifier;
#[cfg(test)]
pub use self::noop_verifier::NoopVerifier; pub use self::noop_verifier::NoopVerifier;
/// Verifier type.
#[derive(Debug)]
pub enum VerifierType {
/// Verifies block normally.
Canon,
/// Does not verify block at all.
/// Used in tests.
Noop,
}
impl Default for VerifierType {
fn default() -> Self {
VerifierType::Canon
}
}
pub fn new(v: VerifierType) -> Box<Verifier> {
match v {
VerifierType::Canon => Box::new(CanonVerifier),
VerifierType::Noop => Box::new(NoopVerifier),
}
}

View File

@ -24,11 +24,11 @@ use super::Verifier;
pub struct NoopVerifier; pub struct NoopVerifier;
impl Verifier for NoopVerifier { impl Verifier for NoopVerifier {
fn verify_block_family(_header: &Header, _bytes: &[u8], _engine: &Engine, _bc: &BlockProvider) -> Result<(), Error> { fn verify_block_family(&self, _header: &Header, _bytes: &[u8], _engine: &Engine, _bc: &BlockProvider) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn verify_block_final(_expected: &Header, _got: &Header) -> Result<(), Error> { fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> {
Ok(()) Ok(())
} }
} }

View File

@ -21,6 +21,6 @@ use header::Header;
/// Should be used to verify blocks. /// Should be used to verify blocks.
pub trait Verifier: Send + Sync { pub trait Verifier: Send + Sync {
fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>; fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>;
fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>; fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>;
} }

View File

@ -1,339 +0,0 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Block oriented views onto rlp.
use util::*;
use header::*;
use transaction::*;
/// View onto transaction rlp.
pub struct TransactionView<'a> {
rlp: Rlp<'a>
}
impl<'a> TransactionView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> TransactionView<'a> {
TransactionView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> {
TransactionView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Get the nonce field of the transaction.
pub fn nonce(&self) -> U256 { self.rlp.val_at(0) }
/// Get the gas_price field of the transaction.
pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) }
/// Get the gas field of the transaction.
pub fn gas(&self) -> U256 { self.rlp.val_at(2) }
/// Get the value field of the transaction.
pub fn value(&self) -> U256 { self.rlp.val_at(4) }
/// Get the data field of the transaction.
pub fn data(&self) -> Bytes { self.rlp.val_at(5) }
/// Get the v field of the transaction.
pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 }
/// Get the r field of the transaction.
pub fn r(&self) -> U256 { self.rlp.val_at(7) }
/// Get the s field of the transaction.
pub fn s(&self) -> U256 { self.rlp.val_at(8) }
// TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) }
}
impl<'a> Hashable for TransactionView<'a> {
fn sha3(&self) -> H256 {
self.rlp.as_raw().sha3()
}
}
/// View onto transaction rlp.
pub struct AccountView<'a> {
rlp: Rlp<'a>
}
impl<'a> AccountView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> AccountView<'a> {
AccountView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> AccountView<'a> {
AccountView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Get the nonce field of the transaction.
pub fn nonce(&self) -> U256 { self.rlp.val_at(0) }
/// Get the gas_price field of the transaction.
pub fn balance(&self) -> U256 { self.rlp.val_at(1) }
/// Get the gas field of the transaction.
pub fn storage_root(&self) -> H256 { self.rlp.val_at(2) }
/// Get the value field of the transaction.
pub fn code_hash(&self) -> H256 { self.rlp.val_at(3) }
}
/// View onto block rlp.
pub struct BlockView<'a> {
rlp: Rlp<'a>
}
impl<'a> BlockView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> BlockView<'a> {
BlockView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> {
BlockView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Create new Header object from header rlp.
pub fn header(&self) -> Header {
self.rlp.val_at(0)
}
/// Create new header view obto block head rlp.
pub fn header_view(&self) -> HeaderView<'a> {
HeaderView::new_from_rlp(self.rlp.at(0))
}
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<SignedTransaction> {
self.rlp.val_at(1)
}
/// Return List of transactions with additional localization info.
pub fn localized_transactions(&self) -> Vec<LocalizedTransaction> {
let header = self.header_view();
let block_hash = header.sha3();
let block_number = header.number();
self.transactions()
.into_iter()
.enumerate()
.map(|(i, t)| LocalizedTransaction {
signed: t,
block_hash: block_hash.clone(),
block_number: block_number,
transaction_index: i
}).collect()
}
/// Return number of transactions in given block, without deserializing them.
pub fn transactions_count(&self) -> usize {
self.rlp.at(1).iter().count()
}
/// Return List of transactions in given block.
pub fn transaction_views(&self) -> Vec<TransactionView> {
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
}
/// Return transaction hashes.
pub fn transaction_hashes(&self) -> Vec<H256> {
self.rlp.at(1).iter().map(|rlp| rlp.as_raw().sha3()).collect()
}
/// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<SignedTransaction> {
self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val())
}
/// Returns localized transaction at given index.
pub fn localized_transaction_at(&self, index: usize) -> Option<LocalizedTransaction> {
let header = self.header_view();
let block_hash = header.sha3();
let block_number = header.number();
self.transaction_at(index).map(|t| LocalizedTransaction {
signed: t,
block_hash: block_hash,
block_number: block_number,
transaction_index: index
})
}
/// Return list of uncles of given block.
pub fn uncles(&self) -> Vec<Header> {
self.rlp.val_at(2)
}
/// Return number of uncles in given block, without deserializing them.
pub fn uncles_count(&self) -> usize {
self.rlp.at(2).iter().count()
}
/// Return List of transactions in given block.
pub fn uncle_views(&self) -> Vec<HeaderView> {
self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect()
}
/// Return list of uncle hashes of given block.
pub fn uncle_hashes(&self) -> Vec<H256> {
self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect()
}
/// Return nth uncle.
pub fn uncle_at(&self, index: usize) -> Option<Header> {
self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val())
}
}
impl<'a> Hashable for BlockView<'a> {
fn sha3(&self) -> H256 {
self.header_view().sha3()
}
}
/// View onto block header rlp.
pub struct HeaderView<'a> {
rlp: Rlp<'a>
}
impl<'a> HeaderView<'a> {
/// Creates new view onto header from raw bytes.
pub fn new(bytes: &'a [u8]) -> HeaderView<'a> {
HeaderView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto header from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> HeaderView<'a> {
HeaderView {
rlp: rlp
}
}
/// Returns header hash.
pub fn hash(&self) -> H256 { self.sha3() }
/// Returns raw rlp.
pub fn rlp(&self) -> &Rlp<'a> { &self.rlp }
/// Returns parent hash.
pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) }
/// Returns uncles hash.
pub fn uncles_hash(&self) -> H256 { self.rlp.val_at(1) }
/// Returns author.
pub fn author(&self) -> Address { self.rlp.val_at(2) }
/// Returns state root.
pub fn state_root(&self) -> H256 { self.rlp.val_at(3) }
/// Returns transactions root.
pub fn transactions_root(&self) -> H256 { self.rlp.val_at(4) }
/// Returns block receipts root.
pub fn receipts_root(&self) -> H256 { self.rlp.val_at(5) }
/// Returns block log bloom.
pub fn log_bloom(&self) -> H2048 { self.rlp.val_at(6) }
/// Returns block difficulty.
pub fn difficulty(&self) -> U256 { self.rlp.val_at(7) }
/// Returns block number.
pub fn number(&self) -> BlockNumber { self.rlp.val_at(8) }
/// Returns block gas limit.
pub fn gas_limit(&self) -> U256 { self.rlp.val_at(9) }
/// Returns block gas used.
pub fn gas_used(&self) -> U256 { self.rlp.val_at(10) }
/// Returns timestamp.
pub fn timestamp(&self) -> u64 { self.rlp.val_at(11) }
/// Returns block extra data.
pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) }
/// Returns a vector of post-RLP-encoded seal fields.
pub fn seal(&self) -> Vec<Bytes> {
let mut seal = vec![];
for i in 13..self.rlp.item_count() {
seal.push(self.rlp.at(i).as_raw().to_vec());
}
seal
}
}
impl<'a> Hashable for HeaderView<'a> {
fn sha3(&self) -> H256 {
self.rlp.as_raw().sha3()
}
}
#[cfg(test)]
mod tests {
use rustc_serialize::hex::FromHex;
use super::BlockView;
#[test]
fn test_header_view_seal_fields() {
// that's rlp of block created with ethash engine.
let block_rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap();
let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap();
let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap();
let block_view = BlockView::new(&block_rlp);
let header_view = block_view.header_view();
let seal_fields = header_view.seal();
assert_eq!(seal_fields.len(), 2);
assert_eq!(seal_fields[0], mix_hash);
assert_eq!(seal_fields[1], nonce);
}
}

167
ethcore/src/views/block.rs Normal file
View File

@ -0,0 +1,167 @@
// 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/>.
//! View onto block rlp.
use util::*;
use header::*;
use transaction::*;
use super::{TransactionView, HeaderView};
/// View onto block rlp.
pub struct BlockView<'a> {
rlp: Rlp<'a>
}
impl<'a> BlockView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> BlockView<'a> {
BlockView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> {
BlockView {
rlp: rlp
}
}
/// Block header hash.
pub fn hash(&self) -> H256 {
self.sha3()
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Create new Header object from header rlp.
pub fn header(&self) -> Header {
self.rlp.val_at(0)
}
/// Create new header view obto block head rlp.
pub fn header_view(&self) -> HeaderView<'a> {
HeaderView::new_from_rlp(self.rlp.at(0))
}
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<SignedTransaction> {
self.rlp.val_at(1)
}
/// Return List of transactions with additional localization info.
pub fn localized_transactions(&self) -> Vec<LocalizedTransaction> {
let header = self.header_view();
let block_hash = header.sha3();
let block_number = header.number();
self.transactions()
.into_iter()
.enumerate()
.map(|(i, t)| LocalizedTransaction {
signed: t,
block_hash: block_hash.clone(),
block_number: block_number,
transaction_index: i
}).collect()
}
/// Return number of transactions in given block, without deserializing them.
pub fn transactions_count(&self) -> usize {
self.rlp.at(1).iter().count()
}
/// Return List of transactions in given block.
pub fn transaction_views(&self) -> Vec<TransactionView> {
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
}
/// Return transaction hashes.
pub fn transaction_hashes(&self) -> Vec<H256> {
self.rlp.at(1).iter().map(|rlp| rlp.as_raw().sha3()).collect()
}
/// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<SignedTransaction> {
self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val())
}
/// Returns localized transaction at given index.
pub fn localized_transaction_at(&self, index: usize) -> Option<LocalizedTransaction> {
let header = self.header_view();
let block_hash = header.sha3();
let block_number = header.number();
self.transaction_at(index).map(|t| LocalizedTransaction {
signed: t,
block_hash: block_hash,
block_number: block_number,
transaction_index: index
})
}
/// Return list of uncles of given block.
pub fn uncles(&self) -> Vec<Header> {
self.rlp.val_at(2)
}
/// Return number of uncles in given block, without deserializing them.
pub fn uncles_count(&self) -> usize {
self.rlp.at(2).iter().count()
}
/// Return List of transactions in given block.
pub fn uncle_views(&self) -> Vec<HeaderView> {
self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect()
}
/// Return list of uncle hashes of given block.
pub fn uncle_hashes(&self) -> Vec<H256> {
self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect()
}
/// Return nth uncle.
pub fn uncle_at(&self, index: usize) -> Option<Header> {
self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val())
}
}
impl<'a> Hashable for BlockView<'a> {
fn sha3(&self) -> H256 {
self.header_view().sha3()
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rustc_serialize::hex::FromHex;
use util::H256;
use super::BlockView;
#[test]
fn test_block_view() {
// that's rlp of block created with ethash engine.
let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap();
let view = BlockView::new(&rlp);
assert_eq!(view.hash(), H256::from_str("2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259").unwrap());
assert_eq!(view.transactions_count(), 1);
assert_eq!(view.uncles_count(), 0);
}
}

134
ethcore/src/views/header.rs Normal file
View File

@ -0,0 +1,134 @@
// 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/>.
//! View onto block header rlp
use util::{Rlp, U256, Bytes, Hashable, H256, Address, H2048, View};
use header::BlockNumber;
/// View onto block header rlp.
pub struct HeaderView<'a> {
rlp: Rlp<'a>
}
impl<'a> HeaderView<'a> {
/// Creates new view onto header from raw bytes.
pub fn new(bytes: &'a [u8]) -> HeaderView<'a> {
HeaderView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto header from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> HeaderView<'a> {
HeaderView {
rlp: rlp
}
}
/// Returns header hash.
pub fn hash(&self) -> H256 { self.sha3() }
/// Returns raw rlp.
pub fn rlp(&self) -> &Rlp<'a> { &self.rlp }
/// Returns parent hash.
pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) }
/// Returns uncles hash.
pub fn uncles_hash(&self) -> H256 { self.rlp.val_at(1) }
/// Returns author.
pub fn author(&self) -> Address { self.rlp.val_at(2) }
/// Returns state root.
pub fn state_root(&self) -> H256 { self.rlp.val_at(3) }
/// Returns transactions root.
pub fn transactions_root(&self) -> H256 { self.rlp.val_at(4) }
/// Returns block receipts root.
pub fn receipts_root(&self) -> H256 { self.rlp.val_at(5) }
/// Returns block log bloom.
pub fn log_bloom(&self) -> H2048 { self.rlp.val_at(6) }
/// Returns block difficulty.
pub fn difficulty(&self) -> U256 { self.rlp.val_at(7) }
/// Returns block number.
pub fn number(&self) -> BlockNumber { self.rlp.val_at(8) }
/// Returns block gas limit.
pub fn gas_limit(&self) -> U256 { self.rlp.val_at(9) }
/// Returns block gas used.
pub fn gas_used(&self) -> U256 { self.rlp.val_at(10) }
/// Returns timestamp.
pub fn timestamp(&self) -> u64 { self.rlp.val_at(11) }
/// Returns block extra data.
pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) }
/// Returns a vector of post-RLP-encoded seal fields.
pub fn seal(&self) -> Vec<Bytes> {
let mut seal = vec![];
for i in 13..self.rlp.item_count() {
seal.push(self.rlp.at(i).as_raw().to_vec());
}
seal
}
}
impl<'a> Hashable for HeaderView<'a> {
fn sha3(&self) -> H256 {
self.rlp.as_raw().sha3()
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rustc_serialize::hex::FromHex;
use util::{H256, Address, H2048, U256};
use super::HeaderView;
#[test]
fn test_header_view() {
// that's rlp of block header created with ethash engine.
let rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap();
let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap();
let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap();
let view = HeaderView::new(&rlp);
assert_eq!(view.hash(), H256::from_str("2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259").unwrap());
assert_eq!(view.parent_hash(), H256::from_str("d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7").unwrap());
assert_eq!(view.uncles_hash(), H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap());
assert_eq!(view.author(), Address::from_str("8888f1f195afa192cfee860698584c030f4c9db1").unwrap());
assert_eq!(view.state_root(), H256::from_str("5fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25").unwrap());
assert_eq!(view.transactions_root(), H256::from_str("88d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158").unwrap());
assert_eq!(view.receipts_root(), H256::from_str("07c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1").unwrap());
assert_eq!(view.log_bloom(), H2048::default());
assert_eq!(view.difficulty(), U256::from(0x02_00_80));
assert_eq!(view.number(), 3);
assert_eq!(view.gas_limit(), U256::from(0x2f_ef_ba));
assert_eq!(view.gas_used(), U256::from(0x52_4d));
assert_eq!(view.timestamp(), 0x56_8e_93_2a);
assert_eq!(view.extra_data(), vec![] as Vec<u8>);
assert_eq!(view.seal(), vec![mix_hash, nonce]);
}
}

25
ethcore/src/views/mod.rs Normal file
View File

@ -0,0 +1,25 @@
// 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 oriented views onto rlp.
mod block;
mod header;
mod transaction;
pub use self::block::BlockView;
pub use self::header::HeaderView;
pub use self::transaction::TransactionView;

View File

@ -0,0 +1,97 @@
// 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/>.
//! View onto transaction rlp
use util::{Rlp, U256, Bytes, Hashable, H256, View};
/// View onto transaction rlp.
pub struct TransactionView<'a> {
rlp: Rlp<'a>
}
impl<'a> TransactionView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> TransactionView<'a> {
TransactionView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> {
TransactionView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Get the nonce field of the transaction.
pub fn nonce(&self) -> U256 { self.rlp.val_at(0) }
/// Get the gas_price field of the transaction.
pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) }
/// Get the gas field of the transaction.
pub fn gas(&self) -> U256 { self.rlp.val_at(2) }
/// Get the value field of the transaction.
pub fn value(&self) -> U256 { self.rlp.val_at(4) }
/// Get the data field of the transaction.
pub fn data(&self) -> Bytes { self.rlp.val_at(5) }
/// Get the v field of the transaction.
pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 }
/// Get the r field of the transaction.
pub fn r(&self) -> U256 { self.rlp.val_at(7) }
/// Get the s field of the transaction.
pub fn s(&self) -> U256 { self.rlp.val_at(8) }
}
impl<'a> Hashable for TransactionView<'a> {
fn sha3(&self) -> H256 {
self.rlp.as_raw().sha3()
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rustc_serialize::hex::FromHex;
use util::U256;
use super::TransactionView;
#[test]
fn test_transaction_view() {
let rlp = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap();
let view = TransactionView::new(&rlp);
assert_eq!(view.nonce(), U256::from(0));
assert_eq!(view.gas_price(), U256::from(1));
assert_eq!(view.gas(), U256::from(0x61a8));
assert_eq!(view.value(), U256::from(0xa));
assert_eq!(view.data(), "0000000000000000000000000000000000000000000000000000000000".from_hex().unwrap());
assert_eq!(view.r(), U256::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353").unwrap());
assert_eq!(view.s(), U256::from_str("efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
assert_eq!(view.v(), 0x1b);
}
}

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
PARITY_DEB_URL=https://github.com/ethcore/parity/releases/download/v1.2.0/parity_linux_1.2.0-0_amd64.deb PARITY_DEB_URL=https://github.com/ethcore/parity/releases/download/v1.2.1/parity_linux_1.2.1-0_amd64.deb
function run_installer() function run_installer()

View File

@ -42,14 +42,12 @@ Protocol Options:
--keys-path PATH Specify the path for JSON key files to be found --keys-path PATH Specify the path for JSON key files to be found
[default: $HOME/.parity/keys]. [default: $HOME/.parity/keys].
--identity NAME Specify your node's name. --identity NAME Specify your node's name.
--fork POLICY Specifies the client's fork policy. POLICY must be
DAO-Rescue Soft-fork Options: one of:
--help-rescue-dao Does nothing - on by default. dogmatic - sticks rigidly to the standard chain.
--dont-help-rescue-dao Votes against the DAO-rescue soft-fork, but supports dao-soft - votes for the DAO-rescue soft-fork.
it if it is triggered anyway. normal - goes with whatever fork is decided but
Equivalent to --gas-floor-target=3141592. votes for none. [default: normal].
--dogmatic Ignores all DAO-rescue soft-fork behaviour. Even if
it means losing mining rewards.
Account Options: Account Options:
--unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution.
@ -133,6 +131,29 @@ Sealing/Mining Options:
NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.
--force-sealing Force the node to author new blocks as if it were --force-sealing Force the node to author new blocks as if it were
always sealing/mining. always sealing/mining.
--reseal-on-txs SET Specify which transactions should force the node
to reseal a block. SET is one of:
none - never reseal on new transactions;
own - reseal only on a new local transaction;
ext - reseal only on a new external transaction;
all - reseal on all new transactions [default: all].
--reseal-min-period MS Specify the minimum time between reseals from
incoming transactions. MS is time measured in
milliseconds [default: 2000].
--work-queue-size ITEMS Specify the number of historical work packages
which are kept cached lest a solution is found for
them later. High values take more memory but result
in fewer unusable solutions [default: 20].
--tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas
a single transaction may have for it to be mined.
--relay-set SET Set of transactions to relay. SET may be:
cheap - Relay any transaction in the queue (this
may include invalid transactions);
strict - Relay only executed transactions (this
guarantees we don't relay invalid transactions, but
means we relay nothing if not mining);
lenient - Same as strict when mining, and cheap
when not [default: cheap].
--usd-per-tx USD Amount of USD to be paid for a basic transaction --usd-per-tx USD Amount of USD to be paid for a basic transaction
[default: 0.005]. The minimum gas price is set [default: 0.005]. The minimum gas price is set
accordingly. accordingly.
@ -141,13 +162,19 @@ Sealing/Mining Options:
web service in turn and fallback on the last known web service in turn and fallback on the last known
good value [default: auto]. good value [default: auto].
--gas-floor-target GAS Amount of gas per block to target when sealing a new --gas-floor-target GAS Amount of gas per block to target when sealing a new
block [default: 3141592]. block [default: 4700000].
--gas-cap GAS A cap on how large we will raise the gas limit per --gas-cap GAS A cap on how large we will raise the gas limit per
block due to transaction volume [default: 3141592]. block due to transaction volume [default: 6283184].
--extra-data STRING Specify a custom extra-data for authored blocks, no --extra-data STRING Specify a custom extra-data for authored blocks, no
more than 32 characters. more than 32 characters.
--tx-limit LIMIT Limit of transactions kept in the queue (waiting to --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting
be included in next block) [default: 1024]. to be included in next block) [default: 1024].
--remove-solved Move solved blocks from the work package queue
instead of cloning them. This gives a slightly
faster import speed, but means that extra solutions
submitted for the same work package will go unused.
--notify-work URLS URLs to which work package notifications are pushed.
URLS should be a comma-delimited list of HTTP URLs.
Footprint Options: Footprint Options:
--tracing BOOL Indicates if full transaction tracing should be --tracing BOOL Indicates if full transaction tracing should be
@ -170,7 +197,12 @@ Footprint Options:
--cache MEGABYTES Set total amount of discretionary memory to use for --cache MEGABYTES Set total amount of discretionary memory to use for
the entire system, overrides other cache and queue the entire system, overrides other cache and queue
options. options.
--db-cache-size MB Database cache size.
Database Options:
--db-cache-size MB Override RocksDB database cache size.
--db-compaction TYPE Database compaction type. TYPE may be one of:
ssd - suitable for SSDs and fast HDDs;
hdd - suitable for slow HDDs [default: ssd].
Import/Export Options: Import/Export Options:
--from BLOCK Export from block BLOCK, which may be an index or --from BLOCK Export from block BLOCK, which may be an index or
@ -241,8 +273,7 @@ pub struct Args {
pub flag_chain: String, pub flag_chain: String,
pub flag_db_path: String, pub flag_db_path: String,
pub flag_identity: String, pub flag_identity: String,
pub flag_dont_help_rescue_dao: bool, pub flag_fork: String,
pub flag_dogmatic: bool,
pub flag_unlock: Option<String>, pub flag_unlock: Option<String>,
pub flag_password: Vec<String>, pub flag_password: Vec<String>,
pub flag_cache: Option<usize>, pub flag_cache: Option<usize>,
@ -283,13 +314,20 @@ pub struct Args {
pub flag_signer_path: String, pub flag_signer_path: String,
pub flag_no_token: bool, pub flag_no_token: bool,
pub flag_force_sealing: bool, pub flag_force_sealing: bool,
pub flag_reseal_on_txs: String,
pub flag_reseal_min_period: u64,
pub flag_work_queue_size: usize,
pub flag_remove_solved: bool,
pub flag_tx_gas_limit: Option<String>,
pub flag_relay_set: String,
pub flag_author: Option<String>, pub flag_author: Option<String>,
pub flag_usd_per_tx: String, pub flag_usd_per_tx: String,
pub flag_usd_per_eth: String, pub flag_usd_per_eth: String,
pub flag_gas_floor_target: String, pub flag_gas_floor_target: String,
pub flag_gas_cap: String, pub flag_gas_cap: String,
pub flag_extra_data: Option<String>, pub flag_extra_data: Option<String>,
pub flag_tx_limit: usize, pub flag_tx_queue_size: usize,
pub flag_notify_work: Option<String>,
pub flag_logging: Option<String>, pub flag_logging: Option<String>,
pub flag_version: bool, pub flag_version: bool,
pub flag_from: String, pub flag_from: String,
@ -323,6 +361,7 @@ pub struct Args {
pub flag_ipcpath: Option<String>, pub flag_ipcpath: Option<String>,
pub flag_ipcapi: Option<String>, pub flag_ipcapi: Option<String>,
pub flag_db_cache_size: Option<usize>, pub flag_db_cache_size: Option<usize>,
pub flag_db_compaction: String,
} }
pub fn print_version() { pub fn print_version() {

View File

@ -16,6 +16,7 @@
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::time::Duration;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::net::{SocketAddr, IpAddr}; use std::net::{SocketAddr, IpAddr};
use std::path::PathBuf; use std::path::PathBuf;
@ -24,9 +25,11 @@ use docopt::Docopt;
use die::*; use die::*;
use util::*; use util::*;
use util::log::Colour::*;
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
use ethcore::client::{append_path, get_db_path, ClientConfig, Switch, VMType}; use ethcore::client::{append_path, get_db_path, ClientConfig, DatabaseCompactionProfile, Switch, VMType};
use ethcore::miner::{MinerOptions, PendingSet};
use ethcore::ethereum; use ethcore::ethereum;
use ethcore::spec::Spec; use ethcore::spec::Spec;
use ethsync::SyncConfig; use ethsync::SyncConfig;
@ -44,6 +47,13 @@ pub struct Directories {
pub signer: String, pub signer: String,
} }
#[derive(Eq, PartialEq, Debug)]
pub enum Policy {
DaoSoft,
Normal,
Dogmatic,
}
impl Configuration { impl Configuration {
pub fn parse() -> Self { pub fn parse() -> Self {
Configuration { Configuration {
@ -67,6 +77,45 @@ impl Configuration {
self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32 self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32
} }
fn decode_u256(d: &str, argument: &str) -> U256 {
U256::from_dec_str(d).unwrap_or_else(|_|
U256::from_str(clean_0x(d)).unwrap_or_else(|_|
die!("{}: Invalid numeric value for {}. Must be either a decimal or a hex number.", d, argument)
)
)
}
fn work_notify(&self) -> Vec<String> {
self.args.flag_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect())
}
pub fn miner_options(&self) -> MinerOptions {
let (own, ext) = match self.args.flag_reseal_on_txs.as_str() {
"none" => (false, false),
"own" => (true, false),
"ext" => (false, true),
"all" => (true, true),
x => die!("{}: Invalid value for --reseal option. Use --help for more information.", x)
};
MinerOptions {
new_work_notify: self.work_notify(),
force_sealing: self.args.flag_force_sealing,
reseal_on_external_tx: ext,
reseal_on_own_tx: own,
tx_gas_limit: self.args.flag_tx_gas_limit.as_ref().map_or(!U256::zero(), |d| Self::decode_u256(d, "--tx-gas-limit")),
tx_queue_size: self.args.flag_tx_queue_size,
pending_set: match self.args.flag_relay_set.as_str() {
"cheap" => PendingSet::AlwaysQueue,
"strict" => PendingSet::AlwaysSealing,
"lenient" => PendingSet::SealingOrElseQueue,
x => die!("{}: Invalid value for --relay-set option. Use --help for more information.", x)
},
reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period),
work_queue_size: self.args.flag_work_queue_size,
enable_resubmission: !self.args.flag_remove_solved,
}
}
pub fn author(&self) -> Option<Address> { pub fn author(&self) -> Option<Address> {
self.args.flag_etherbase.as_ref() self.args.flag_etherbase.as_ref()
.or(self.args.flag_author.as_ref()) .or(self.args.flag_author.as_ref())
@ -75,9 +124,18 @@ impl Configuration {
})) }))
} }
pub fn policy(&self) -> Policy {
match self.args.flag_fork.as_str() {
"dao-soft" => Policy::DaoSoft,
"normal" => Policy::Normal,
"dogmatic" => Policy::Dogmatic,
x => die!("{}: Invalid value given for --policy option. Use --help for more info.", x)
}
}
pub fn gas_floor_target(&self) -> U256 { pub fn gas_floor_target(&self) -> U256 {
if self.args.flag_dont_help_rescue_dao || self.args.flag_dogmatic { if self.policy() == Policy::DaoSoft {
4_700_000.into() 3_141_592.into()
} else { } else {
let d = &self.args.flag_gas_floor_target; let d = &self.args.flag_gas_floor_target;
U256::from_dec_str(d).unwrap_or_else(|_| { U256::from_dec_str(d).unwrap_or_else(|_| {
@ -87,8 +145,8 @@ impl Configuration {
} }
pub fn gas_ceil_target(&self) -> U256 { pub fn gas_ceil_target(&self) -> U256 {
if self.args.flag_dont_help_rescue_dao || self.args.flag_dogmatic { if self.policy() == Policy::DaoSoft {
10_000_000.into() 3_141_592.into()
} else { } else {
let d = &self.args.flag_gas_cap; let d = &self.args.flag_gas_cap;
U256::from_dec_str(d).unwrap_or_else(|_| { U256::from_dec_str(d).unwrap_or_else(|_| {
@ -124,7 +182,7 @@ impl Configuration {
let wei_per_usd: f32 = 1.0e18 / usd_per_eth; let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
let gas_per_tx: f32 = 21000.0; let gas_per_tx: f32 = 21000.0;
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas); info!("Using a conversion rate of Ξ1 = {} ({} wei/gas)", paint(White.bold(), format!("US${}", usd_per_eth)), paint(Yellow.bold(), format!("{}", wei_per_gas)));
U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap()
} }
} }
@ -140,7 +198,7 @@ impl Configuration {
pub fn spec(&self) -> Spec { pub fn spec(&self) -> Spec {
match self.chain().as_str() { match self.chain().as_str() {
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(!self.args.flag_dogmatic), "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(self.policy() != Policy::Dogmatic),
"morden" | "testnet" => ethereum::new_morden(), "morden" | "testnet" => ethereum::new_morden(),
"olympic" => ethereum::new_olympic(), "olympic" => ethereum::new_olympic(),
f => Spec::load(contents(f).unwrap_or_else(|_| { f => Spec::load(contents(f).unwrap_or_else(|_| {
@ -227,7 +285,7 @@ impl Configuration {
let mut latest_era = None; let mut latest_era = None;
let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted]; let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted];
for i in jdb_types.into_iter() { for i in jdb_types.into_iter() {
let db = journaldb::new(&append_path(&get_db_path(Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i, None); let db = journaldb::new(&append_path(&get_db_path(Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i, kvdb::DatabaseConfig::default());
trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era()); trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era());
match (latest_era, db.latest_era()) { match (latest_era, db.latest_era()) {
(Some(best), Some(this)) if best >= this => {} (Some(best), Some(this)) if best >= this => {}
@ -278,6 +336,13 @@ impl Configuration {
// forced state db cache size if provided // forced state db cache size if provided
client_config.db_cache_size = self.args.flag_db_cache_size.and_then(|cs| Some(cs / 4)); client_config.db_cache_size = self.args.flag_db_cache_size.and_then(|cs| Some(cs / 4));
// compaction profile
client_config.db_compaction = match self.args.flag_db_compaction.as_str() {
"ssd" => DatabaseCompactionProfile::Default,
"hdd" => DatabaseCompactionProfile::HDD,
_ => { die!("Invalid compaction profile given (--db-compaction argument), expected hdd/default."); }
};
if self.args.flag_jitvm { if self.args.flag_jitvm {
client_config.vm_type = VMType::jit().unwrap_or_else(|| die!("Parity built without jit vm.")) client_config.vm_type = VMType::jit().unwrap_or_else(|| die!("Parity built without jit vm."))
} }

View File

@ -80,7 +80,7 @@ use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
use ctrlc::CtrlC; use ctrlc::CtrlC;
use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError}; use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version};
use util::panics::{MayPanic, ForwardPanic, PanicHandler}; use util::panics::{MayPanic, ForwardPanic, PanicHandler};
use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path}; use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path};
use ethcore::error::{Error, ImportError}; use ethcore::error::{Error, ImportError};
@ -184,10 +184,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
// Setup logging // Setup logging
let logger = setup_log::setup_log(&conf.args.flag_logging); let logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color());
// Raise fdlimit // Raise fdlimit
unsafe { ::fdlimit::raise_fd_limit(); } unsafe { ::fdlimit::raise_fd_limit(); }
info!("Starting {}", paint(Colour::White.bold(), format!("{}", version())));
let net_settings = conf.net_settings(&spec); let net_settings = conf.net_settings(&spec);
let sync_config = conf.sync_config(&spec); let sync_config = conf.sync_config(&spec);
@ -208,13 +210,13 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
let account_service = Arc::new(conf.account_service()); let account_service = Arc::new(conf.account_service());
// Miner // Miner
let miner = Miner::with_accounts(conf.args.flag_force_sealing, conf.spec(), account_service.clone()); let miner = Miner::new(conf.miner_options(), conf.spec(), Some(account_service.clone()));
miner.set_author(conf.author().unwrap_or(Default::default())); miner.set_author(conf.author().unwrap_or_default());
miner.set_gas_floor_target(conf.gas_floor_target()); miner.set_gas_floor_target(conf.gas_floor_target());
miner.set_gas_ceil_target(conf.gas_ceil_target()); miner.set_gas_ceil_target(conf.gas_ceil_target());
miner.set_extra_data(conf.extra_data()); miner.set_extra_data(conf.extra_data());
miner.set_minimal_gas_price(conf.gas_price()); miner.set_minimal_gas_price(conf.gas_price());
miner.set_transactions_limit(conf.args.flag_tx_limit); miner.set_transactions_limit(conf.args.flag_tx_queue_size);
// Build client // Build client
let mut service = ClientService::start( let mut service = ClientService::start(
@ -320,6 +322,8 @@ fn execute_export(conf: Configuration) {
// Setup panic handler // Setup panic handler
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
// Setup logging
let _logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color());
// Raise fdlimit // Raise fdlimit
unsafe { ::fdlimit::raise_fd_limit(); } unsafe { ::fdlimit::raise_fd_limit(); }
@ -341,7 +345,7 @@ fn execute_export(conf: Configuration) {
// Build client // Build client
let service = ClientService::start( let service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), false client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::with_spec(conf.spec())), false
).unwrap_or_else(|e| die_with_error("Client", e)); ).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service); panic_handler.forward_from(&service);
@ -392,6 +396,8 @@ fn execute_import(conf: Configuration) {
// Setup panic handler // Setup panic handler
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
// Setup logging
let _logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color());
// Raise fdlimit // Raise fdlimit
unsafe { ::fdlimit::raise_fd_limit(); } unsafe { ::fdlimit::raise_fd_limit(); }
@ -413,7 +419,7 @@ fn execute_import(conf: Configuration) {
// Build client // Build client
let service = ClientService::start( let service = ClientService::start(
client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), false client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::with_spec(conf.spec())), false
).unwrap_or_else(|e| die_with_error("Client", e)); ).unwrap_or_else(|e| die_with_error("Client", e));
panic_handler.forward_from(&service); panic_handler.forward_from(&service);

View File

@ -20,7 +20,7 @@ use std::io::{Read, Write, Error as IoError, ErrorKind};
use std::path::PathBuf; use std::path::PathBuf;
use std::fmt::{Display, Formatter, Error as FmtError}; use std::fmt::{Display, Formatter, Error as FmtError};
use util::migration::{Manager as MigrationManager, Config as MigrationConfig, MigrationIterator}; use util::migration::{Manager as MigrationManager, Config as MigrationConfig, MigrationIterator};
use util::kvdb::{Database, DatabaseConfig}; use util::kvdb::{Database, DatabaseConfig, CompactionProfile};
use ethcore::migrations; use ethcore::migrations;
/// Database is assumed to be at default version, when no version file is found. /// Database is assumed to be at default version, when no version file is found.
@ -163,6 +163,7 @@ fn migrate_database(version: u32, path: PathBuf, migrations: MigrationManager) -
prefix_size: None, prefix_size: None,
max_open_files: 64, max_open_files: 64,
cache_size: None, cache_size: None,
compaction: CompactionProfile::default(),
}; };
// open old database // open old database

View File

@ -19,10 +19,10 @@ use std::env;
use std::sync::Arc; use std::sync::Arc;
use time; use time;
use env_logger::LogBuilder; use env_logger::LogBuilder;
use util::{RotatingLogger}; use util::RotatingLogger;
/// Sets up the logger /// Sets up the logger
pub fn setup_log(init: &Option<String>) -> Arc<RotatingLogger> { pub fn setup_log(init: &Option<String>, enable_color: bool) -> Arc<RotatingLogger> {
use rlog::*; use rlog::*;
let mut levels = String::new(); let mut levels = String::new();
@ -43,7 +43,7 @@ pub fn setup_log(init: &Option<String>) -> Arc<RotatingLogger> {
builder.parse(s); builder.parse(s);
} }
let logs = Arc::new(RotatingLogger::new(levels)); let logs = Arc::new(RotatingLogger::new(levels, enable_color));
let logger = logs.clone(); let logger = logs.clone();
let format = move |record: &LogRecord| { let format = move |record: &LogRecord| {
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();

View File

@ -18,6 +18,8 @@
extern crate ethash; extern crate ethash;
use std::thread;
use std::time::{Instant, Duration};
use std::sync::{Arc, Weak, Mutex}; use std::sync::{Arc, Weak, Mutex};
use std::ops::Deref; use std::ops::Deref;
use ethsync::{SyncProvider, SyncState}; use ethsync::{SyncProvider, SyncState};
@ -479,6 +481,12 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
trace!(target: "miner", "Syncing. Cannot give any work."); trace!(target: "miner", "Syncing. Cannot give any work.");
return Err(no_work_err()); return Err(no_work_err());
} }
// Otherwise spin until our submitted block has been included.
let timeout = Instant::now() + Duration::from_millis(1000);
while Instant::now() < timeout && client.queue_info().total_queue_size() > 0 {
thread::sleep(Duration::from_millis(1));
}
} }
let miner = take_weak!(self.miner); let miner = take_weak!(self.miner);
@ -490,7 +498,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
let pow_hash = b.hash(); let pow_hash = b.hash();
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number()); let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number());
to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target)) to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target, &U256::from(b.block().header().number())))
}).unwrap_or(Err(Error::internal_error())) // no work found. }).unwrap_or(Err(Error::internal_error())) // no work found.
}, },
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params())

View File

@ -29,6 +29,9 @@ use v1::impls::{default_gas_price, sign_and_dispatch};
fn fill_optional_fields<C, M>(request: &mut TransactionRequest, client: &C, miner: &M) fn fill_optional_fields<C, M>(request: &mut TransactionRequest, client: &C, miner: &M)
where C: MiningBlockChainClient, M: MinerService { where C: MiningBlockChainClient, M: MinerService {
if request.value.is_none() {
request.value = Some(U256::zero());
}
if request.gas.is_none() { if request.gas.is_none() {
request.gas = Some(miner.sensible_gas_limit()); request.gas = Some(miner.sensible_gas_limit());
} }

View File

@ -22,7 +22,7 @@ use jsonrpc_core::*;
use ethcore::miner::MinerService; use ethcore::miner::MinerService;
use ethcore::service::SyncMessage; use ethcore::service::SyncMessage;
use v1::traits::EthcoreSet; use v1::traits::EthcoreSet;
use v1::types::{Bytes}; use v1::types::Bytes;
/// Ethcore-specific rpc interface for operations altering the settings. /// Ethcore-specific rpc interface for operations altering the settings.
pub struct EthcoreSetClient<M> where pub struct EthcoreSetClient<M> where
@ -86,6 +86,13 @@ impl<M> EthcoreSet for EthcoreSetClient<M> where M: MinerService + 'static {
}) })
} }
fn set_tx_gas_limit(&self, params: Params) -> Result<Value, Error> {
from_params::<(U256,)>(params).and_then(|(limit,)| {
take_weak!(self.miner).set_tx_gas_limit(limit.into());
to_value(&true)
})
}
fn add_reserved_peer(&self, params: Params) -> Result<Value, Error> { fn add_reserved_peer(&self, params: Params) -> Result<Value, Error> {
from_params::<(String,)>(params).and_then(|(peer,)| { from_params::<(String,)>(params).and_then(|(peer,)| {
match take_weak!(self.net).add_reserved_peer(&peer) { match take_weak!(self.net).add_reserved_peer(&peer) {

View File

@ -165,7 +165,7 @@ fn transaction_error(error: EthcoreError) -> Error {
"There is too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into() "There is too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
}, },
InsufficientGasPrice { minimal, got } => { InsufficientGasPrice { minimal, got } => {
format!("Transaction fee is to low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got) format!("Transaction fee is too low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got)
}, },
InsufficientBalance { balance, cost } => { InsufficientBalance { balance, cost } => {
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance) format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)

View File

@ -17,6 +17,7 @@
//! rpc integration tests. //! rpc integration tests.
use std::sync::Arc; use std::sync::Arc;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration;
use ethcore::client::{BlockChainClient, Client, ClientConfig}; use ethcore::client::{BlockChainClient, Client, ClientConfig};
use ethcore::ids::BlockID; use ethcore::ids::BlockID;
@ -24,12 +25,12 @@ use ethcore::spec::{Genesis, Spec};
use ethcore::block::Block; use ethcore::block::Block;
use ethcore::views::BlockView; use ethcore::views::BlockView;
use ethcore::ethereum; use ethcore::ethereum;
use ethcore::miner::{MinerService, ExternalMiner, Miner}; use ethcore::miner::{MinerOptions, MinerService, ExternalMiner, Miner, PendingSet};
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use devtools::RandomTempPath; use devtools::RandomTempPath;
use util::Hashable; use util::Hashable;
use util::io::IoChannel; use util::io::IoChannel;
use util::{U256, H256}; use util::{U256, H256, Uint};
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use ethjson::blockchain::BlockChain; use ethjson::blockchain::BlockChain;
@ -49,7 +50,22 @@ fn sync_provider() -> Arc<TestSyncProvider> {
} }
fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> { fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
Miner::with_accounts(true, spec, accounts) Miner::new(
MinerOptions {
new_work_notify: vec![],
force_sealing: true,
reseal_on_external_tx: true,
reseal_on_own_tx: true,
tx_queue_size: 1024,
tx_gas_limit: !U256::zero(),
pending_set: PendingSet::SealingOrElseQueue,
reseal_min_period: Duration::from_secs(0),
work_queue_size: 50,
enable_resubmission: true,
},
spec,
Some(accounts)
)
} }
fn make_spec(chain: &BlockChain) -> Spec { fn make_spec(chain: &BlockChain) -> Spec {

View File

@ -43,6 +43,7 @@ pub struct TestMinerService {
author: RwLock<Address>, author: RwLock<Address>,
extra_data: RwLock<Bytes>, extra_data: RwLock<Bytes>,
limit: RwLock<usize>, limit: RwLock<usize>,
tx_gas_limit: RwLock<U256>,
} }
impl Default for TestMinerService { impl Default for TestMinerService {
@ -58,6 +59,7 @@ impl Default for TestMinerService {
author: RwLock::new(Address::zero()), author: RwLock::new(Address::zero()),
extra_data: RwLock::new(vec![1, 2, 3, 4]), extra_data: RwLock::new(vec![1, 2, 3, 4]),
limit: RwLock::new(1024), limit: RwLock::new(1024),
tx_gas_limit: RwLock::new(!U256::zero()),
} }
} }
} }
@ -99,6 +101,10 @@ impl MinerService for TestMinerService {
*self.limit.write().unwrap() = limit; *self.limit.write().unwrap() = limit;
} }
fn set_tx_gas_limit(&self, limit: U256) {
*self.tx_gas_limit.write().unwrap() = limit;
}
fn transactions_limit(&self) -> usize { fn transactions_limit(&self) -> usize {
*self.limit.read().unwrap() *self.limit.read().unwrap()
} }

View File

@ -32,7 +32,7 @@ fn client_service() -> Arc<TestBlockChainClient> {
} }
fn logger() -> Arc<RotatingLogger> { fn logger() -> Arc<RotatingLogger> {
Arc::new(RotatingLogger::new("rpc=trace".to_owned())) Arc::new(RotatingLogger::new("rpc=trace".to_owned(), false))
} }
fn settings() -> Arc<NetworkSettings> { fn settings() -> Arc<NetworkSettings> {

View File

@ -40,6 +40,9 @@ pub trait EthcoreSet: Sized + Send + Sync + 'static {
/// Sets the limits for transaction queue. /// Sets the limits for transaction queue.
fn set_transactions_limit(&self, _: Params) -> Result<Value, Error>; fn set_transactions_limit(&self, _: Params) -> Result<Value, Error>;
/// Sets the maximum amount of gas a single transaction may consume.
fn set_tx_gas_limit(&self, _: Params) -> Result<Value, Error>;
/// Add a reserved peer. /// Add a reserved peer.
fn add_reserved_peer(&self, _: Params) -> Result<Value, Error>; fn add_reserved_peer(&self, _: Params) -> Result<Value, Error>;
@ -60,6 +63,7 @@ pub trait EthcoreSet: Sized + Send + Sync + 'static {
delegate.add_method("ethcore_setGasCeilTarget", EthcoreSet::set_gas_ceil_target); delegate.add_method("ethcore_setGasCeilTarget", EthcoreSet::set_gas_ceil_target);
delegate.add_method("ethcore_setExtraData", EthcoreSet::set_extra_data); delegate.add_method("ethcore_setExtraData", EthcoreSet::set_extra_data);
delegate.add_method("ethcore_setAuthor", EthcoreSet::set_author); delegate.add_method("ethcore_setAuthor", EthcoreSet::set_author);
delegate.add_method("ethcore_setMaxTransactionGas", EthcoreSet::set_tx_gas_limit);
delegate.add_method("ethcore_setTransactionsLimit", EthcoreSet::set_transactions_limit); delegate.add_method("ethcore_setTransactionsLimit", EthcoreSet::set_transactions_limit);
delegate.add_method("ethcore_addReservedPeer", EthcoreSet::add_reserved_peer); delegate.add_method("ethcore_addReservedPeer", EthcoreSet::add_reserved_peer);
delegate.add_method("ethcore_removeReservedPeer", EthcoreSet::remove_reserved_peer); delegate.add_method("ethcore_removeReservedPeer", EthcoreSet::remove_reserved_peer);

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde_json::Value; use serde_json::Value;
/// Optional value /// Optional value
@ -26,13 +26,22 @@ pub enum OptionalValue<T> where T: Serialize {
Null Null
} }
impl<T> Default for OptionalValue<T> where T: Serialize { impl<T> Default for OptionalValue<T> where T: Serialize + Deserialize {
fn default() -> Self { fn default() -> Self {
OptionalValue::Null OptionalValue::Null
} }
} }
impl<T> Serialize for OptionalValue<T> where T: Serialize { impl<T> Into<Option<T>> for OptionalValue<T> where T: Serialize + Deserialize {
fn into(self) -> Option<T> {
match self {
OptionalValue::Null => None,
OptionalValue::Value(t) => Some(t),
}
}
}
impl<T> Serialize for OptionalValue<T> where T: Serialize + Deserialize {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer { where S: Serializer {
match *self { match *self {
@ -42,6 +51,17 @@ impl<T> Serialize for OptionalValue<T> where T: Serialize {
} }
} }
impl<T> Deserialize for OptionalValue<T> where T: Serialize + Deserialize {
fn deserialize<D>(deserializer: &mut D) -> Result<OptionalValue<T>, D::Error>
where D: Deserializer {
let deser_result: Result<T, D::Error> = Deserialize::deserialize(deserializer);
match deser_result {
Ok(t) => Ok(OptionalValue::Value(t)),
Err(_) => Ok(OptionalValue::Null),
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serde_json; use serde_json;

View File

@ -15,10 +15,10 @@ rand = "0.3.14"
jsonrpc-core = "2.0" jsonrpc-core = "2.0"
log = "0.3" log = "0.3"
env_logger = "0.3" env_logger = "0.3"
ws = { git = "https://github.com/ethcore/ws-rs.git" } ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "stable" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
ethcore-rpc = { path = "../rpc" } ethcore-rpc = { path = "../rpc" }
parity-minimal-sysui = { git = "https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git" } parity-minimal-sysui = { git = "https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git", version = "0.2.0" }
clippy = { version = "0.0.77", optional = true} clippy = { version = "0.0.77", optional = true}

View File

@ -120,7 +120,7 @@ impl<T: TimeProvider> AuthCodes<T> {
.filter_map(|f| String::from_utf8(f.to_vec()).ok()) .filter_map(|f| String::from_utf8(f.to_vec()).ok())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("-"); .join("-");
info!(target: "signer", "New authentication token generated."); trace!(target: "signer", "New authentication token generated.");
self.codes.push(code); self.codes.push(code);
Ok(readable_code) Ok(readable_code)
} }

View File

@ -110,7 +110,17 @@ impl Server {
// Spawn a thread with event loop // Spawn a thread with event loop
let handle = thread::spawn(move || { let handle = thread::spawn(move || {
ph.catch_panic(move || { ph.catch_panic(move || {
ws.listen(addr).unwrap() match ws.listen(addr).map_err(ServerError::from) {
Err(ServerError::IoError(io)) => die(format!(
"Signer: Could not start listening on specified address. Make sure that no other instance is running on Signer's port. Details: {:?}",
io
)),
Err(any_error) => die(format!(
"Signer: Unknown error occured when starting Signer. Details: {:?}",
any_error
)),
Ok(server) => server,
}
}).unwrap() }).unwrap()
}); });
@ -123,7 +133,11 @@ impl Server {
// TODO [ToDr] Some better structure here for messages. // TODO [ToDr] Some better structure here for messages.
broadcaster.send("new_message").unwrap(); broadcaster.send("new_message").unwrap();
}).expect("It's the only place we are running start_listening. It shouldn't fail."); }).expect("It's the only place we are running start_listening. It shouldn't fail.");
broadcaster.shutdown().expect("Broadcaster should close gently.") let res = broadcaster.shutdown();
if let Err(e) = res {
warn!("Signer: Broadcaster was not closed cleanly. Details: {:?}", e);
}
}).unwrap() }).unwrap()
}); });
@ -148,5 +162,11 @@ impl Drop for Server {
self.queue.finish(); self.queue.finish();
self.broadcaster_handle.take().unwrap().join().unwrap(); self.broadcaster_handle.take().unwrap().join().unwrap();
self.handle.take().unwrap().join().unwrap(); self.handle.take().unwrap().join().unwrap();
} }
} }
fn die(msg: String) -> ! {
println!("ERROR: {}", msg);
std::process::exit(1);
}

View File

@ -62,6 +62,19 @@ fn auth_is_valid(codes: &Path, protocols: ws::Result<Vec<&str>>) -> bool {
} }
} }
fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response {
let content_len = format!("{}", response.len());
{
let mut headers = response.headers_mut();
headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec()));
headers.push(("Server".into(), b"Parity/SignerUI".to_vec()));
headers.push(("Content-Length".into(), content_len.as_bytes().to_vec()));
headers.push(("Content-Type".into(), mime.as_bytes().to_vec()));
headers.push(("Connection".into(), b"close".to_vec()));
}
response
}
pub struct Session { pub struct Session {
out: ws::Sender, out: ws::Sender,
self_origin: String, self_origin: String,
@ -98,26 +111,13 @@ impl ws::Handler for Session {
} }
// Otherwise try to serve a page. // Otherwise try to serve a page.
sysui::handle(req.resource()) Ok(sysui::handle(req.resource())
.map_or_else( .map_or_else(
// return error // return 404 not found
|| Ok(ws::Response::not_found("Page not found".into())), || add_headers(ws::Response::not_found("Not found".into()), "text/plain"),
// or serve the file // or serve the file
|f| { |f| add_headers(ws::Response::ok(f.content.into()), &f.mime)
let content_len = format!("{}", f.content.as_bytes().len()); ))
let mut res = ws::Response::ok(f.content.into());
{
let mut headers = res.headers_mut();
headers.push(("Server".into(), b"Parity/SignerUI".to_vec()));
headers.push(("Connection".into(), b"Closed".to_vec()));
headers.push(("Content-Length".into(), content_len.as_bytes().to_vec()));
headers.push(("Content-Type".into(), f.mime.as_bytes().to_vec()));
if !f.safe_to_embed {
headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec()));
}
}
Ok(res)
})
} }
fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {

View File

@ -1231,6 +1231,14 @@ impl ChainSync {
rlp_stream.out() rlp_stream.out()
} }
/// creates latest block rlp for the given client
fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes {
let mut rlp_stream = RlpStream::new_list(2);
rlp_stream.append_raw(&chain.block(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed"), 1);
rlp_stream.append(&chain.block_total_difficulty(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed."));
rlp_stream.out()
}
/// returns peer ids that have less blocks than our chain /// returns peer ids that have less blocks than our chain
fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &SyncIo) -> Vec<(PeerId, BlockNumber)> { fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &SyncIo) -> Vec<(PeerId, BlockNumber)> {
let latest_hash = chain_info.best_block_hash; let latest_hash = chain_info.best_block_hash;
@ -1250,7 +1258,6 @@ impl ChainSync {
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
fn select_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> Vec<(PeerId, BlockNumber)> { fn select_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> Vec<(PeerId, BlockNumber)> {
use rand::Rng; use rand::Rng;
let mut lagging_peers = self.get_lagging_peers(chain_info, io); let mut lagging_peers = self.get_lagging_peers(chain_info, io);
@ -1263,13 +1270,24 @@ impl ChainSync {
} }
/// propagates latest block to lagging peers /// propagates latest block to lagging peers
fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize { fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, sealed: &[H256]) -> usize {
let lucky_peers = self.select_lagging_peers(chain_info, io); let lucky_peers: Vec<_> = if sealed.is_empty() {
self.select_lagging_peers(chain_info, io).iter().map(|&(id, _)| id).collect()
} else {
self.peers.keys().cloned().collect()
};
trace!(target: "sync", "Sending NewBlocks to {:?}", lucky_peers); trace!(target: "sync", "Sending NewBlocks to {:?}", lucky_peers);
let mut sent = 0; let mut sent = 0;
for (peer_id, _) in lucky_peers { for peer_id in lucky_peers {
let rlp = ChainSync::create_latest_block_rlp(io.chain()); if sealed.is_empty() {
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp); let rlp = ChainSync::create_latest_block_rlp(io.chain());
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp);
} else {
for h in sealed {
let rlp = ChainSync::create_new_block_rlp(io.chain(), h);
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp);
}
}
self.peers.get_mut(&peer_id).unwrap().latest_hash = chain_info.best_block_hash.clone(); self.peers.get_mut(&peer_id).unwrap().latest_hash = chain_info.best_block_hash.clone();
self.peers.get_mut(&peer_id).unwrap().latest_number = Some(chain_info.best_block_number); self.peers.get_mut(&peer_id).unwrap().latest_number = Some(chain_info.best_block_number);
sent += 1; sent += 1;
@ -1314,7 +1332,7 @@ impl ChainSync {
return 0; return 0;
} }
let mut transactions = io.chain().all_transactions(); let mut transactions = io.chain().pending_transactions();
if transactions.is_empty() { if transactions.is_empty() {
return 0; return 0;
} }
@ -1346,11 +1364,11 @@ impl ChainSync {
sent sent
} }
fn propagate_latest_blocks(&mut self, io: &mut SyncIo) { fn propagate_latest_blocks(&mut self, io: &mut SyncIo, sealed: &[H256]) {
let chain_info = io.chain().chain_info(); let chain_info = io.chain().chain_info();
if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
let hashes = self.propagate_new_hashes(&chain_info, io); let hashes = self.propagate_new_hashes(&chain_info, io);
let blocks = self.propagate_blocks(&chain_info, io); let blocks = self.propagate_blocks(&chain_info, io, sealed);
if blocks != 0 || hashes != 0 { if blocks != 0 || hashes != 0 {
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
} }
@ -1365,10 +1383,10 @@ impl ChainSync {
} }
/// called when block is imported to chain, updates transactions queue and propagates the blocks /// called when block is imported to chain, updates transactions queue and propagates the blocks
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256], sealed: &[H256]) {
if io.is_chain_queue_empty() { if io.is_chain_queue_empty() {
// Propagate latests blocks // Propagate latests blocks
self.propagate_latest_blocks(io); self.propagate_latest_blocks(io, sealed);
} }
if !invalid.is_empty() { if !invalid.is_empty() {
trace!(target: "sync", "Bad blocks in the queue, restarting"); trace!(target: "sync", "Bad blocks in the queue, restarting");
@ -1637,7 +1655,26 @@ mod tests {
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client);
let chain_info = client.chain_info(); let chain_info = client.chain_info();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
let peer_count = sync.propagate_blocks(&chain_info, &mut io); let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[]);
// 1 message should be send
assert_eq!(1, io.queue.len());
// 1 peer should be updated
assert_eq!(1, peer_count);
// NEW_BLOCK_PACKET
assert_eq!(0x07, io.queue[0].packet_id);
}
#[test]
fn sends_sealed_block() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let hash = client.block_hash(BlockID::Number(99)).unwrap();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client);
let chain_info = client.chain_info();
let mut io = TestIo::new(&mut client, &mut queue, None);
let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[hash.clone()]);
// 1 message should be send // 1 message should be send
assert_eq!(1, io.queue.len()); assert_eq!(1, io.queue.len());
@ -1761,7 +1798,7 @@ mod tests {
let chain_info = client.chain_info(); let chain_info = client.chain_info();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
sync.propagate_blocks(&chain_info, &mut io); sync.propagate_blocks(&chain_info, &mut io, &[]);
let data = &io.queue[0].data.clone(); let data = &io.queue[0].data.clone();
let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(data)); let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(data));
@ -1794,7 +1831,7 @@ mod tests {
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks);
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[]);
assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0);
assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 1); assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 1);
} }
@ -1808,7 +1845,7 @@ mod tests {
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks);
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks); sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[]);
} }
// then // then
@ -1833,10 +1870,10 @@ mod tests {
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
// when // when
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[]);
assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0);
assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 0);
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks); sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[]);
// then // then
let status = io.chain.miner.status(); let status = io.chain.miner.status();

View File

@ -44,8 +44,14 @@
//! let mut service = NetworkService::new(NetworkConfiguration::new()).unwrap(); //! let mut service = NetworkService::new(NetworkConfiguration::new()).unwrap();
//! service.start().unwrap(); //! service.start().unwrap();
//! let dir = env::temp_dir(); //! let dir = env::temp_dir();
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(true), &dir, Arc::new(Miner::default()), service.io().channel()).unwrap(); //! let miner = Miner::new(Default::default(), ethereum::new_frontier(true), None);
//! let miner = Miner::new(false, ethereum::new_frontier(true)); //! let client = Client::new(
//! ClientConfig::default(),
//! ethereum::new_frontier(true),
//! &dir,
//! miner,
//! service.io().channel()
//! ).unwrap();
//! let sync = EthSync::new(SyncConfig::default(), client); //! let sync = EthSync::new(SyncConfig::default(), client);
//! EthSync::register(&mut service, sync); //! EthSync::register(&mut service, sync);
//! } //! }
@ -154,11 +160,13 @@ impl SyncProvider for EthSync {
} }
fn start_network(&self) { fn start_network(&self) {
self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StartNetwork)).expect("Error sending IO notification"); self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StartNetwork))
.unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
} }
fn stop_network(&self) { fn stop_network(&self) {
self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StopNetwork)).expect("Error sending IO notification"); self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StopNetwork))
.unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
} }
} }
@ -188,9 +196,9 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
#[cfg_attr(feature="dev", allow(single_match))] #[cfg_attr(feature="dev", allow(single_match))]
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) { fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
match *message { match *message {
SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted } => { SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted, ref sealed } => {
let mut sync_io = NetSyncIo::new(io, self.chain.deref()); let mut sync_io = NetSyncIo::new(io, self.chain.deref());
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted); self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted, sealed);
}, },
_ => {/* Ignore other messages */}, _ => {/* Ignore other messages */},
} }

View File

@ -173,6 +173,6 @@ impl TestNet {
pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) {
let mut peer = self.peer_mut(peer_id); let mut peer = self.peer_mut(peer_id);
peer.sync.write().unwrap().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[]); peer.sync.write().unwrap().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[], &[]);
} }
} }

View File

@ -37,6 +37,7 @@ vergen = "0.1"
target_info = "0.1" target_info = "0.1"
bigint = { path = "bigint" } bigint = { path = "bigint" }
chrono = "0.2" chrono = "0.2"
ansi_term = "0.7"
[features] [features]
default = [] default = []

View File

@ -557,7 +557,7 @@ macro_rules! construct_uint {
($name:ident, $n_words:expr) => ( ($name:ident, $n_words:expr) => (
/// Little-endian large integer type /// Little-endian large integer type
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct $name(pub [u64; $n_words]); pub struct $name(pub [u64; $n_words]);
impl Uint for $name { impl Uint for $name {
@ -1126,14 +1126,6 @@ macro_rules! construct_uint {
Ok(()) Ok(())
} }
} }
#[cfg_attr(feature="dev", allow(derive_hash_xor_eq))] // We are pretty sure it's ok.
impl Hash for $name {
fn hash<H>(&self, state: &mut H) where H: Hasher {
unsafe { state.write(::std::slice::from_raw_parts(self.0.as_ptr() as *mut u8, self.0.len() * 8)); }
state.finish();
}
}
); );
} }

View File

@ -132,15 +132,10 @@ macro_rules! impl_hash {
$size $size
} }
// TODO: remove once slice::clone_from_slice is stable
#[inline] #[inline]
fn clone_from_slice(&mut self, src: &[u8]) -> usize { fn clone_from_slice(&mut self, src: &[u8]) -> usize {
let min = ::std::cmp::min($size, src.len()); let min = cmp::min($size, src.len());
let dst = &mut self.deref_mut()[.. min]; self.0[..min].copy_from_slice(&src[..min]);
let src = &src[.. min];
for i in 0..min {
dst[i] = src[i];
}
min min
} }
@ -151,7 +146,7 @@ macro_rules! impl_hash {
} }
fn copy_to(&self, dest: &mut[u8]) { fn copy_to(&self, dest: &mut[u8]) {
let min = ::std::cmp::min($size, dest.len()); let min = cmp::min($size, dest.len());
dest[..min].copy_from_slice(&self.0[..min]); dest[..min].copy_from_slice(&self.0[..min]);
} }

View File

@ -135,8 +135,9 @@ impl<Message> IoContext<Message> where Message: Send + Clone + 'static {
} }
/// Broadcast a message to other IO clients /// Broadcast a message to other IO clients
pub fn message(&self, message: Message) { pub fn message(&self, message: Message) -> Result<(), UtilError> {
self.channel.send(message).expect("Error seding message"); try!(self.channel.send(message));
Ok(())
} }
/// Get message channel /// Get message channel
@ -351,7 +352,9 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
/// Starts IO event loop /// Starts IO event loop
pub fn start() -> Result<IoService<Message>, UtilError> { pub fn start() -> Result<IoService<Message>, UtilError> {
let panic_handler = PanicHandler::new_in_arc(); let panic_handler = PanicHandler::new_in_arc();
let mut event_loop = EventLoop::new().unwrap(); let mut config = EventLoopConfig::new();
config.messages_per_tick(1024);
let mut event_loop = EventLoop::configured(config).expect("Error creating event loop");
let channel = event_loop.channel(); let channel = event_loop.channel();
let panic = panic_handler.clone(); let panic = panic_handler.clone();
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
@ -390,7 +393,7 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
impl<Message> Drop for IoService<Message> where Message: Send + Sync + Clone { impl<Message> Drop for IoService<Message> where Message: Send + Sync + Clone {
fn drop(&mut self) { fn drop(&mut self) {
trace!(target: "shutdown", "[IoService] Closing..."); trace!(target: "shutdown", "[IoService] Closing...");
self.host_channel.send(IoMessage::Shutdown).unwrap(); self.host_channel.send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e));
self.thread.take().unwrap().join().ok(); self.thread.take().unwrap().join().ok();
trace!(target: "shutdown", "[IoService] Closed."); trace!(target: "shutdown", "[IoService] Closed.");
} }

View File

@ -48,13 +48,8 @@ pub struct ArchiveDB {
impl ArchiveDB { impl ArchiveDB {
/// Create a new instance from file /// Create a new instance from file
pub fn new(path: &str, cache_size: Option<usize>) -> ArchiveDB { pub fn new(path: &str, config: DatabaseConfig) -> ArchiveDB {
let opts = DatabaseConfig { let opts = config.prefix(DB_PREFIX_LEN);
// this must match account_db prefix
prefix_size: Some(DB_PREFIX_LEN),
max_open_files: 256,
cache_size: cache_size,
};
let backing = Database::open(&opts, path).unwrap_or_else(|e| { let backing = Database::open(&opts, path).unwrap_or_else(|e| {
panic!("Error opening state db: {}", e); panic!("Error opening state db: {}", e);
}); });
@ -80,7 +75,7 @@ impl ArchiveDB {
fn new_temp() -> ArchiveDB { fn new_temp() -> ArchiveDB {
let mut dir = env::temp_dir(); let mut dir = env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
Self::new(dir.to_str().unwrap(), None) Self::new(dir.to_str().unwrap(), DatabaseConfig::default())
} }
fn payload(&self, key: &H256) -> Option<Bytes> { fn payload(&self, key: &H256) -> Option<Bytes> {
@ -222,6 +217,7 @@ mod tests {
use super::*; use super::*;
use hashdb::*; use hashdb::*;
use journaldb::traits::JournalDB; use journaldb::traits::JournalDB;
use kvdb::DatabaseConfig;
#[test] #[test]
fn insert_same_in_fork() { fn insert_same_in_fork() {
@ -363,7 +359,7 @@ mod tests {
let bar = H256::random(); let bar = H256::random();
let foo = { let foo = {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.emplace(bar.clone(), b"bar".to_vec()); jdb.emplace(bar.clone(), b"bar".to_vec());
@ -372,13 +368,13 @@ mod tests {
}; };
{ {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.remove(&foo); jdb.remove(&foo);
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
} }
{ {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
assert!(jdb.contains(&bar)); assert!(jdb.contains(&bar));
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap(); jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
@ -391,7 +387,7 @@ mod tests {
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let foo = { let foo = {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
@ -405,7 +401,7 @@ mod tests {
}; };
{ {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.remove(&foo); jdb.remove(&foo);
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap(); jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
@ -420,7 +416,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let (foo, _, _) = { let (foo, _, _) = {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
let bar = jdb.insert(b"bar"); let bar = jdb.insert(b"bar");
@ -435,7 +431,7 @@ mod tests {
}; };
{ {
let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap(); jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
} }
@ -446,14 +442,14 @@ mod tests {
let temp = ::devtools::RandomTempPath::new(); let temp = ::devtools::RandomTempPath::new();
let key = { let key = {
let mut jdb = ArchiveDB::new(temp.as_str(), None); let mut jdb = ArchiveDB::new(temp.as_str(), DatabaseConfig::default());
let key = jdb.insert(b"foo"); let key = jdb.insert(b"foo");
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
key key
}; };
{ {
let jdb = ArchiveDB::new(temp.as_str(), None); let jdb = ArchiveDB::new(temp.as_str(), DatabaseConfig::default());
let state = jdb.state(&key); let state = jdb.state(&key);
assert!(state.is_some()); assert!(state.is_some());
} }

View File

@ -73,13 +73,8 @@ const PADDING : [u8; 10] = [ 0u8; 10 ];
impl EarlyMergeDB { impl EarlyMergeDB {
/// Create a new instance from file /// Create a new instance from file
pub fn new(path: &str, cache_size: Option<usize>) -> EarlyMergeDB { pub fn new(path: &str, config: DatabaseConfig) -> EarlyMergeDB {
let opts = DatabaseConfig { let opts = config.prefix(DB_PREFIX_LEN);
// this must match account_db prefix
prefix_size: Some(DB_PREFIX_LEN),
max_open_files: 256,
cache_size: cache_size,
};
let backing = Database::open(&opts, path).unwrap_or_else(|e| { let backing = Database::open(&opts, path).unwrap_or_else(|e| {
panic!("Error opening state db: {}", e); panic!("Error opening state db: {}", e);
}); });
@ -107,7 +102,7 @@ impl EarlyMergeDB {
fn new_temp() -> EarlyMergeDB { fn new_temp() -> EarlyMergeDB {
let mut dir = env::temp_dir(); let mut dir = env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
Self::new(dir.to_str().unwrap(), None) Self::new(dir.to_str().unwrap(), DatabaseConfig::default())
} }
fn morph_key(key: &H256, index: u8) -> Bytes { fn morph_key(key: &H256, index: u8) -> Bytes {
@ -537,6 +532,7 @@ mod tests {
use super::super::traits::JournalDB; use super::super::traits::JournalDB;
use hashdb::*; use hashdb::*;
use log::init_log; use log::init_log;
use kvdb::DatabaseConfig;
#[test] #[test]
fn insert_same_in_fork() { fn insert_same_in_fork() {
@ -714,7 +710,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -742,7 +738,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -770,7 +766,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -808,7 +804,7 @@ mod tests {
let bar = H256::random(); let bar = H256::random();
let foo = { let foo = {
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.emplace(bar.clone(), b"bar".to_vec()); jdb.emplace(bar.clone(), b"bar".to_vec());
@ -818,14 +814,14 @@ mod tests {
}; };
{ {
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.remove(&foo); jdb.remove(&foo);
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
} }
{ {
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
assert!(jdb.contains(&bar)); assert!(jdb.contains(&bar));
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap(); jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
@ -840,7 +836,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 4 // history is 4
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
@ -869,7 +865,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 4 // history is 4
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
@ -918,7 +914,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
@ -949,7 +945,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 4 // history is 4
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
@ -989,7 +985,7 @@ mod tests {
let foo = b"foo".sha3(); let foo = b"foo".sha3();
{ {
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
jdb.insert(b"foo"); jdb.insert(b"foo");
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
@ -1010,7 +1006,7 @@ mod tests {
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
// incantation to reopen the db // incantation to reopen the db
}; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.remove(&foo); jdb.remove(&foo);
jdb.commit(4, &b"4".sha3(), Some((2, b"2".sha3()))).unwrap(); jdb.commit(4, &b"4".sha3(), Some((2, b"2".sha3()))).unwrap();
@ -1018,14 +1014,14 @@ mod tests {
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
// incantation to reopen the db // incantation to reopen the db
}; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(5, &b"5".sha3(), Some((3, b"3".sha3()))).unwrap(); jdb.commit(5, &b"5".sha3(), Some((3, b"3".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
// incantation to reopen the db // incantation to reopen the db
}; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(6, &b"6".sha3(), Some((4, b"4".sha3()))).unwrap(); jdb.commit(6, &b"6".sha3(), Some((4, b"4".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -1038,7 +1034,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let (foo, bar, baz) = { let (foo, bar, baz) = {
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
let bar = jdb.insert(b"bar"); let bar = jdb.insert(b"bar");
@ -1056,7 +1052,7 @@ mod tests {
}; };
{ {
let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap(); jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));

View File

@ -17,6 +17,7 @@
//! `JournalDB` interface and implementation. //! `JournalDB` interface and implementation.
use common::*; use common::*;
use kvdb::DatabaseConfig;
/// Export the journaldb module. /// Export the journaldb module.
pub mod traits; pub mod traits;
@ -71,12 +72,12 @@ impl fmt::Display for Algorithm {
} }
/// Create a new `JournalDB` trait object. /// Create a new `JournalDB` trait object.
pub fn new(path: &str, algorithm: Algorithm, cache_size: Option<usize>) -> Box<JournalDB> { pub fn new(path: &str, algorithm: Algorithm, config: DatabaseConfig) -> Box<JournalDB> {
match algorithm { match algorithm {
Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path, cache_size)), Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path, config)),
Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(path, cache_size)), Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(path, config)),
Algorithm::OverlayRecent => Box::new(overlayrecentdb::OverlayRecentDB::new(path, cache_size)), Algorithm::OverlayRecent => Box::new(overlayrecentdb::OverlayRecentDB::new(path, config)),
Algorithm::RefCounted => Box::new(refcounteddb::RefCountedDB::new(path, cache_size)), Algorithm::RefCounted => Box::new(refcounteddb::RefCountedDB::new(path, config)),
} }
} }

View File

@ -98,18 +98,13 @@ const PADDING : [u8; 10] = [ 0u8; 10 ];
impl OverlayRecentDB { impl OverlayRecentDB {
/// Create a new instance from file /// Create a new instance from file
pub fn new(path: &str, cache_size: Option<usize>) -> OverlayRecentDB { pub fn new(path: &str, config: DatabaseConfig) -> OverlayRecentDB {
Self::from_prefs(path, cache_size) Self::from_prefs(path, config)
} }
/// Create a new instance from file /// Create a new instance from file
pub fn from_prefs(path: &str, cache_size: Option<usize>) -> OverlayRecentDB { pub fn from_prefs(path: &str, config: DatabaseConfig) -> OverlayRecentDB {
let opts = DatabaseConfig { let opts = config.prefix(DB_PREFIX_LEN);
// this must match account_db prefix
prefix_size: Some(DB_PREFIX_LEN),
max_open_files: 256,
cache_size: cache_size,
};
let backing = Database::open(&opts, path).unwrap_or_else(|e| { let backing = Database::open(&opts, path).unwrap_or_else(|e| {
panic!("Error opening state db: {}", e); panic!("Error opening state db: {}", e);
}); });
@ -135,7 +130,7 @@ impl OverlayRecentDB {
pub fn new_temp() -> OverlayRecentDB { pub fn new_temp() -> OverlayRecentDB {
let mut dir = env::temp_dir(); let mut dir = env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
Self::new(dir.to_str().unwrap(), None) Self::new(dir.to_str().unwrap(), DatabaseConfig::default())
} }
#[cfg(test)] #[cfg(test)]
@ -369,6 +364,7 @@ mod tests {
use hashdb::*; use hashdb::*;
use log::init_log; use log::init_log;
use journaldb::JournalDB; use journaldb::JournalDB;
use kvdb::DatabaseConfig;
#[test] #[test]
fn insert_same_in_fork() { fn insert_same_in_fork() {
@ -526,7 +522,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -554,7 +550,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -582,7 +578,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -620,7 +616,7 @@ mod tests {
let bar = H256::random(); let bar = H256::random();
let foo = { let foo = {
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.emplace(bar.clone(), b"bar".to_vec()); jdb.emplace(bar.clone(), b"bar".to_vec());
@ -630,14 +626,14 @@ mod tests {
}; };
{ {
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.remove(&foo); jdb.remove(&foo);
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
} }
{ {
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
assert!(jdb.contains(&bar)); assert!(jdb.contains(&bar));
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap(); jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
@ -652,7 +648,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 4 // history is 4
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
@ -681,7 +677,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 4 // history is 4
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
@ -730,7 +726,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
@ -761,7 +757,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 4 // history is 4
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
@ -801,7 +797,7 @@ mod tests {
let foo = b"foo".sha3(); let foo = b"foo".sha3();
{ {
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
jdb.insert(b"foo"); jdb.insert(b"foo");
jdb.commit(0, &b"0".sha3(), None).unwrap(); jdb.commit(0, &b"0".sha3(), None).unwrap();
@ -822,7 +818,7 @@ mod tests {
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
// incantation to reopen the db // incantation to reopen the db
}; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.remove(&foo); jdb.remove(&foo);
jdb.commit(4, &b"4".sha3(), Some((2, b"2".sha3()))).unwrap(); jdb.commit(4, &b"4".sha3(), Some((2, b"2".sha3()))).unwrap();
@ -830,14 +826,14 @@ mod tests {
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
// incantation to reopen the db // incantation to reopen the db
}; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(5, &b"5".sha3(), Some((3, b"3".sha3()))).unwrap(); jdb.commit(5, &b"5".sha3(), Some((3, b"3".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));
// incantation to reopen the db // incantation to reopen the db
}; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(6, &b"6".sha3(), Some((4, b"4".sha3()))).unwrap(); jdb.commit(6, &b"6".sha3(), Some((4, b"4".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
@ -850,7 +846,7 @@ mod tests {
let mut dir = ::std::env::temp_dir(); let mut dir = ::std::env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
let (foo, bar, baz) = { let (foo, bar, baz) = {
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
// history is 1 // history is 1
let foo = jdb.insert(b"foo"); let foo = jdb.insert(b"foo");
let bar = jdb.insert(b"bar"); let bar = jdb.insert(b"bar");
@ -868,7 +864,7 @@ mod tests {
}; };
{ {
let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default());
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap(); jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
assert!(jdb.can_reconstruct_refs()); assert!(jdb.can_reconstruct_refs());
assert!(jdb.contains(&foo)); assert!(jdb.contains(&foo));

View File

@ -46,13 +46,8 @@ const PADDING : [u8; 10] = [ 0u8; 10 ];
impl RefCountedDB { impl RefCountedDB {
/// Create a new instance given a `backing` database. /// Create a new instance given a `backing` database.
pub fn new(path: &str, cache_size: Option<usize>) -> RefCountedDB { pub fn new(path: &str, config: DatabaseConfig) -> RefCountedDB {
let opts = DatabaseConfig { let opts = config.prefix(DB_PREFIX_LEN);
// this must match account_db prefix
prefix_size: Some(DB_PREFIX_LEN),
max_open_files: 256,
cache_size: cache_size,
};
let backing = Database::open(&opts, path).unwrap_or_else(|e| { let backing = Database::open(&opts, path).unwrap_or_else(|e| {
panic!("Error opening state db: {}", e); panic!("Error opening state db: {}", e);
}); });
@ -82,7 +77,7 @@ impl RefCountedDB {
fn new_temp() -> RefCountedDB { fn new_temp() -> RefCountedDB {
let mut dir = env::temp_dir(); let mut dir = env::temp_dir();
dir.push(H32::random().hex()); dir.push(H32::random().hex());
Self::new(dir.to_str().unwrap(), None) Self::new(dir.to_str().unwrap(), DatabaseConfig::default())
} }
} }

View File

@ -20,8 +20,6 @@ use std::default::Default;
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBVector, DBIterator, use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBVector, DBIterator,
IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction}; IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
const DB_FILE_SIZE_BASE: u64 = 16 * 1024 * 1024;
const DB_FILE_SIZE_MULTIPLIER: i32 = 5;
const DB_BACKGROUND_FLUSHES: i32 = 2; const DB_BACKGROUND_FLUSHES: i32 = 2;
const DB_BACKGROUND_COMPACTIONS: i32 = 2; const DB_BACKGROUND_COMPACTIONS: i32 = 2;
@ -53,6 +51,36 @@ impl DBTransaction {
} }
} }
/// Compaction profile for the database settings
pub struct CompactionProfile {
/// L0-L1 target file size
pub initial_file_size: u64,
/// L2-LN target file size multiplier
pub file_size_multiplier: i32,
/// rate limiter for background flushes and compactions, bytes/sec, if any
pub write_rate_limit: Option<u64>,
}
impl CompactionProfile {
/// Default profile suitable for most storage
pub fn default() -> CompactionProfile {
CompactionProfile {
initial_file_size: 32 * 1024 * 1024,
file_size_multiplier: 2,
write_rate_limit: None,
}
}
/// Slow hdd compaction profile
pub fn hdd() -> CompactionProfile {
CompactionProfile {
initial_file_size: 192 * 1024 * 1024,
file_size_multiplier: 1,
write_rate_limit: Some(8 * 1024 * 1024),
}
}
}
/// Database configuration /// Database configuration
pub struct DatabaseConfig { pub struct DatabaseConfig {
/// Optional prefix size in bytes. Allows lookup by partial key. /// Optional prefix size in bytes. Allows lookup by partial key.
@ -61,6 +89,8 @@ pub struct DatabaseConfig {
pub max_open_files: i32, pub max_open_files: i32,
/// Cache-size /// Cache-size
pub cache_size: Option<usize>, pub cache_size: Option<usize>,
/// Compaction profile
pub compaction: CompactionProfile,
} }
impl DatabaseConfig { impl DatabaseConfig {
@ -69,9 +99,22 @@ impl DatabaseConfig {
DatabaseConfig { DatabaseConfig {
cache_size: Some(cache_size), cache_size: Some(cache_size),
prefix_size: None, prefix_size: None,
max_open_files: -1, max_open_files: 256,
compaction: CompactionProfile::default(),
} }
} }
/// Modify the compaction profile
pub fn compaction(mut self, profile: CompactionProfile) -> Self {
self.compaction = profile;
self
}
/// Modify the prefix of the db
pub fn prefix(mut self, prefix_size: usize) -> Self {
self.prefix_size = Some(prefix_size);
self
}
} }
impl Default for DatabaseConfig { impl Default for DatabaseConfig {
@ -79,7 +122,8 @@ impl Default for DatabaseConfig {
DatabaseConfig { DatabaseConfig {
cache_size: None, cache_size: None,
prefix_size: None, prefix_size: None,
max_open_files: -1, max_open_files: 256,
compaction: CompactionProfile::default(),
} }
} }
} }
@ -111,13 +155,18 @@ impl Database {
/// Open database file. Creates if it does not exist. /// Open database file. Creates if it does not exist.
pub fn open(config: &DatabaseConfig, path: &str) -> Result<Database, String> { pub fn open(config: &DatabaseConfig, path: &str) -> Result<Database, String> {
let mut opts = Options::new(); let mut opts = Options::new();
try!(opts.set_parsed_options("rate_limiter_bytes_per_sec=256000000")); if let Some(rate_limit) = config.compaction.write_rate_limit {
try!(opts.set_parsed_options(&format!("rate_limiter_bytes_per_sec={}", rate_limit)));
}
opts.set_max_open_files(config.max_open_files); opts.set_max_open_files(config.max_open_files);
opts.create_if_missing(true); opts.create_if_missing(true);
opts.set_use_fsync(false); opts.set_use_fsync(false);
// compaction settings
opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction); opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction);
opts.set_target_file_size_base(DB_FILE_SIZE_BASE); opts.set_target_file_size_base(config.compaction.initial_file_size);
opts.set_target_file_size_multiplier(DB_FILE_SIZE_MULTIPLIER); opts.set_target_file_size_multiplier(config.compaction.file_size_multiplier);
opts.set_max_background_flushes(DB_BACKGROUND_FLUSHES); opts.set_max_background_flushes(DB_BACKGROUND_FLUSHES);
opts.set_max_background_compactions(DB_BACKGROUND_COMPACTIONS); opts.set_max_background_compactions(DB_BACKGROUND_COMPACTIONS);
if let Some(cache_size) = config.cache_size { if let Some(cache_size) = config.cache_size {
@ -150,7 +199,16 @@ impl Database {
opts.set_block_based_table_factory(&block_opts); opts.set_block_based_table_factory(&block_opts);
opts.set_prefix_extractor_fixed_size(size); opts.set_prefix_extractor_fixed_size(size);
} }
let db = try!(DB::open(&opts, path)); let db = match DB::open(&opts, path) {
Ok(db) => db,
Err(ref s) if s.starts_with("Corruption:") => {
info!("{}", s);
info!("Attempting DB repair for {}", path);
try!(DB::repair(&opts, path));
try!(DB::open(&opts, path))
},
Err(s) => { return Err(s); }
};
Ok(Database { db: db }) Ok(Database { db: db })
} }
@ -244,10 +302,10 @@ mod tests {
let path = RandomTempPath::create_dir(); let path = RandomTempPath::create_dir();
let smoke = Database::open_default(path.as_path().to_str().unwrap()).unwrap(); let smoke = Database::open_default(path.as_path().to_str().unwrap()).unwrap();
assert!(smoke.is_empty()); assert!(smoke.is_empty());
test_db(&DatabaseConfig { prefix_size: None, max_open_files: 256, cache_size: None, }); test_db(&DatabaseConfig::default());
test_db(&DatabaseConfig { prefix_size: Some(1), max_open_files: 256, cache_size: None, }); test_db(&DatabaseConfig::default().prefix(12));
test_db(&DatabaseConfig { prefix_size: Some(8), max_open_files: 256, cache_size: None, }); test_db(&DatabaseConfig::default().prefix(22));
test_db(&DatabaseConfig { prefix_size: Some(32), max_open_files: 256, cache_size: None, }); test_db(&DatabaseConfig::default().prefix(8));
} }
} }

View File

@ -117,6 +117,7 @@ extern crate libc;
extern crate target_info; extern crate target_info;
extern crate bigint; extern crate bigint;
extern crate chrono; extern crate chrono;
extern crate ansi_term;
pub mod standard; pub mod standard;
#[macro_use] #[macro_use]

View File

@ -20,7 +20,21 @@ use std::env;
use rlog::{LogLevelFilter}; use rlog::{LogLevelFilter};
use env_logger::LogBuilder; use env_logger::LogBuilder;
use std::sync::{RwLock, RwLockReadGuard}; use std::sync::{RwLock, RwLockReadGuard};
use std::sync::atomic::{Ordering, AtomicBool};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
pub use ansi_term::{Colour, Style};
lazy_static! {
static ref USE_COLOR: AtomicBool = AtomicBool::new(false);
}
/// Paint, using colour if desired.
pub fn paint(c: Style, t: String) -> String {
match USE_COLOR.load(Ordering::Relaxed) {
true => format!("{}", c.paint(t)),
false => t,
}
}
lazy_static! { lazy_static! {
static ref LOG_DUMMY: bool = { static ref LOG_DUMMY: bool = {
@ -57,7 +71,8 @@ impl RotatingLogger {
/// Creates new `RotatingLogger` with given levels. /// Creates new `RotatingLogger` with given levels.
/// It does not enforce levels - it's just read only. /// It does not enforce levels - it's just read only.
pub fn new(levels: String) -> Self { pub fn new(levels: String, enable_color: bool) -> Self {
USE_COLOR.store(enable_color, Ordering::Relaxed);
RotatingLogger { RotatingLogger {
levels: levels, levels: levels,
logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()), logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()),
@ -86,7 +101,7 @@ mod test {
use super::RotatingLogger; use super::RotatingLogger;
fn logger() -> RotatingLogger { fn logger() -> RotatingLogger {
RotatingLogger::new("test".to_owned()) RotatingLogger::new("test".to_owned(), false)
} }
#[test] #[test]

View File

@ -28,7 +28,7 @@ use crypto::*;
use rlp::*; use rlp::*;
use network::node_table::*; use network::node_table::*;
use network::error::NetworkError; use network::error::NetworkError;
use io::StreamToken; use io::{StreamToken, IoContext};
use network::PROTOCOL_VERSION; use network::PROTOCOL_VERSION;
@ -283,7 +283,7 @@ impl Discovery {
ret ret
} }
pub fn writable(&mut self) { pub fn writable<Message>(&mut self, io: &IoContext<Message>) where Message: Send + Sync + Clone {
while !self.send_queue.is_empty() { while !self.send_queue.is_empty() {
let data = self.send_queue.pop_front().unwrap(); let data = self.send_queue.pop_front().unwrap();
match self.udp_socket.send_to(&data.payload, &data.address) { match self.udp_socket.send_to(&data.payload, &data.address) {
@ -302,15 +302,17 @@ impl Discovery {
} }
} }
} }
io.update_registration(self.token).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
} }
fn send_to(&mut self, payload: Bytes, address: SocketAddr) { fn send_to(&mut self, payload: Bytes, address: SocketAddr) {
self.send_queue.push_back(Datagramm { payload: payload, address: address }); self.send_queue.push_back(Datagramm { payload: payload, address: address });
} }
pub fn readable(&mut self) -> Option<TableUpdates> { pub fn readable<Message>(&mut self, io: &IoContext<Message>) -> Option<TableUpdates> where Message: Send + Sync + Clone {
let mut buf: [u8; MAX_DATAGRAM_SIZE] = unsafe { mem::uninitialized() }; let mut buf: [u8; MAX_DATAGRAM_SIZE] = unsafe { mem::uninitialized() };
match self.udp_socket.recv_from(&mut buf) { let writable = !self.send_queue.is_empty();
let res = match self.udp_socket.recv_from(&mut buf) {
Ok(Some((len, address))) => self.on_packet(&buf[0..len], address).unwrap_or_else(|e| { Ok(Some((len, address))) => self.on_packet(&buf[0..len], address).unwrap_or_else(|e| {
debug!("Error processing UDP packet: {:?}", e); debug!("Error processing UDP packet: {:?}", e);
None None
@ -320,7 +322,12 @@ impl Discovery {
debug!("Error reading UPD socket: {:?}", e); debug!("Error reading UPD socket: {:?}", e);
None None
} }
};
let new_writable = !self.send_queue.is_empty();
if writable != new_writable {
io.update_registration(self.token).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
} }
res
} }
fn on_packet(&mut self, packet: &[u8], from: SocketAddr) -> Result<Option<TableUpdates>, NetworkError> { fn on_packet(&mut self, packet: &[u8], from: SocketAddr) -> Result<Option<TableUpdates>, NetworkError> {

View File

@ -32,6 +32,8 @@ use misc::version;
use crypto::*; use crypto::*;
use sha3::Hashable; use sha3::Hashable;
use rlp::*; use rlp::*;
use log::Colour::White;
use log::paint;
use network::session::{Session, SessionData}; use network::session::{Session, SessionData};
use error::*; use error::*;
use io::*; use io::*;
@ -236,8 +238,8 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
} }
/// Send an IO message /// Send an IO message
pub fn message(&self, msg: Message) { pub fn message(&self, msg: Message) -> Result<(), UtilError> {
self.io.message(NetworkIoMessage::User(msg)); self.io.message(NetworkIoMessage::User(msg))
} }
/// Get an IoChannel. /// Get an IoChannel.
@ -248,12 +250,14 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
/// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected. /// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected.
pub fn disable_peer(&self, peer: PeerId) { pub fn disable_peer(&self, peer: PeerId) {
//TODO: remove capability, disconnect if no capabilities left //TODO: remove capability, disconnect if no capabilities left
self.io.message(NetworkIoMessage::DisablePeer(peer)); self.io.message(NetworkIoMessage::DisablePeer(peer))
.unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e));
} }
/// Disconnect peer. Reconnect can be attempted later. /// Disconnect peer. Reconnect can be attempted later.
pub fn disconnect_peer(&self, peer: PeerId) { pub fn disconnect_peer(&self, peer: PeerId) {
self.io.message(NetworkIoMessage::Disconnect(peer)); self.io.message(NetworkIoMessage::Disconnect(peer))
.unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e));
} }
/// Check if the session is still active. /// Check if the session is still active.
@ -267,7 +271,7 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone
token: token, token: token,
delay: ms, delay: ms,
protocol: self.protocol, protocol: self.protocol,
}); }).unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e));
Ok(()) Ok(())
} }
@ -341,6 +345,7 @@ pub struct Host<Message> where Message: Send + Sync + Clone {
reserved_nodes: RwLock<HashSet<NodeId>>, reserved_nodes: RwLock<HashSet<NodeId>>,
num_sessions: AtomicUsize, num_sessions: AtomicUsize,
stopping: AtomicBool, stopping: AtomicBool,
first_time: AtomicBool,
} }
impl<Message> Host<Message> where Message: Send + Sync + Clone { impl<Message> Host<Message> where Message: Send + Sync + Clone {
@ -396,6 +401,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
reserved_nodes: RwLock::new(HashSet::new()), reserved_nodes: RwLock::new(HashSet::new()),
num_sessions: AtomicUsize::new(0), num_sessions: AtomicUsize::new(0),
stopping: AtomicBool::new(false), stopping: AtomicBool::new(false),
first_time: AtomicBool::new(true),
}; };
for n in boot_nodes { for n in boot_nodes {
@ -531,7 +537,11 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
}; };
self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone()); self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone());
info!("Public node URL: {}", self.external_url().unwrap());
if self.first_time.load(AtomicOrdering::Relaxed) {
info!("Public node URL: {}", paint(White.bold(), format!("{}", self.external_url().unwrap())));
self.first_time.store(false, AtomicOrdering::Relaxed);
}
// Initialize discovery. // Initialize discovery.
let discovery = { let discovery = {
@ -714,7 +724,6 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
debug!(target: "network", "Can't accept connection: {:?}", e); debug!(target: "network", "Can't accept connection: {:?}", e);
} }
} }
io.update_registration(TCP_ACCEPT).expect("Error registering TCP listener");
} }
fn session_writable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) { fn session_writable(&self, token: StreamToken, io: &IoContext<NetworkIoMessage<Message>>) {
@ -910,11 +919,10 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
match stream { match stream {
FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io), FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io),
DISCOVERY => { DISCOVERY => {
let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().readable() }; let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().readable(io) };
if let Some(node_changes) = node_changes { if let Some(node_changes) = node_changes {
self.update_nodes(io, node_changes); self.update_nodes(io, node_changes);
} }
io.update_registration(DISCOVERY).expect("Error updating discovery registration");
}, },
TCP_ACCEPT => self.accept(io), TCP_ACCEPT => self.accept(io),
_ => panic!("Received unknown readable token"), _ => panic!("Received unknown readable token"),
@ -928,8 +936,7 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
match stream { match stream {
FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io), FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io),
DISCOVERY => { DISCOVERY => {
self.discovery.lock().unwrap().as_mut().unwrap().writable(); self.discovery.lock().unwrap().as_mut().unwrap().writable(io);
io.update_registration(DISCOVERY).expect("Error updating discovery registration");
} }
_ => panic!("Received unknown writable token"), _ => panic!("Received unknown writable token"),
} }
@ -946,14 +953,14 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io), FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io),
DISCOVERY_REFRESH => { DISCOVERY_REFRESH => {
self.discovery.lock().unwrap().as_mut().unwrap().refresh(); self.discovery.lock().unwrap().as_mut().unwrap().refresh();
io.update_registration(DISCOVERY).expect("Error updating discovery registration"); io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
}, },
DISCOVERY_ROUND => { DISCOVERY_ROUND => {
let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().round() }; let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().round() };
if let Some(node_changes) = node_changes { if let Some(node_changes) = node_changes {
self.update_nodes(io, node_changes); self.update_nodes(io, node_changes);
} }
io.update_registration(DISCOVERY).expect("Error updating discovery registration"); io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
}, },
NODE_TABLE => { NODE_TABLE => {
trace!(target: "network", "Refreshing node table"); trace!(target: "network", "Refreshing node table");
@ -1004,7 +1011,7 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
handler_token handler_token
}; };
self.timers.write().unwrap().insert(handler_token, ProtocolTimer { protocol: protocol, token: *token }); self.timers.write().unwrap().insert(handler_token, ProtocolTimer { protocol: protocol, token: *token });
io.register_timer(handler_token, *delay).expect("Error registering timer"); io.register_timer(handler_token, *delay).unwrap_or_else(|e| debug!("Error registering timer {}: {:?}", token, e));
}, },
NetworkIoMessage::Disconnect(ref peer) => { NetworkIoMessage::Disconnect(ref peer) => {
let session = { self.sessions.read().unwrap().get(*peer).cloned() }; let session = { self.sessions.read().unwrap().get(*peer).cloned() };

View File

@ -88,7 +88,7 @@ impl NetworkProtocolHandler<TestProtocolMessage> for TestProtocol {
/// Timer function called after a timeout created with `NetworkContext::timeout`. /// Timer function called after a timeout created with `NetworkContext::timeout`.
fn timeout(&self, io: &NetworkContext<TestProtocolMessage>, timer: TimerToken) { fn timeout(&self, io: &NetworkContext<TestProtocolMessage>, timer: TimerToken) {
io.message(TestProtocolMessage { payload: 22 }); io.message(TestProtocolMessage { payload: 22 }).unwrap();
assert_eq!(timer, 0); assert_eq!(timer, 0);
self.got_timeout.store(true, AtomicOrdering::Relaxed); self.got_timeout.store(true, AtomicOrdering::Relaxed);
} }

View File

@ -258,14 +258,7 @@ impl <T>FromBytes for T where T: FixedHash {
Ordering::Equal => () Ordering::Equal => ()
}; };
unsafe { Ok(T::from_slice(bytes))
use std::{mem, ptr};
let mut res: T = mem::uninitialized();
ptr::copy(bytes.as_ptr(), res.as_slice_mut().as_mut_ptr(), T::len());
Ok(res)
}
} }
} }

View File

@ -27,6 +27,14 @@ pub struct UsingQueue<T> where T: Clone {
max_size: usize, max_size: usize,
} }
/// Take an item or just clone it?
pub enum GetAction {
/// Remove the item, faster but you can't get it back.
Take,
/// Clone the item, slower but you can get it again.
Clone,
}
impl<T> UsingQueue<T> where T: Clone { impl<T> UsingQueue<T> where T: Clone {
/// Create a new struct with a maximum size of `max_size`. /// Create a new struct with a maximum size of `max_size`.
pub fn new(max_size: usize) -> UsingQueue<T> { pub fn new(max_size: usize) -> UsingQueue<T> {
@ -74,6 +82,20 @@ impl<T> UsingQueue<T> where T: Clone {
self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i)) self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i))
} }
/// Returns `Some` item which is the first that `f` returns `true` with a reference to it
/// as a parameter or `None` if no such item exists in the queue.
pub fn clone_used_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
self.in_use.iter().find(|r| predicate(r)).cloned()
}
/// Fork-function for `take_used_if` and `clone_used_if`.
pub fn get_used_if<P>(&mut self, action: GetAction, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
match action {
GetAction::Take => self.take_used_if(predicate),
GetAction::Clone => self.clone_used_if(predicate),
}
}
/// Returns the most recently pushed block if `f` returns `true` with a reference to it as /// Returns the most recently pushed block if `f` returns `true` with a reference to it as
/// a parameter, otherwise `None`. /// a parameter, otherwise `None`.
/// Will not destroy a block if a reference to it has previously been returned by `use_last_ref`, /// Will not destroy a block if a reference to it has previously been returned by `use_last_ref`,
@ -94,18 +116,66 @@ impl<T> UsingQueue<T> where T: Clone {
} }
#[test] #[test]
fn should_find_when_pushed() { fn should_not_find_when_pushed() {
let mut q = UsingQueue::new(2); let mut q = UsingQueue::new(2);
q.push(1); q.push(1);
assert!(q.take_used_if(|i| i == &1).is_none()); assert!(q.take_used_if(|i| i == &1).is_none());
} }
#[test]
fn should_not_find_when_pushed_with_clone() {
let mut q = UsingQueue::new(2);
q.push(1);
assert!(q.clone_used_if(|i| i == &1).is_none());
}
#[test] #[test]
fn should_find_when_pushed_and_used() { fn should_find_when_pushed_and_used() {
let mut q = UsingQueue::new(2); let mut q = UsingQueue::new(2);
q.push(1); q.push(1);
q.use_last_ref(); q.use_last_ref();
assert!(q.take_used_if(|i| i == &1).is_some()); assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
}
#[test]
fn should_have_same_semantics_for_get_take_clone() {
let mut q = UsingQueue::new(2);
q.push(1);
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
q.use_last_ref();
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1);
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
}
#[test]
fn should_find_when_pushed_and_used_with_clone() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
}
#[test]
fn should_not_find_again_when_pushed_and_taken() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
assert!(q.clone_used_if(|i| i == &1).is_none());
}
#[test]
fn should_find_again_when_pushed_and_cloned() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
} }
#[test] #[test]