Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64af073bd4 | ||
|
|
fe83046198 | ||
|
|
8031910892 | ||
|
|
73a3dac38c | ||
|
|
bbaf5ed4f5 | ||
|
|
9cf777510f | ||
|
|
9b398421ce | ||
|
|
a618dcaaf0 | ||
|
|
50021c7611 | ||
|
|
f62d2e6ea8 | ||
|
|
7da8e7926d | ||
|
|
5363e75f3a | ||
|
|
31d667a29a | ||
|
|
ca2bc92dda | ||
|
|
a0b71a9f52 | ||
|
|
300c0c120b | ||
|
|
b228dc7b54 |
@@ -264,9 +264,10 @@ windows:
|
||||
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
|
||||
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
|
||||
- set RUST_BACKTRACE=1
|
||||
- rustup default 1.11.0-x86_64-pc-windows-msvc
|
||||
- set RUSTFLAGS=-Zorbit=off
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo build --release --verbose
|
||||
- md5sum target\release\parity >> checksum
|
||||
- cmd md5sum target\release\parity >> checksum
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
- aws configure set aws_secret_access_key %s3_secret%
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.exe
|
||||
|
||||
111
Cargo.lock
generated
111
Cargo.lock
generated
@@ -1,12 +1,12 @@
|
||||
[root]
|
||||
name = "parity"
|
||||
version = "1.3.3"
|
||||
version = "1.3.4"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
|
||||
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.3.0",
|
||||
"ethcore-dapps 1.3.0",
|
||||
@@ -20,22 +20,22 @@ dependencies = [
|
||||
"ethcore-logger 1.3.0",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-signer 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"ethsync 1.3.0",
|
||||
"fdlimit 0.1.0",
|
||||
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)",
|
||||
"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)",
|
||||
"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.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 0.2.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_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.36.0 (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)",
|
||||
]
|
||||
@@ -192,7 +192,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -208,12 +208,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "docopt"
|
||||
version = "0.6.80"
|
||||
version = "0.6.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -236,7 +237,7 @@ version = "0.5.4"
|
||||
source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c"
|
||||
dependencies = [
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -269,7 +270,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"ethjson 0.1.0",
|
||||
"ethstore 0.1.0",
|
||||
"evmjit 1.3.0",
|
||||
@@ -293,7 +294,7 @@ version = "1.3.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
@@ -335,7 +336,7 @@ name = "ethcore-ipc"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -380,7 +381,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -392,7 +393,7 @@ name = "ethcore-logger"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -407,7 +408,7 @@ dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -431,10 +432,10 @@ dependencies = [
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"ethjson 0.1.0",
|
||||
"ethsync 1.3.0",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -454,7 +455,7 @@ dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
@@ -465,7 +466,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.3.3"
|
||||
version = "1.3.4"
|
||||
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)",
|
||||
@@ -498,7 +499,7 @@ dependencies = [
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"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_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -546,7 +547,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-network 1.3.0",
|
||||
"ethcore-util 1.3.3",
|
||||
"ethcore-util 1.3.4",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -571,8 +572,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.28"
|
||||
version = "0.3.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
@@ -706,7 +710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "json-ipc-server"
|
||||
version = "0.2.4"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git#56b6307130710ebc73cb9be087b6ed0b6c400bcf"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git?branch=beta#42d0dacc9cd203757ba894d7ba3690e12ef9f934"
|
||||
dependencies = [
|
||||
"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)",
|
||||
@@ -885,7 +889,7 @@ name = "nanomsg-sys"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1012,7 +1016,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1221,7 +1225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
||||
dependencies = [
|
||||
"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)",
|
||||
@@ -1230,9 +1234,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocksdb-sys"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#485dd747a2c9a9f910fc8ac696fc9edf5fa22aa3"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1249,7 +1253,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1263,7 +1267,7 @@ name = "rust-crypto"
|
||||
version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1331,7 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "sha3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1375,7 +1379,7 @@ source = "git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47#3c5cd1ca4
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.3.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -1386,6 +1390,14 @@ dependencies = [
|
||||
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex"
|
||||
version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_syntax 0.36.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_syntax"
|
||||
version = "0.33.0"
|
||||
@@ -1399,6 +1411,19 @@ dependencies = [
|
||||
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_syntax"
|
||||
version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.5.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)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "table"
|
||||
version = "0.1.0"
|
||||
@@ -1641,13 +1666,13 @@ dependencies = [
|
||||
"checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245"
|
||||
"checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc"
|
||||
"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "<none>"
|
||||
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
|
||||
"checksum daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0239832c1b4ca406d5ec73728cf4c7336d25cf85dd32db9e047e9e706ee0e935"
|
||||
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
||||
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
|
||||
"checksum docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)" = "4a7ef30445607f6fc8720f0a0a2c7442284b629cf0d049286860fae23e71c4d9"
|
||||
"checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
|
||||
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
||||
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
||||
"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
|
||||
"checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
|
||||
"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
|
||||
@@ -1660,7 +1685,7 @@ dependencies = [
|
||||
"checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484"
|
||||
"checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c"
|
||||
"checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76"
|
||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)" = "<none>"
|
||||
"checksum jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec4477e4e8218da23caa5dd31f4eb39999aa0ea9035660617eccfb19a23bf5ad"
|
||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
@@ -1693,7 +1718,7 @@ dependencies = [
|
||||
"checksum num-rational 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "48cdcc9ff4ae2a8296805ac15af88b3d88ce62128ded0cb74ffb63a587502a84"
|
||||
"checksum num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39"
|
||||
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
||||
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
||||
"checksum number_prefix 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b09f7c223751819881e1b95670d36fb12cdaa89848147f2222f1a2b691d5f0a3"
|
||||
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
||||
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||
@@ -1721,7 +1746,7 @@ dependencies = [
|
||||
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||
"checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "<none>"
|
||||
"checksum rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d3a99497c5c544e629cc8b359ae5ede321eba5fa8e5a8078f3ced727a976c3f"
|
||||
"checksum rpassword 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "320da1dfcf5c570a6c07ff60bb7cd4cdc986d2ea89caea139f2247371ab6a1df"
|
||||
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
||||
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||
@@ -1738,9 +1763,11 @@ dependencies = [
|
||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||
"checksum stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)" = "<none>"
|
||||
"checksum strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825"
|
||||
"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e"
|
||||
"checksum syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393b6dd0889df2b064beeea954cfda6bc2571604ac460deeae0fed55a53988af"
|
||||
"checksum syntex 0.36.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61dc0bbe1e46dcd53ec50d6600e750152c22e0e9352cadbd413e86fb847ae899"
|
||||
"checksum syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44bded3cabafc65c90b663b1071bd2d198a9ab7515e6ce729e4570aaf53c407e"
|
||||
"checksum syntex_syntax 0.36.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b92a8c33fad2fa99e14fe499ec17e82b6c6496a7a38a499f33b584ffa1886fa"
|
||||
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
||||
"checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281"
|
||||
"checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Ethcore client."
|
||||
name = "parity"
|
||||
version = "1.3.3"
|
||||
version = "1.3.4"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
@@ -39,7 +39,7 @@ ethcore-ipc-nano = { path = "ipc/nano" }
|
||||
ethcore-ipc = { path = "ipc/rpc" }
|
||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git", branch = "beta" }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
//! Single account in the system.
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use util::*;
|
||||
use pod_account::*;
|
||||
use account_db::*;
|
||||
@@ -24,9 +23,11 @@ use lru_cache::LruCache;
|
||||
|
||||
use std::cell::{RefCell, Cell};
|
||||
|
||||
const STORAGE_CACHE_ITEMS: usize = 4096;
|
||||
const STORAGE_CACHE_ITEMS: usize = 8192;
|
||||
|
||||
/// Single account in the system.
|
||||
/// Keeps track of changes to the code and storage.
|
||||
/// The changes are applied in `commit_storage` and `commit_code`
|
||||
pub struct Account {
|
||||
// Balance of the account.
|
||||
balance: U256,
|
||||
@@ -46,8 +47,6 @@ pub struct Account {
|
||||
code_size: Option<u64>,
|
||||
// Code cache of the account.
|
||||
code_cache: Arc<Bytes>,
|
||||
// Account is new or has been modified.
|
||||
filth: Filth,
|
||||
// Account code new or has been modified.
|
||||
code_filth: Filth,
|
||||
// Cached address hash.
|
||||
@@ -67,7 +66,6 @@ impl Account {
|
||||
code_hash: code.sha3(),
|
||||
code_size: Some(code.len() as u64),
|
||||
code_cache: Arc::new(code),
|
||||
filth: Filth::Dirty,
|
||||
code_filth: Filth::Dirty,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -89,7 +87,6 @@ impl Account {
|
||||
code_filth: Filth::Dirty,
|
||||
code_size: Some(pod.code.as_ref().map_or(0, |c| c.len() as u64)),
|
||||
code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)),
|
||||
filth: Filth::Dirty,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
@@ -105,7 +102,6 @@ impl Account {
|
||||
code_hash: SHA3_EMPTY,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: Some(0),
|
||||
filth: Filth::Dirty,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -123,7 +119,6 @@ impl Account {
|
||||
code_hash: r.val_at(3),
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
filth: Filth::Clean,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -141,7 +136,6 @@ impl Account {
|
||||
code_hash: SHA3_EMPTY,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
filth: Filth::Dirty,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
@@ -153,7 +147,6 @@ impl Account {
|
||||
self.code_hash = code.sha3();
|
||||
self.code_cache = Arc::new(code);
|
||||
self.code_size = Some(self.code_cache.len() as u64);
|
||||
self.filth = Filth::Dirty;
|
||||
self.code_filth = Filth::Dirty;
|
||||
}
|
||||
|
||||
@@ -164,17 +157,7 @@ impl Account {
|
||||
|
||||
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
|
||||
pub fn set_storage(&mut self, key: H256, value: H256) {
|
||||
match self.storage_changes.entry(key) {
|
||||
Entry::Occupied(ref mut entry) if entry.get() != &value => {
|
||||
entry.insert(value);
|
||||
self.filth = Filth::Dirty;
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
self.filth = Filth::Dirty;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
self.storage_changes.insert(key, value);
|
||||
}
|
||||
|
||||
/// Get (and cache) the contents of the trie's storage at `key`.
|
||||
@@ -263,17 +246,6 @@ impl Account {
|
||||
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == SHA3_EMPTY)
|
||||
}
|
||||
|
||||
/// Is this a new or modified account?
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.filth == Filth::Dirty || self.code_filth == Filth::Dirty || !self.storage_is_clean()
|
||||
}
|
||||
|
||||
/// Mark account as clean.
|
||||
pub fn set_clean(&mut self) {
|
||||
assert!(self.storage_is_clean());
|
||||
self.filth = Filth::Clean
|
||||
}
|
||||
|
||||
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
|
||||
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
|
||||
// TODO: fill out self.code_cache;
|
||||
@@ -326,25 +298,18 @@ impl Account {
|
||||
/// Increment the nonce of the account by one.
|
||||
pub fn inc_nonce(&mut self) {
|
||||
self.nonce = self.nonce + U256::from(1u8);
|
||||
self.filth = Filth::Dirty;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
/// Increase account balance.
|
||||
pub fn add_balance(&mut self, x: &U256) {
|
||||
if !x.is_zero() {
|
||||
self.balance = self.balance + *x;
|
||||
self.filth = Filth::Dirty;
|
||||
}
|
||||
self.balance = self.balance + *x;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
/// Decrease account balance.
|
||||
/// Panics if balance is less than `x`
|
||||
pub fn sub_balance(&mut self, x: &U256) {
|
||||
if !x.is_zero() {
|
||||
assert!(self.balance >= *x);
|
||||
self.balance = self.balance - *x;
|
||||
self.filth = Filth::Dirty;
|
||||
}
|
||||
assert!(self.balance >= *x);
|
||||
self.balance = self.balance - *x;
|
||||
}
|
||||
|
||||
/// Commit the `storage_changes` to the backing DB and update `storage_root`.
|
||||
@@ -406,7 +371,6 @@ impl Account {
|
||||
code_hash: self.code_hash.clone(),
|
||||
code_size: self.code_size.clone(),
|
||||
code_cache: self.code_cache.clone(),
|
||||
filth: self.filth,
|
||||
code_filth: self.code_filth,
|
||||
address_hash: self.address_hash.clone(),
|
||||
}
|
||||
@@ -427,10 +391,10 @@ impl Account {
|
||||
account
|
||||
}
|
||||
|
||||
/// Replace self with the data from other account merging storage cache
|
||||
pub fn merge_with(&mut self, other: Account) {
|
||||
assert!(self.storage_is_clean());
|
||||
assert!(other.storage_is_clean());
|
||||
/// Replace self with the data from other account merging storage cache.
|
||||
/// Basic account data and all modifications are overwritten
|
||||
/// with new values.
|
||||
pub fn overwrite_with(&mut self, other: Account) {
|
||||
self.balance = other.balance;
|
||||
self.nonce = other.nonce;
|
||||
self.storage_root = other.storage_root;
|
||||
@@ -443,6 +407,7 @@ impl Account {
|
||||
for (k, v) in other.storage_cache.into_inner().into_iter() {
|
||||
cache.insert(k.clone() , v.clone()); //TODO: cloning should not be required here
|
||||
}
|
||||
self.storage_changes = other.storage_changes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,9 +303,8 @@ impl Client {
|
||||
|
||||
// Enact Verified Block
|
||||
let parent = chain_has_parent.unwrap();
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash.clone());
|
||||
let is_canon = header.parent_hash == self.chain.best_block_hash();
|
||||
let db = if is_canon { self.state_db.lock().boxed_clone_canon() } else { self.state_db.lock().boxed_clone() };
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
|
||||
let db = self.state_db.lock().boxed_clone_canon(&header.parent_hash);
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone());
|
||||
if let Err(e) = enact_result {
|
||||
@@ -442,7 +441,6 @@ impl Client {
|
||||
.collect();
|
||||
|
||||
//let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
|
||||
|
||||
let batch = DBTransaction::new(&self.db);
|
||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||
// already-imported block of the same number.
|
||||
@@ -458,6 +456,8 @@ impl Client {
|
||||
enacted: route.enacted.clone(),
|
||||
retracted: route.retracted.len()
|
||||
});
|
||||
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
|
||||
state.sync_cache(&route.enacted, &route.retracted, is_canon);
|
||||
// Final commit to the DB
|
||||
self.db.write_buffered(batch).expect("DB write failed.");
|
||||
self.chain.commit();
|
||||
@@ -532,9 +532,11 @@ impl Client {
|
||||
|
||||
/// Get a copy of the best block's state.
|
||||
pub fn state(&self) -> State {
|
||||
let header = self.best_block_header();
|
||||
let header = HeaderView::new(&header);
|
||||
State::from_existing(
|
||||
self.state_db.lock().boxed_clone(),
|
||||
HeaderView::new(&self.best_block_header()).state_root(),
|
||||
self.state_db.lock().boxed_clone_canon(&header.hash()),
|
||||
header.state_root(),
|
||||
self.engine.account_start_nonce(),
|
||||
self.trie_factory.clone())
|
||||
.expect("State root of best block header always valid.")
|
||||
@@ -1036,7 +1038,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain.best_block_number())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1085,9 +1087,9 @@ impl MiningBlockChainClient for Client {
|
||||
|
||||
let block_data = block.rlp_bytes();
|
||||
// Clear canonical state cache
|
||||
self.state_db.lock().clear_cache();
|
||||
let route = self.commit_block(block, &h, &block_data);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
||||
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
||||
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
||||
|
||||
@@ -558,6 +558,6 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use util::sha3::*;
|
||||
use bit_set::BitSet;
|
||||
use super::super::instructions;
|
||||
|
||||
const CACHE_CODE_ITEMS: usize = 4096;
|
||||
const CACHE_CODE_ITEMS: usize = 65536;
|
||||
|
||||
/// GLobal cache for EVM interpreter
|
||||
pub struct SharedCache {
|
||||
|
||||
@@ -26,10 +26,10 @@ use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, E
|
||||
use crossbeam;
|
||||
pub use types::executed::{Executed, ExecutionResult};
|
||||
|
||||
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
|
||||
/// Roughly estimate what stack size each level of evm depth will use
|
||||
/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132)
|
||||
/// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp`
|
||||
const MAX_VM_DEPTH_FOR_THREAD: usize = 64;
|
||||
const STACK_SIZE_PER_DEPTH: usize = 24*1024;
|
||||
|
||||
/// Returns new address created from address and given nonce.
|
||||
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||
@@ -148,12 +148,13 @@ impl<'a> Executive<'a> {
|
||||
|
||||
// TODO: we might need bigints here, or at least check overflows.
|
||||
let balance = self.state.balance(&sender);
|
||||
let gas_cost = U512::from(t.gas) * U512::from(t.gas_price);
|
||||
let gas_cost = t.gas.full_mul(t.gas_price);
|
||||
let total_cost = U512::from(t.value) + gas_cost;
|
||||
|
||||
// avoid unaffordable transactions
|
||||
if U512::from(balance) < total_cost {
|
||||
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: U512::from(balance) }));
|
||||
let balance512 = U512::from(balance);
|
||||
if balance512 < total_cost {
|
||||
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 }));
|
||||
}
|
||||
|
||||
// NOTE: there can be no invalid transactions from this point.
|
||||
@@ -212,8 +213,11 @@ impl<'a> Executive<'a> {
|
||||
tracer: &mut T,
|
||||
vm_tracer: &mut V
|
||||
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
|
||||
|
||||
let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);
|
||||
|
||||
// Ordinary execution - keep VM in same thread
|
||||
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
|
||||
if (self.depth + 1) % depth_threshold != 0 {
|
||||
let vm_factory = self.vm_factory;
|
||||
let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer);
|
||||
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
|
||||
@@ -265,7 +269,7 @@ impl<'a> Executive<'a> {
|
||||
let cost = self.engine.cost_of_builtin(¶ms.code_address, data);
|
||||
if cost <= params.gas {
|
||||
self.engine.execute_builtin(¶ms.code_address, data, &mut output);
|
||||
self.state.clear_snapshot();
|
||||
self.state.discard_snapshot();
|
||||
|
||||
// trace only top level calls to builtins to avoid DDoS attacks
|
||||
if self.depth == 0 {
|
||||
@@ -285,7 +289,7 @@ impl<'a> Executive<'a> {
|
||||
Ok(params.gas - cost)
|
||||
} else {
|
||||
// just drain the whole gas
|
||||
self.state.revert_snapshot();
|
||||
self.state.revert_to_snapshot();
|
||||
|
||||
tracer.trace_failed_call(trace_info, vec![]);
|
||||
|
||||
@@ -331,7 +335,7 @@ impl<'a> Executive<'a> {
|
||||
res
|
||||
} else {
|
||||
// otherwise it's just a basic transaction, only do tracing, if necessary.
|
||||
self.state.clear_snapshot();
|
||||
self.state.discard_snapshot();
|
||||
|
||||
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
|
||||
Ok(params.gas)
|
||||
@@ -413,7 +417,7 @@ impl<'a> Executive<'a> {
|
||||
|
||||
// real ammount to refund
|
||||
let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() };
|
||||
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2));
|
||||
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1);
|
||||
let gas_left = gas_left_prerefund + refunded;
|
||||
|
||||
let gas_used = t.gas - gas_left;
|
||||
@@ -473,10 +477,10 @@ impl<'a> Executive<'a> {
|
||||
| Err(evm::Error::BadInstruction {.. })
|
||||
| Err(evm::Error::StackUnderflow {..})
|
||||
| Err(evm::Error::OutOfStack {..}) => {
|
||||
self.state.revert_snapshot();
|
||||
self.state.revert_to_snapshot();
|
||||
},
|
||||
Ok(_) | Err(evm::Error::Internal) => {
|
||||
self.state.clear_snapshot();
|
||||
self.state.discard_snapshot();
|
||||
substate.accrue(un_substate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,13 @@ use state::State;
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||
use executive::contract_address;
|
||||
use block::{ClosedBlock, IsBlock, Block};
|
||||
use header::BlockNumber;
|
||||
use error::*;
|
||||
use transaction::{Action, SignedTransaction};
|
||||
use receipt::{Receipt, RichReceipt};
|
||||
use spec::Spec;
|
||||
use engines::Engine;
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||
use miner::work_notify::WorkPoster;
|
||||
use client::TransactionImportResult;
|
||||
use miner::price_info::PriceInfo;
|
||||
@@ -64,6 +65,8 @@ pub struct MinerOptions {
|
||||
pub tx_gas_limit: U256,
|
||||
/// Maximum size of the transaction queue.
|
||||
pub tx_queue_size: usize,
|
||||
/// Strategy to use for prioritizing transactions in the queue.
|
||||
pub tx_queue_strategy: PrioritizationStrategy,
|
||||
/// 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?
|
||||
@@ -80,7 +83,8 @@ impl Default for MinerOptions {
|
||||
reseal_on_external_tx: false,
|
||||
reseal_on_own_tx: true,
|
||||
tx_gas_limit: !U256::zero(),
|
||||
tx_queue_size: 1024,
|
||||
tx_queue_size: 2048,
|
||||
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
|
||||
pending_set: PendingSet::AlwaysQueue,
|
||||
reseal_min_period: Duration::from_secs(2),
|
||||
work_queue_size: 20,
|
||||
@@ -188,7 +192,7 @@ impl Miner {
|
||||
/// Creates new instance of miner without accounts, but with given spec.
|
||||
pub fn with_spec(spec: &Spec) -> Miner {
|
||||
Miner {
|
||||
transaction_queue: Arc::new(Mutex::new(TransactionQueue::new())),
|
||||
transaction_queue: Arc::new(Mutex::new(TransactionQueue::default())),
|
||||
options: Default::default(),
|
||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||
sealing_block_last_request: Mutex::new(0),
|
||||
@@ -206,7 +210,9 @@ impl Miner {
|
||||
/// Creates new instance of miner
|
||||
pub fn new(options: MinerOptions, gas_pricer: GasPricer, 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 };
|
||||
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)));
|
||||
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(
|
||||
options.tx_queue_strategy, options.tx_queue_size, options.tx_gas_limit
|
||||
)));
|
||||
Arc::new(Miner {
|
||||
transaction_queue: txq,
|
||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||
@@ -444,6 +450,21 @@ impl Miner {
|
||||
|
||||
/// Are we allowed to do a non-mandatory reseal?
|
||||
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() }
|
||||
|
||||
fn from_pending_block<H, F, G>(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H
|
||||
where F: Fn() -> H, G: Fn(&ClosedBlock) -> H {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
|| from_chain(),
|
||||
|b| {
|
||||
if b.block().header().number() > latest_block_number {
|
||||
map_block(b)
|
||||
} else {
|
||||
from_chain()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
|
||||
@@ -516,29 +537,35 @@ impl MinerService for Miner {
|
||||
}
|
||||
|
||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_balance(address),
|
||||
|b| b.block().fields().state.balance(address)
|
||||
)
|
||||
}
|
||||
|
||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_storage_at(address, position),
|
||||
|b| b.block().fields().state.storage_at(address, position)
|
||||
)
|
||||
}
|
||||
|
||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_nonce(address),
|
||||
|b| b.block().fields().state.nonce(address)
|
||||
)
|
||||
}
|
||||
|
||||
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address).map(|c| (*c).clone()))
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_code(address),
|
||||
|b| b.block().fields().state.code(address).map(|c| (*c).clone())
|
||||
)
|
||||
}
|
||||
|
||||
fn set_author(&self, author: Address) {
|
||||
@@ -683,50 +710,74 @@ impl MinerService for Miner {
|
||||
queue.top_transactions()
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
fn pending_transactions(&self, best_block: BlockNumber) -> Vec<SignedTransaction> {
|
||||
let queue = self.transaction_queue.lock();
|
||||
let sw = self.sealing_work.lock();
|
||||
// TODO: should only use the sealing_work when it's current (it could be an old block)
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
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().to_owned()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.top_transactions(),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.top_transactions(),
|
||||
|sealing| sealing.transactions().to_owned()
|
||||
)
|
||||
},
|
||||
PendingSet::AlwaysSealing => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| vec![],
|
||||
|sealing| sealing.transactions().to_owned()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256> {
|
||||
fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256> {
|
||||
let queue = self.transaction_queue.lock();
|
||||
let sw = self.sealing_work.lock();
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.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()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.pending_hashes(),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.pending_hashes(),
|
||||
|sealing| sealing.transactions().iter().map(|t| t.hash()).collect()
|
||||
)
|
||||
},
|
||||
PendingSet::AlwaysSealing => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| vec![],
|
||||
|sealing| sealing.transactions().iter().map(|t| t.hash()).collect()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||
fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction> {
|
||||
let queue = self.transaction_queue.lock();
|
||||
let sw = self.sealing_work.lock();
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.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()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.find(hash),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.find(hash),
|
||||
|sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned()
|
||||
)
|
||||
},
|
||||
PendingSet::AlwaysSealing => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| None,
|
||||
|sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||
(true, Some(pending)) => {
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| None,
|
||||
|pending| {
|
||||
let txs = pending.transactions();
|
||||
txs.iter()
|
||||
.map(|t| t.hash())
|
||||
@@ -747,15 +798,15 @@ impl MinerService for Miner {
|
||||
logs: receipt.logs.clone(),
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||
(true, Some(pending)) => {
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt> {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| BTreeMap::new(),
|
||||
|pending| {
|
||||
let hashes = pending.transactions()
|
||||
.iter()
|
||||
.map(|t| t.hash());
|
||||
@@ -763,9 +814,8 @@ impl MinerService for Miner {
|
||||
let receipts = pending.receipts().iter().cloned();
|
||||
|
||||
hashes.zip(receipts).collect()
|
||||
},
|
||||
_ => BTreeMap::new()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256> {
|
||||
@@ -918,7 +968,7 @@ impl MinerService for Miner {
|
||||
mod tests {
|
||||
|
||||
use std::time::Duration;
|
||||
use super::super::MinerService;
|
||||
use super::super::{MinerService, PrioritizationStrategy};
|
||||
use super::*;
|
||||
use util::*;
|
||||
use client::{TestBlockChainClient, EachBlockWith};
|
||||
@@ -969,6 +1019,7 @@ mod tests {
|
||||
reseal_min_period: Duration::from_secs(5),
|
||||
tx_gas_limit: !U256::zero(),
|
||||
tx_queue_size: 1024,
|
||||
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
|
||||
pending_set: PendingSet::AlwaysSealing,
|
||||
work_queue_size: 5,
|
||||
enable_resubmission: true,
|
||||
@@ -995,20 +1046,48 @@ mod tests {
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
};
|
||||
|
||||
let best_block = 0;
|
||||
// when
|
||||
let res = miner.import_own_transaction(&client, transaction);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes().len(), 1);
|
||||
assert_eq!(miner.pending_receipts().len(), 1);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1);
|
||||
assert_eq!(miner.pending_receipts(best_block).len(), 1);
|
||||
// This method will let us know if pending block was created (before calling that method)
|
||||
assert_eq!(miner.enable_and_prepare_sealing(&client), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_use_pending_block_if_best_block_is_higher() {
|
||||
// given
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
let transaction = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::zero(),
|
||||
data: "3331600055".from_hex().unwrap(),
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
};
|
||||
let best_block = 10;
|
||||
// when
|
||||
let res = miner.import_own_transaction(&client, transaction);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_import_external_transaction() {
|
||||
// given
|
||||
@@ -1025,16 +1104,16 @@ mod tests {
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
};
|
||||
|
||||
let best_block = 0;
|
||||
// when
|
||||
let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes().len(), 0);
|
||||
assert_eq!(miner.pending_transactions().len(), 0);
|
||||
assert_eq!(miner.pending_receipts().len(), 0);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
||||
// This method will let us know if pending block was created (before calling that method)
|
||||
assert_eq!(miner.enable_and_prepare_sealing(&client), true);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ mod transaction_queue;
|
||||
mod work_notify;
|
||||
mod price_info;
|
||||
|
||||
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||
pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions};
|
||||
pub use self::external::{ExternalMiner, ExternalMinerService};
|
||||
pub use client::TransactionImportResult;
|
||||
@@ -56,6 +56,7 @@ use std::collections::BTreeMap;
|
||||
use util::{H256, U256, Address, Bytes};
|
||||
use client::{MiningBlockChainClient, Executed, CallAnalytics};
|
||||
use block::ClosedBlock;
|
||||
use header::BlockNumber;
|
||||
use receipt::{RichReceipt, Receipt};
|
||||
use error::{Error, CallError};
|
||||
use transaction::SignedTransaction;
|
||||
@@ -115,7 +116,7 @@ pub trait MinerService : Send + Sync {
|
||||
Result<TransactionImportResult, Error>;
|
||||
|
||||
/// Returns hashes of transactions currently in pending
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256>;
|
||||
fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256>;
|
||||
|
||||
/// Removes all transactions from the queue and restart mining operation.
|
||||
fn clear_and_reset(&self, chain: &MiningBlockChainClient);
|
||||
@@ -135,19 +136,19 @@ pub trait MinerService : Send + Sync {
|
||||
where F: FnOnce(&ClosedBlock) -> T, Self: Sized;
|
||||
|
||||
/// Query pending transactions for hash.
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction>;
|
||||
fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction>;
|
||||
|
||||
/// Get a list of all transactions.
|
||||
fn all_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get a list of all pending transactions.
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
fn pending_transactions(&self, best_block: BlockNumber) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get a list of all pending receipts.
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt>;
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt>;
|
||||
|
||||
/// Get a particular reciept.
|
||||
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt>;
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
|
||||
|
||||
/// Returns highest transaction nonce for given address.
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
//! balance: U256::from(1_000_000),
|
||||
//! };
|
||||
//!
|
||||
//! let mut txq = TransactionQueue::new();
|
||||
//! let mut txq = TransactionQueue::default();
|
||||
//! txq.add(st2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//! txq.add(st1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//!
|
||||
@@ -134,9 +134,17 @@ struct TransactionOrder {
|
||||
/// Gas Price of the transaction.
|
||||
/// Low gas price = Low priority (processed later)
|
||||
gas_price: U256,
|
||||
/// Gas (limit) of the transaction.
|
||||
/// Gas usage priority factor. Usage depends on strategy.
|
||||
/// Represents the linear increment in required gas price for heavy transactions.
|
||||
///
|
||||
/// High gas limit + Low gas price = Low priority
|
||||
/// High gas limit + High gas price = High priority
|
||||
gas_factor: U256,
|
||||
/// Gas (limit) of the transaction. Usage depends on strategy.
|
||||
/// Low gas limit = High priority (processed earlier)
|
||||
gas: U256,
|
||||
/// Transaction ordering strategy
|
||||
strategy: PrioritizationStrategy,
|
||||
/// Hash to identify associated transaction
|
||||
hash: H256,
|
||||
/// Origin of the transaction
|
||||
@@ -147,11 +155,15 @@ struct TransactionOrder {
|
||||
|
||||
|
||||
impl TransactionOrder {
|
||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
||||
|
||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256, min_gas_price: U256, strategy: PrioritizationStrategy) -> Self {
|
||||
let factor = (tx.transaction.gas >> 15) * min_gas_price;
|
||||
TransactionOrder {
|
||||
nonce_height: tx.nonce() - base_nonce,
|
||||
gas_price: tx.transaction.gas_price,
|
||||
gas: tx.transaction.gas,
|
||||
gas_factor: factor,
|
||||
strategy: strategy,
|
||||
hash: tx.hash(),
|
||||
origin: tx.origin,
|
||||
penalties: 0,
|
||||
@@ -199,18 +211,28 @@ impl Ord for TransactionOrder {
|
||||
return self.origin.cmp(&b.origin);
|
||||
}
|
||||
|
||||
// Then compare gas usage
|
||||
let a_gas = self.gas;
|
||||
let b_gas = b.gas;
|
||||
if a_gas != b_gas {
|
||||
return a_gas.cmp(&b_gas);
|
||||
match self.strategy {
|
||||
PrioritizationStrategy::GasAndGasPrice => {
|
||||
if self.gas != b.gas {
|
||||
return self.gas.cmp(&b.gas);
|
||||
}
|
||||
},
|
||||
PrioritizationStrategy::GasFactorAndGasPrice => {
|
||||
// avoiding overflows
|
||||
// (gp1 - g1) > (gp2 - g2) <=>
|
||||
// (gp1 + g2) > (gp2 + g1)
|
||||
let f_a = self.gas_price + b.gas_factor;
|
||||
let f_b = b.gas_price + self.gas_factor;
|
||||
if f_a != f_b {
|
||||
return f_b.cmp(&f_a);
|
||||
}
|
||||
},
|
||||
PrioritizationStrategy::GasPriceOnly => {},
|
||||
}
|
||||
|
||||
// Then compare gas_prices
|
||||
let a_gas_price = self.gas_price;
|
||||
let b_gas_price = b.gas_price;
|
||||
if a_gas_price != b_gas_price {
|
||||
return b_gas_price.cmp(&a_gas_price);
|
||||
if self.gas_price != b.gas_price {
|
||||
return b.gas_price.cmp(&self.gas_price);
|
||||
}
|
||||
|
||||
// Compare hashes
|
||||
@@ -350,8 +372,32 @@ pub struct AccountDetails {
|
||||
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
||||
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
|
||||
|
||||
/// Describes the strategy used to prioritize transactions in the queue.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PrioritizationStrategy {
|
||||
/// Use only gas price. Disregards the actual computation cost of the transaction.
|
||||
/// i.e. Higher gas price = Higher priority
|
||||
GasPriceOnly,
|
||||
/// Use gas limit and then gas price.
|
||||
/// i.e. Higher gas limit = Lower priority
|
||||
GasAndGasPrice,
|
||||
/// Calculate and use priority based on gas and gas price.
|
||||
/// PRIORITY = GAS_PRICE - GAS/2^15 * MIN_GAS_PRICE
|
||||
///
|
||||
/// Rationale:
|
||||
/// Heavy transactions are paying linear cost (GAS * GAS_PRICE)
|
||||
/// while the computation might be more expensive.
|
||||
///
|
||||
/// i.e.
|
||||
/// 1M gas tx with `gas_price=30*min` has the same priority
|
||||
/// as 32k gas tx with `gas_price=min`
|
||||
GasFactorAndGasPrice,
|
||||
}
|
||||
|
||||
/// `TransactionQueue` implementation
|
||||
pub struct TransactionQueue {
|
||||
/// Prioritization strategy for this queue
|
||||
strategy: PrioritizationStrategy,
|
||||
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
|
||||
minimal_gas_price: U256,
|
||||
/// The maximum amount of gas any individual transaction may use.
|
||||
@@ -370,18 +416,18 @@ pub struct TransactionQueue {
|
||||
|
||||
impl Default for TransactionQueue {
|
||||
fn default() -> Self {
|
||||
TransactionQueue::new()
|
||||
TransactionQueue::new(PrioritizationStrategy::GasPriceOnly)
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionQueue {
|
||||
/// Creates new instance of this Queue
|
||||
pub fn new() -> Self {
|
||||
Self::with_limits(1024, !U256::zero())
|
||||
pub fn new(strategy: PrioritizationStrategy) -> Self {
|
||||
Self::with_limits(strategy, 1024, !U256::zero())
|
||||
}
|
||||
|
||||
/// Create new instance of this Queue with specified limits
|
||||
pub fn with_limits(limit: usize, tx_gas_limit: U256) -> Self {
|
||||
pub fn with_limits(strategy: PrioritizationStrategy, limit: usize, tx_gas_limit: U256) -> Self {
|
||||
let current = TransactionSet {
|
||||
by_priority: BTreeSet::new(),
|
||||
by_address: Table::new(),
|
||||
@@ -395,6 +441,7 @@ impl TransactionQueue {
|
||||
};
|
||||
|
||||
TransactionQueue {
|
||||
strategy: strategy,
|
||||
minimal_gas_price: U256::zero(),
|
||||
tx_gas_limit: tx_gas_limit,
|
||||
gas_limit: !U256::zero(),
|
||||
@@ -560,7 +607,7 @@ impl TransactionQueue {
|
||||
};
|
||||
for k in nonces_from_sender {
|
||||
let order = self.future.drop(&sender, &k).unwrap();
|
||||
self.current.insert(sender, k, order.penalize());
|
||||
self.future.insert(sender, k, order.penalize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,6 +705,15 @@ impl TransactionQueue {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn future_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.future.by_priority
|
||||
.iter()
|
||||
.map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`"))
|
||||
.map(|t| t.transaction.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns hashes of all transactions from current, ordered by priority.
|
||||
pub fn pending_hashes(&self) -> Vec<H256> {
|
||||
self.current.by_priority
|
||||
@@ -736,6 +792,7 @@ impl TransactionQueue {
|
||||
return Err(TransactionError::AlreadyImported);
|
||||
}
|
||||
|
||||
let min_gas_price = (self.minimal_gas_price, self.strategy);
|
||||
let address = tx.sender();
|
||||
let nonce = tx.nonce();
|
||||
let hash = tx.hash();
|
||||
@@ -763,7 +820,7 @@ impl TransactionQueue {
|
||||
if nonce > next_nonce {
|
||||
// We have a gap - put to future.
|
||||
// Insert transaction (or replace old one with lower gas price)
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.future, &mut self.by_hash)));
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.future, &mut self.by_hash)));
|
||||
// Enforce limit in Future
|
||||
let removed = self.future.enforce_limit(&mut self.by_hash);
|
||||
// Return an error if this transaction was not imported because of limit.
|
||||
@@ -779,7 +836,7 @@ impl TransactionQueue {
|
||||
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
|
||||
|
||||
// Replace transaction if any
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash)));
|
||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.current, &mut self.by_hash)));
|
||||
// Keep track of highest nonce stored in current
|
||||
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
||||
self.last_nonces.insert(address, new_max);
|
||||
@@ -815,8 +872,8 @@ impl TransactionQueue {
|
||||
///
|
||||
/// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher
|
||||
/// gas_price)
|
||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, min_gas_price: (U256, PrioritizationStrategy), set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||
let order = TransactionOrder::for_transaction(&tx, base_nonce, min_gas_price.0, min_gas_price.1);
|
||||
let hash = tx.hash();
|
||||
let address = tx.sender();
|
||||
let nonce = tx.nonce();
|
||||
@@ -982,6 +1039,10 @@ mod test {
|
||||
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
|
||||
}
|
||||
|
||||
fn transaction_order(tx: &VerifiedTransaction, nonce: U256) -> TransactionOrder {
|
||||
TransactionOrder::for_transaction(tx, nonce, 0.into(), PrioritizationStrategy::GasPriceOnly)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_transaction_set() {
|
||||
// given
|
||||
@@ -1002,9 +1063,9 @@ mod test {
|
||||
x
|
||||
};
|
||||
// Insert both transactions
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||
let order1 = transaction_order(&tx1, U256::zero());
|
||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
|
||||
let order2 = transaction_order(&tx2, U256::zero());
|
||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||
assert_eq!(set.by_priority.len(), 2);
|
||||
assert_eq!(set.by_address.len(), 2);
|
||||
@@ -1043,12 +1104,12 @@ mod test {
|
||||
x
|
||||
};
|
||||
// Insert both transactions
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
||||
let order1 = transaction_order(&tx1, U256::zero());
|
||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
// Two different orders (imagine nonce changed in the meantime)
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::one());
|
||||
let order2 = transaction_order(&tx2, U256::one());
|
||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||
assert_eq!(set.by_priority.len(), 1);
|
||||
assert_eq!(set.by_address.len(), 1);
|
||||
@@ -1063,7 +1124,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_similar_txs();
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
@@ -1088,7 +1149,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_move_all_transactions_from_future() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs_with_gas_price_diff(1.into(), 1.into());
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
@@ -1114,7 +1175,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_import_tx() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
|
||||
// when
|
||||
@@ -1129,7 +1190,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_order_by_gas() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasAndGasPrice);
|
||||
let tx1 = new_tx_with_gas(50000.into(), 40.into());
|
||||
let tx2 = new_tx_with_gas(40000.into(), 30.into());
|
||||
let tx3 = new_tx_with_gas(30000.into(), 10.into());
|
||||
@@ -1159,10 +1220,45 @@ mod test {
|
||||
assert_eq!(txq.top_transactions()[2].gas_price, 20.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_order_by_gas_factor() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasFactorAndGasPrice);
|
||||
|
||||
let tx1 = new_tx_with_gas(150_000.into(), 40.into());
|
||||
let tx2 = new_tx_with_gas(40_000.into(), 16.into());
|
||||
let tx3 = new_tx_with_gas(30_000.into(), 15.into());
|
||||
let tx4 = new_tx_with_gas(150_000.into(), 62.into());
|
||||
txq.set_minimal_gas_price(15.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx1, &default_nonce, TransactionOrigin::External);
|
||||
let res2 = txq.add(tx2, &default_nonce, TransactionOrigin::External);
|
||||
let res3 = txq.add(tx3, &default_nonce, TransactionOrigin::External);
|
||||
let res4 = txq.add(tx4, &default_nonce, TransactionOrigin::External);
|
||||
|
||||
// then
|
||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(res2.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(res3.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
|
||||
let stats = txq.status();
|
||||
assert_eq!(stats.pending, 4);
|
||||
println!("{:?}", txq.top_transactions());
|
||||
assert_eq!(txq.top_transactions()[0].gas, 30_000.into());
|
||||
assert_eq!(txq.top_transactions()[1].gas, 150_000.into());
|
||||
assert_eq!(txq.top_transactions()[2].gas, 40_000.into());
|
||||
assert_eq!(txq.top_transactions()[3].gas, 150_000.into());
|
||||
assert_eq!(txq.top_transactions()[0].gas_price, 15.into());
|
||||
assert_eq!(txq.top_transactions()[1].gas_price, 62.into());
|
||||
assert_eq!(txq.top_transactions()[2].gas_price, 16.into());
|
||||
assert_eq!(txq.top_transactions()[3].gas_price, 40.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gas_limit_should_never_overflow() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
txq.set_gas_limit(U256::zero());
|
||||
assert_eq!(txq.gas_limit, U256::zero());
|
||||
|
||||
@@ -1176,7 +1272,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_import_transaction_above_gas_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let gas = tx.gas;
|
||||
let limit = gas / U256::from(2);
|
||||
@@ -1198,7 +1294,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_drop_transactions_from_senders_without_balance() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let account = |a: &Address| AccountDetails {
|
||||
nonce: default_nonce(a).nonce,
|
||||
@@ -1221,7 +1317,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
@@ -1241,7 +1337,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
@@ -1258,7 +1354,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_reject_incorrectly_signed_transaction() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_unsigned_tx(U256::from(123));
|
||||
let stx = {
|
||||
let mut s = RlpStream::new_list(9);
|
||||
@@ -1283,7 +1379,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_import_txs_from_same_sender() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
|
||||
@@ -1301,7 +1397,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_prioritize_local_transactions_within_same_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
// the second one has same nonce but higher `gas_price`
|
||||
let (_, tx2) = new_similar_txs();
|
||||
@@ -1322,7 +1418,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_prioritize_reimported_transactions_within_same_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
// the second one has same nonce but higher `gas_price`
|
||||
let (_, tx2) = new_similar_txs();
|
||||
@@ -1343,7 +1439,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
|
||||
// when
|
||||
@@ -1357,10 +1453,40 @@ mod test {
|
||||
assert_eq!(top.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_penalize_transactions_from_sender_in_future() {
|
||||
// given
|
||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::default();
|
||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||
let (txa, txb) = new_txs(U256::from(1));
|
||||
let (tx1, tx2) = new_txs_with_higher_gas_price(U256::from(3));
|
||||
|
||||
// insert everything
|
||||
txq.add(txa.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(txb.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx1.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
|
||||
assert_eq!(txq.status().future, 4);
|
||||
|
||||
// when
|
||||
txq.penalize(&tx1.hash());
|
||||
|
||||
// then
|
||||
let top = txq.future_transactions();
|
||||
assert_eq!(top[0], txa);
|
||||
assert_eq!(top[1], txb);
|
||||
assert_eq!(top[2], tx1);
|
||||
assert_eq!(top[3], tx2);
|
||||
assert_eq!(top.len(), 4);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_penalize_transactions_from_sender() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||
let (txa, txb) = new_txs(U256::from(1));
|
||||
let (tx1, tx2) = new_txs_with_higher_gas_price(U256::from(3));
|
||||
@@ -1393,7 +1519,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_pending_hashes() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
|
||||
@@ -1411,7 +1537,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_put_transaction_to_futures_if_gap_detected() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(2));
|
||||
|
||||
@@ -1437,7 +1563,7 @@ mod test {
|
||||
!U256::zero() };
|
||||
let next2_nonce = default_nonce_val() + U256::from(3);
|
||||
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1456,7 +1582,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_move_transactions_if_gap_filled() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let kp = KeyPair::create().unwrap();
|
||||
let secret = kp.secret();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(secret);
|
||||
@@ -1480,7 +1606,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_remove_transaction() {
|
||||
// given
|
||||
let mut txq2 = TransactionQueue::new();
|
||||
let mut txq2 = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(3));
|
||||
txq2.add(tx.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq2.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1501,7 +1627,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_move_transactions_to_future_if_gap_introduced() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let tx3 = new_tx();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1522,7 +1648,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_clear_queue() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::one());
|
||||
|
||||
// add
|
||||
@@ -1542,7 +1668,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero());
|
||||
let (tx, tx2) = new_txs(U256::one());
|
||||
let sender = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
@@ -1564,7 +1690,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(2, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 2, !U256::zero());
|
||||
let tx = new_tx();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let sender = tx1.sender().unwrap();
|
||||
@@ -1585,7 +1711,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn should_limit_future_transactions() {
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero());
|
||||
txq.current.set_limit(10);
|
||||
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));
|
||||
@@ -1604,7 +1730,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn should_drop_transactions_with_old_nonces() {
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let last_nonce = tx.nonce + U256::one();
|
||||
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
|
||||
@@ -1624,7 +1750,7 @@ mod test {
|
||||
// given
|
||||
let nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce + U256::one(),
|
||||
balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (_tx1, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
@@ -1643,7 +1769,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_accept_same_transaction_twice_if_removed() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1664,7 +1790,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_not_move_to_future_if_state_nonce_is_higher() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_txs(U256::from(1));
|
||||
let tx3 = new_tx();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1687,7 +1813,7 @@ mod test {
|
||||
fn should_replace_same_transaction_when_has_higher_fee() {
|
||||
init_log();
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let tx2 = {
|
||||
@@ -1710,7 +1836,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_replace_same_transaction_when_importing_to_futures() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx0 = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let tx1 = {
|
||||
@@ -1744,7 +1870,7 @@ mod test {
|
||||
!U256::zero() };
|
||||
let next_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
|
||||
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
|
||||
@@ -1762,7 +1888,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_none_when_transaction_from_given_address_does_not_exist() {
|
||||
// given
|
||||
let txq = TransactionQueue::new();
|
||||
let txq = TransactionQueue::default();
|
||||
|
||||
// then
|
||||
assert_eq!(txq.last_nonce(&Address::default()), None);
|
||||
@@ -1771,7 +1897,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let tx = new_tx();
|
||||
let from = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
@@ -1787,7 +1913,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::one());
|
||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
||||
@@ -1805,7 +1931,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_valid_last_nonce_after_remove_all() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::from(4));
|
||||
let sender = tx1.sender().unwrap();
|
||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||
@@ -1829,7 +1955,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_true_if_there_is_local_transaction_pending() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||
|
||||
@@ -1845,7 +1971,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_keep_right_order_in_future() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero());
|
||||
let (tx1, tx2) = new_txs(U256::from(1));
|
||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance:
|
||||
default_nonce(a).balance };
|
||||
@@ -1862,7 +1988,7 @@ mod test {
|
||||
#[test]
|
||||
fn should_return_correct_last_nonce() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::new();
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2, tx2_2, tx3) = {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let secret = &keypair.secret();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::{RefCell, RefMut};
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use common::*;
|
||||
use engines::Engine;
|
||||
use executive::{Executive, TransactOptions};
|
||||
@@ -38,42 +38,93 @@ pub struct ApplyOutcome {
|
||||
/// Result type for the execution ("application") of a transaction.
|
||||
pub type ApplyResult = Result<ApplyOutcome, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AccountEntry {
|
||||
/// Contains account data.
|
||||
Cached(Account),
|
||||
/// Account has been deleted.
|
||||
Killed,
|
||||
/// Account does not exist.
|
||||
Missing,
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||
/// Account modification state. Used to check if the account was
|
||||
/// Modified in between commits and overall.
|
||||
enum AccountState {
|
||||
/// Account was loaded from disk and never modified in this state object.
|
||||
CleanFresh,
|
||||
/// Account was loaded from the global cache and never modified.
|
||||
CleanCached,
|
||||
/// Account has been modified and is not committed to the trie yet.
|
||||
/// This is set if any of the account data is changed, including
|
||||
/// storage and code.
|
||||
Dirty,
|
||||
/// Account was modified and committed to the trie.
|
||||
Committed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// In-memory copy of the account data. Holds the optional account
|
||||
/// and the modification status.
|
||||
/// Account entry can contain existing (`Some`) or non-existing
|
||||
/// account (`None`)
|
||||
struct AccountEntry {
|
||||
account: Option<Account>,
|
||||
state: AccountState,
|
||||
}
|
||||
|
||||
// Account cache item. Contains account data and
|
||||
// modification state
|
||||
impl AccountEntry {
|
||||
fn is_dirty(&self) -> bool {
|
||||
match *self {
|
||||
AccountEntry::Cached(ref a) => a.is_dirty(),
|
||||
AccountEntry::Killed => true,
|
||||
AccountEntry::Missing => false,
|
||||
}
|
||||
self.state == AccountState::Dirty
|
||||
}
|
||||
|
||||
/// Clone dirty data into new `AccountEntry`.
|
||||
/// Clone dirty data into new `AccountEntry`. This includes
|
||||
/// basic account data and modified storage keys.
|
||||
/// Returns None if clean.
|
||||
fn clone_dirty(&self) -> Option<AccountEntry> {
|
||||
match *self {
|
||||
AccountEntry::Cached(ref acc) if acc.is_dirty() => Some(AccountEntry::Cached(acc.clone_dirty())),
|
||||
AccountEntry::Killed => Some(AccountEntry::Killed),
|
||||
_ => None,
|
||||
fn clone_if_dirty(&self) -> Option<AccountEntry> {
|
||||
match self.is_dirty() {
|
||||
true => Some(self.clone_dirty()),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone account entry data that needs to be saved in the snapshot.
|
||||
/// This includes basic account information and all locally cached storage keys
|
||||
fn clone_for_snapshot(&self) -> AccountEntry {
|
||||
match *self {
|
||||
AccountEntry::Cached(ref acc) => AccountEntry::Cached(acc.clone_all()),
|
||||
AccountEntry::Killed => AccountEntry::Killed,
|
||||
AccountEntry::Missing => AccountEntry::Missing,
|
||||
/// Clone dirty data into new `AccountEntry`. This includes
|
||||
/// basic account data and modified storage keys.
|
||||
fn clone_dirty(&self) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: self.account.as_ref().map(Account::clone_dirty),
|
||||
state: self.state,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as dirty.
|
||||
fn new_dirty(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::Dirty,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as clean.
|
||||
fn new_clean(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::CleanFresh,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as clean and cached.
|
||||
fn new_clean_cached(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::CleanCached,
|
||||
}
|
||||
}
|
||||
|
||||
// Replace data with another entry but preserve storage cache.
|
||||
fn overwrite_with(&mut self, other: AccountEntry) {
|
||||
self.state = other.state;
|
||||
match other.account {
|
||||
Some(acc) => match self.account {
|
||||
Some(ref mut ours) => {
|
||||
ours.overwrite_with(acc);
|
||||
},
|
||||
None => {},
|
||||
},
|
||||
None => self.account = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +137,9 @@ impl AccountEntry {
|
||||
/// locally from previous commits. Global cache reflects the database
|
||||
/// state and never contains any changes.
|
||||
///
|
||||
/// Cache items contains account data, or the flag that account does not exist
|
||||
/// and modification state (see `AccountState`)
|
||||
///
|
||||
/// Account data can be in the following cache states:
|
||||
/// * In global but not local - something that was queried from the database,
|
||||
/// but never modified
|
||||
@@ -99,12 +153,32 @@ impl AccountEntry {
|
||||
/// then global state cache. If data is not found in any of the caches
|
||||
/// it is loaded from the DB to the local cache.
|
||||
///
|
||||
/// **** IMPORTANT *************************************************************
|
||||
/// All the modifications to the account data must set the `Dirty` state in the
|
||||
/// `AccountEntry`. This is done in `require` and `require_or_from`. So just
|
||||
/// use that.
|
||||
/// ****************************************************************************
|
||||
///
|
||||
/// Upon destruction all the local cache data merged into the global cache.
|
||||
/// The merge might be rejected if current state is non-canonical.
|
||||
///
|
||||
/// State snapshotting.
|
||||
///
|
||||
/// A new snapshot can be created with `snapshot()`. Snapshots can be
|
||||
/// created in a hierarchy.
|
||||
/// When a snapshot is active all changes are applied directly into
|
||||
/// `cache` and the original value is copied into an active snapshot.
|
||||
/// Reverting a snapshot with `revert_to_snapshot` involves copying
|
||||
/// original values from the latest snapshot back into `cache`. The code
|
||||
/// takes care not to overwrite cached storage while doing that.
|
||||
/// Snapshot can be discateded with `discard_snapshot`. All of the orignal
|
||||
/// backed-up values are moved into a parent snapshot (if any).
|
||||
///
|
||||
pub struct State {
|
||||
db: StateDB,
|
||||
root: H256,
|
||||
cache: RefCell<HashMap<Address, AccountEntry>>,
|
||||
// The original account is preserved in
|
||||
snapshots: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
|
||||
account_start_nonce: U256,
|
||||
trie_factory: TrieFactory,
|
||||
@@ -158,35 +232,48 @@ impl State {
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Create a recoverable snaphot of this state
|
||||
/// Create a recoverable snaphot of this state.
|
||||
pub fn snapshot(&mut self) {
|
||||
self.snapshots.borrow_mut().push(HashMap::new());
|
||||
}
|
||||
|
||||
/// Merge last snapshot with previous
|
||||
pub fn clear_snapshot(&mut self) {
|
||||
/// Merge last snapshot with previous.
|
||||
pub fn discard_snapshot(&mut self) {
|
||||
// merge with previous snapshot
|
||||
let last = self.snapshots.borrow_mut().pop();
|
||||
if let Some(mut snapshot) = last {
|
||||
if let Some(ref mut prev) = self.snapshots.borrow_mut().last_mut() {
|
||||
for (k, v) in snapshot.drain() {
|
||||
prev.entry(k).or_insert(v);
|
||||
if prev.is_empty() {
|
||||
**prev = snapshot;
|
||||
} else {
|
||||
for (k, v) in snapshot.drain() {
|
||||
prev.entry(k).or_insert(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Revert to snapshot
|
||||
pub fn revert_snapshot(&mut self) {
|
||||
/// Revert to the last snapshot and discard it.
|
||||
pub fn revert_to_snapshot(&mut self) {
|
||||
if let Some(mut snapshot) = self.snapshots.borrow_mut().pop() {
|
||||
for (k, v) in snapshot.drain() {
|
||||
match v {
|
||||
Some(v) => {
|
||||
self.cache.borrow_mut().insert(k, v);
|
||||
match self.cache.borrow_mut().entry(k) {
|
||||
Entry::Occupied(mut e) => {
|
||||
// Merge snapshotted changes back into the main account
|
||||
// storage preserving the cache.
|
||||
e.get_mut().overwrite_with(v);
|
||||
},
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(v);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
match self.cache.borrow_mut().entry(k) {
|
||||
::std::collections::hash_map::Entry::Occupied(e) => {
|
||||
Entry::Occupied(e) => {
|
||||
if e.get().is_dirty() {
|
||||
e.remove();
|
||||
}
|
||||
@@ -200,10 +287,17 @@ impl State {
|
||||
}
|
||||
|
||||
fn insert_cache(&self, address: &Address, account: AccountEntry) {
|
||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||
if !snapshot.contains_key(address) {
|
||||
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
|
||||
return;
|
||||
// Dirty account which is not in the cache means this is a new account.
|
||||
// It goes directly into the snapshot as there's nothing to rever to.
|
||||
//
|
||||
// In all other cases account is read as clean first, and after that made
|
||||
// dirty in and added to the snapshot with `note_cache`.
|
||||
if account.is_dirty() {
|
||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||
if !snapshot.contains_key(address) {
|
||||
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cache.borrow_mut().insert(address.clone(), account);
|
||||
@@ -212,14 +306,14 @@ impl State {
|
||||
fn note_cache(&self, address: &Address) {
|
||||
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
|
||||
if !snapshot.contains_key(address) {
|
||||
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_for_snapshot));
|
||||
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_dirty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy the current object and return root and database.
|
||||
pub fn drop(mut self) -> (H256, StateDB) {
|
||||
self.commit_cache();
|
||||
self.update_shared_cache();
|
||||
(self.root, self.db)
|
||||
}
|
||||
|
||||
@@ -231,12 +325,12 @@ impl State {
|
||||
/// Create a new contract at address `contract`. If there is already an account at the address
|
||||
/// it will have its code reset, ready for `init_code()`.
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
|
||||
self.insert_cache(contract, AccountEntry::Cached(Account::new_contract(balance, self.account_start_nonce)));
|
||||
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce))));
|
||||
}
|
||||
|
||||
/// Remove an existing account.
|
||||
pub fn kill_account(&mut self, account: &Address) {
|
||||
self.insert_cache(account, AccountEntry::Killed);
|
||||
self.insert_cache(account, AccountEntry::new_dirty(None));
|
||||
}
|
||||
|
||||
/// Determine whether an account exists.
|
||||
@@ -271,8 +365,8 @@ impl State {
|
||||
let local_cache = self.cache.borrow_mut();
|
||||
let mut local_account = None;
|
||||
if let Some(maybe_acc) = local_cache.get(address) {
|
||||
match *maybe_acc {
|
||||
AccountEntry::Cached(ref account) => {
|
||||
match maybe_acc.account {
|
||||
Some(ref account) => {
|
||||
if let Some(value) = account.cached_storage_at(key) {
|
||||
return value;
|
||||
} else {
|
||||
@@ -288,7 +382,7 @@ impl State {
|
||||
return result;
|
||||
}
|
||||
if let Some(ref mut acc) = local_account {
|
||||
if let AccountEntry::Cached(ref account) = **acc {
|
||||
if let Some(ref account) = acc.account {
|
||||
return account.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), account.address_hash(address)), key)
|
||||
} else {
|
||||
return H256::new()
|
||||
@@ -303,10 +397,7 @@ impl State {
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
let r = maybe_acc.as_ref().map_or(H256::new(), |a| a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key));
|
||||
match maybe_acc {
|
||||
Some(account) => self.insert_cache(address, AccountEntry::Cached(account)),
|
||||
None => self.insert_cache(address, AccountEntry::Missing),
|
||||
}
|
||||
self.insert_cache(address, AccountEntry::new_clean(maybe_acc));
|
||||
r
|
||||
}
|
||||
|
||||
@@ -330,13 +421,17 @@ impl State {
|
||||
/// Add `incr` to the balance of account `a`.
|
||||
pub fn add_balance(&mut self, a: &Address, incr: &U256) {
|
||||
trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a));
|
||||
self.require(a, false).add_balance(incr);
|
||||
if !incr.is_zero() || !self.exists(a) {
|
||||
self.require(a, false).add_balance(incr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract `decr` from the balance of account `a`.
|
||||
pub fn sub_balance(&mut self, a: &Address, decr: &U256) {
|
||||
trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a));
|
||||
self.require(a, false).sub_balance(decr);
|
||||
if !decr.is_zero() || !self.exists(a) {
|
||||
self.require(a, false).sub_balance(decr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtracts `by` from the balance of `from` and adds it to that of `to`.
|
||||
@@ -352,7 +447,9 @@ impl State {
|
||||
|
||||
/// Mutate storage of account `a` so that it is `value` for `key`.
|
||||
pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) {
|
||||
self.require(a, false).set_storage(key, value)
|
||||
if self.storage_at(a, &key) != value {
|
||||
self.require(a, false).set_storage(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise the code of account `a` so that it is `code`.
|
||||
@@ -392,10 +489,9 @@ impl State {
|
||||
accounts: &mut HashMap<Address, AccountEntry>
|
||||
) -> Result<(), Error> {
|
||||
// first, commit the sub trees.
|
||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match a {
|
||||
&mut&mut AccountEntry::Cached(ref mut account) if account.is_dirty() => {
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
match a.account {
|
||||
Some(ref mut account) => {
|
||||
db.note_account_bloom(&address);
|
||||
let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), account.address_hash(address));
|
||||
account.commit_storage(trie_factory, &mut account_db);
|
||||
@@ -407,17 +503,15 @@ impl State {
|
||||
|
||||
{
|
||||
let mut trie = trie_factory.from_existing(db.as_hashdb_mut(), root).unwrap();
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match **a {
|
||||
AccountEntry::Cached(ref mut account) if account.is_dirty() => {
|
||||
account.set_clean();
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
a.state = AccountState::Committed;
|
||||
match a.account {
|
||||
Some(ref mut account) => {
|
||||
try!(trie.insert(address, &account.rlp()));
|
||||
},
|
||||
AccountEntry::Killed => {
|
||||
None => {
|
||||
try!(trie.remove(address));
|
||||
**a = AccountEntry::Missing;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,20 +519,12 @@ impl State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit_cache(&mut self) {
|
||||
/// Merge local cache into shared canonical state cache.
|
||||
fn update_shared_cache(&mut self) {
|
||||
let mut addresses = self.cache.borrow_mut();
|
||||
for (address, a) in addresses.drain() {
|
||||
match a {
|
||||
AccountEntry::Cached(account) => {
|
||||
if !account.is_dirty() {
|
||||
self.db.cache_account(address, Some(account));
|
||||
}
|
||||
},
|
||||
AccountEntry::Missing => {
|
||||
self.db.cache_account(address, None);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
trace!("Committing cache {:?} entries", addresses.len());
|
||||
for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) {
|
||||
self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +546,7 @@ impl State {
|
||||
assert!(self.snapshots.borrow().is_empty());
|
||||
for (add, acc) in accounts.drain().into_iter() {
|
||||
self.db.note_account_bloom(&add);
|
||||
self.cache.borrow_mut().insert(add, AccountEntry::Cached(Account::from_pod(acc)));
|
||||
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +556,7 @@ impl State {
|
||||
// TODO: handle database rather than just the cache.
|
||||
// will need fat db.
|
||||
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
|
||||
if let AccountEntry::Cached(ref acc) = *opt {
|
||||
if let Some(ref acc) = opt.account {
|
||||
m.insert(add.clone(), PodAccount::from_account(acc));
|
||||
}
|
||||
m
|
||||
@@ -519,7 +605,7 @@ impl State {
|
||||
where F: Fn(Option<&Account>) -> U {
|
||||
// check local cache first
|
||||
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
||||
if let AccountEntry::Cached(ref mut account) = **maybe_acc {
|
||||
if let Some(ref mut account) = maybe_acc.account {
|
||||
Self::update_account_cache(require, account, a, self.db.as_hashdb());
|
||||
return f(Some(account));
|
||||
}
|
||||
@@ -546,10 +632,7 @@ impl State {
|
||||
Self::update_account_cache(require, account, a, self.db.as_hashdb());
|
||||
}
|
||||
let r = f(maybe_acc.as_ref());
|
||||
match maybe_acc {
|
||||
Some(account) => self.insert_cache(a, AccountEntry::Cached(account)),
|
||||
None => self.insert_cache(a, AccountEntry::Missing),
|
||||
}
|
||||
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
|
||||
r
|
||||
}
|
||||
}
|
||||
@@ -568,36 +651,38 @@ impl State {
|
||||
let contains_key = self.cache.borrow().contains_key(a);
|
||||
if !contains_key {
|
||||
match self.db.get_cached_account(a) {
|
||||
Some(Some(acc)) => self.insert_cache(a, AccountEntry::Cached(acc)),
|
||||
Some(None) => self.insert_cache(a, AccountEntry::Missing),
|
||||
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
|
||||
None => {
|
||||
let maybe_acc = if self.db.check_account_bloom(a) {
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(a) {
|
||||
Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)),
|
||||
Ok(None) => AccountEntry::Missing,
|
||||
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(acc))),
|
||||
Ok(None) => AccountEntry::new_clean(None),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
maybe_acc
|
||||
}
|
||||
else {
|
||||
AccountEntry::Missing
|
||||
AccountEntry::new_clean(None)
|
||||
};
|
||||
self.insert_cache(a, maybe_acc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.note_cache(a);
|
||||
}
|
||||
|
||||
match self.cache.borrow_mut().get_mut(a).unwrap() {
|
||||
&mut AccountEntry::Cached(ref mut acc) => not_default(acc),
|
||||
slot => *slot = AccountEntry::Cached(default()),
|
||||
}
|
||||
self.note_cache(a);
|
||||
|
||||
match &mut self.cache.borrow_mut().get_mut(a).unwrap().account {
|
||||
&mut Some(ref mut acc) => not_default(acc),
|
||||
slot => *slot = Some(default()),
|
||||
}
|
||||
|
||||
// at this point the account is guaranteed to be in the cache.
|
||||
RefMut::map(self.cache.borrow_mut(), |c| {
|
||||
match c.get_mut(a).unwrap() {
|
||||
&mut AccountEntry::Cached(ref mut account) => {
|
||||
let mut entry = c.get_mut(a).unwrap();
|
||||
// set the dirty flag after changing account data.
|
||||
entry.state = AccountState::Dirty;
|
||||
match entry.account {
|
||||
Some(ref mut account) => {
|
||||
if require_code {
|
||||
let addr_hash = account.address_hash(a);
|
||||
account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash));
|
||||
@@ -621,7 +706,7 @@ impl Clone for State {
|
||||
let cache = {
|
||||
let mut cache: HashMap<Address, AccountEntry> = HashMap::new();
|
||||
for (key, val) in self.cache.borrow().iter() {
|
||||
if let Some(entry) = val.clone_dirty() {
|
||||
if let Some(entry) = val.clone_if_dirty() {
|
||||
cache.insert(key.clone(), entry);
|
||||
}
|
||||
}
|
||||
@@ -1677,12 +1762,12 @@ fn snapshot_basic() {
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.clear_snapshot();
|
||||
state.discard_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(1u64));
|
||||
assert_eq!(state.balance(&a), U256::from(70u64));
|
||||
state.revert_snapshot();
|
||||
state.revert_to_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
}
|
||||
|
||||
@@ -1695,9 +1780,9 @@ fn snapshot_nested() {
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.clear_snapshot();
|
||||
state.discard_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.revert_snapshot();
|
||||
state.revert_to_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,22 +14,53 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{VecDeque, HashSet};
|
||||
use lru_cache::LruCache;
|
||||
use util::journaldb::JournalDB;
|
||||
use util::hash::{H256};
|
||||
use util::hashdb::HashDB;
|
||||
use util::{Arc, Address, DBTransaction, UtilError, Mutex, Hashable, BytesConvertable};
|
||||
use account::Account;
|
||||
use header::BlockNumber;
|
||||
use util::{Arc, Address, Database, DBTransaction, UtilError, Mutex, Hashable, BytesConvertable};
|
||||
use bloomfilter::{Bloom, BloomJournal};
|
||||
use util::Database;
|
||||
use client::DB_COL_ACCOUNT_BLOOM;
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
const STATE_CACHE_ITEMS: usize = 65536;
|
||||
const STATE_CACHE_ITEMS: usize = 256000;
|
||||
const STATE_CACHE_BLOCKS: usize = 8;
|
||||
|
||||
/// Shared canonical state cache.
|
||||
struct AccountCache {
|
||||
/// DB Account cache. `None` indicates that account is known to be missing.
|
||||
accounts: LruCache<Address, Option<Account>>,
|
||||
/// Accounts changed in recently committed blocks. Ordered by block number.
|
||||
modifications: VecDeque<BlockChanges>,
|
||||
}
|
||||
|
||||
/// Buffered account cache item.
|
||||
struct CacheQueueItem {
|
||||
/// Account address.
|
||||
address: Address,
|
||||
/// Acccount data or `None` if account does not exist.
|
||||
account: Option<Account>,
|
||||
/// Indicates that the account was modified before being
|
||||
/// added to the cache.
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Accumulates a list of accounts changed in a block.
|
||||
struct BlockChanges {
|
||||
/// Block number.
|
||||
number: BlockNumber,
|
||||
/// Block hash.
|
||||
hash: H256,
|
||||
/// Parent block hash.
|
||||
parent: H256,
|
||||
/// A set of modified account addresses.
|
||||
accounts: HashSet<Address>,
|
||||
/// Block is part of the canonical chain.
|
||||
is_canon: bool,
|
||||
}
|
||||
|
||||
/// State database abstraction.
|
||||
@@ -39,11 +70,21 @@ struct AccountCache {
|
||||
/// on commit.
|
||||
/// For non-canonical clones cache is cleared on commit.
|
||||
pub struct StateDB {
|
||||
/// Backing database.
|
||||
db: Box<JournalDB>,
|
||||
/// Shared canonical state cache.
|
||||
account_cache: Arc<Mutex<AccountCache>>,
|
||||
cache_overlay: Vec<(Address, Option<Account>)>,
|
||||
is_canon: bool,
|
||||
/// Local cache buffer.
|
||||
cache_buffer: Vec<CacheQueueItem>,
|
||||
/// Shared account bloom. Does not handle chain reorganizations.
|
||||
account_bloom: Arc<Mutex<Bloom>>,
|
||||
/// Hash of the block on top of which this instance was created or
|
||||
/// `None` if cache is disabled
|
||||
parent_hash: Option<H256>,
|
||||
/// Hash of the committing block or `None` if not committed yet.
|
||||
commit_hash: Option<H256>,
|
||||
/// Number of the committing block or `None` if not committed yet.
|
||||
commit_number: Option<BlockNumber>,
|
||||
}
|
||||
|
||||
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;
|
||||
@@ -52,6 +93,8 @@ pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;
|
||||
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
|
||||
|
||||
impl StateDB {
|
||||
/// Loads accounts bloom from the database
|
||||
/// This bloom is used to handle request for the non-existant account fast
|
||||
pub fn load_bloom(db: &Database) -> Bloom {
|
||||
let hash_count_entry = db.get(DB_COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
|
||||
.expect("Low-level database error");
|
||||
@@ -82,10 +125,15 @@ impl StateDB {
|
||||
let bloom = Self::load_bloom(db.backing());
|
||||
StateDB {
|
||||
db: db,
|
||||
account_cache: Arc::new(Mutex::new(AccountCache { accounts: LruCache::new(STATE_CACHE_ITEMS) })),
|
||||
cache_overlay: Vec::new(),
|
||||
is_canon: false,
|
||||
account_cache: Arc::new(Mutex::new(AccountCache {
|
||||
accounts: LruCache::new(STATE_CACHE_ITEMS),
|
||||
modifications: VecDeque::new(),
|
||||
})),
|
||||
cache_buffer: Vec::new(),
|
||||
account_bloom: Arc::new(Mutex::new(bloom)),
|
||||
parent_hash: None,
|
||||
commit_hash: None,
|
||||
commit_number: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,14 +172,106 @@ impl StateDB {
|
||||
}
|
||||
|
||||
let records = try!(self.db.commit(batch, now, id, end));
|
||||
if self.is_canon {
|
||||
self.commit_cache();
|
||||
} else {
|
||||
self.clear_cache();
|
||||
}
|
||||
self.commit_hash = Some(id.clone());
|
||||
self.commit_number = Some(now);
|
||||
Ok(records)
|
||||
}
|
||||
|
||||
/// Apply buffered cache changes and synchronize canonical
|
||||
/// state cache with the best block state.
|
||||
/// This function updates the cache by removing entries that are
|
||||
/// invalidated by chain reorganization. `update_cache` should be
|
||||
/// called after the block has been commited and the blockchain
|
||||
/// route has ben calculated.
|
||||
pub fn sync_cache(&mut self, enacted: &[H256], retracted: &[H256], is_best: bool) {
|
||||
trace!("sync_cache id = (#{:?}, {:?}), parent={:?}, best={}", self.commit_number, self.commit_hash, self.parent_hash, is_best);
|
||||
let mut cache = self.account_cache.lock();
|
||||
let mut cache = &mut *cache;
|
||||
|
||||
// Clean changes from re-enacted and retracted blocks
|
||||
let mut clear = false;
|
||||
for block in enacted.iter().filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) {
|
||||
clear = clear || {
|
||||
if let Some(ref mut m) = cache.modifications.iter_mut().find(|ref m| &m.hash == block) {
|
||||
trace!("Reverting enacted block {:?}", block);
|
||||
m.is_canon = true;
|
||||
for a in &m.accounts {
|
||||
trace!("Reverting enacted address {:?}", a);
|
||||
cache.accounts.remove(a);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for block in retracted {
|
||||
clear = clear || {
|
||||
if let Some(ref mut m) = cache.modifications.iter_mut().find(|ref m| &m.hash == block) {
|
||||
trace!("Retracting block {:?}", block);
|
||||
m.is_canon = false;
|
||||
for a in &m.accounts {
|
||||
trace!("Retracted address {:?}", a);
|
||||
cache.accounts.remove(a);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
if clear {
|
||||
// We don't know anything about the block; clear everything
|
||||
trace!("Wiping cache");
|
||||
cache.accounts.clear();
|
||||
cache.modifications.clear();
|
||||
}
|
||||
|
||||
// Apply cache changes only if committing on top of the latest canonical state
|
||||
// blocks are ordered by number and only one block with a given number is marked as canonical
|
||||
// (contributed to canonical state cache)
|
||||
if let (Some(ref number), Some(ref hash), Some(ref parent)) = (self.commit_number, self.commit_hash, self.parent_hash) {
|
||||
if cache.modifications.len() == STATE_CACHE_BLOCKS {
|
||||
cache.modifications.pop_back();
|
||||
}
|
||||
let mut modifications = HashSet::new();
|
||||
trace!("committing {} cache entries", self.cache_buffer.len());
|
||||
for account in self.cache_buffer.drain(..) {
|
||||
if account.modified {
|
||||
modifications.insert(account.address.clone());
|
||||
}
|
||||
if is_best {
|
||||
if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&account.address) {
|
||||
if let Some(new) = account.account {
|
||||
if account.modified {
|
||||
existing.overwrite_with(new);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cache.accounts.insert(account.address, account.account);
|
||||
}
|
||||
}
|
||||
|
||||
// Save modified accounts. These are ordered by the block number.
|
||||
let block_changes = BlockChanges {
|
||||
accounts: modifications,
|
||||
number: *number,
|
||||
hash: hash.clone(),
|
||||
is_canon: is_best,
|
||||
parent: parent.clone(),
|
||||
};
|
||||
let insert_at = cache.modifications.iter().enumerate().find(|&(_, ref m)| m.number < *number).map(|(i, _)| i);
|
||||
trace!("inserting modifications at {:?}", insert_at);
|
||||
if let Some(insert_at) = insert_at {
|
||||
cache.modifications.insert(insert_at, block_changes);
|
||||
} else {
|
||||
cache.modifications.push_back(block_changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an interface to HashDB.
|
||||
pub fn as_hashdb(&self) -> &HashDB {
|
||||
self.db.as_hashdb()
|
||||
@@ -147,20 +287,24 @@ impl StateDB {
|
||||
StateDB {
|
||||
db: self.db.boxed_clone(),
|
||||
account_cache: self.account_cache.clone(),
|
||||
cache_overlay: Vec::new(),
|
||||
is_canon: false,
|
||||
cache_buffer: Vec::new(),
|
||||
account_bloom: self.account_bloom.clone(),
|
||||
parent_hash: None,
|
||||
commit_hash: None,
|
||||
commit_number: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the database for a canonical state.
|
||||
pub fn boxed_clone_canon(&self) -> StateDB {
|
||||
pub fn boxed_clone_canon(&self, parent: &H256) -> StateDB {
|
||||
StateDB {
|
||||
db: self.db.boxed_clone(),
|
||||
account_cache: self.account_cache.clone(),
|
||||
cache_overlay: Vec::new(),
|
||||
is_canon: true,
|
||||
cache_buffer: Vec::new(),
|
||||
account_bloom: self.account_bloom.clone(),
|
||||
parent_hash: Some(parent.clone()),
|
||||
commit_hash: None,
|
||||
commit_number: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,53 +323,147 @@ impl StateDB {
|
||||
&*self.db
|
||||
}
|
||||
|
||||
/// Enqueue cache change.
|
||||
pub fn cache_account(&mut self, addr: Address, data: Option<Account>) {
|
||||
self.cache_overlay.push((addr, data));
|
||||
}
|
||||
|
||||
/// Apply pending cache changes.
|
||||
fn commit_cache(&mut self) {
|
||||
let mut cache = self.account_cache.lock();
|
||||
for (address, account) in self.cache_overlay.drain(..) {
|
||||
if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&address) {
|
||||
if let Some(new) = account {
|
||||
existing.merge_with(new);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cache.accounts.insert(address, account);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the cache.
|
||||
pub fn clear_cache(&mut self) {
|
||||
self.cache_overlay.clear();
|
||||
let mut cache = self.account_cache.lock();
|
||||
cache.accounts.clear();
|
||||
/// Add pending cache change.
|
||||
/// The change is queued to be applied in `commit`.
|
||||
pub fn add_to_account_cache(&mut self, addr: Address, data: Option<Account>, modified: bool) {
|
||||
self.cache_buffer.push(CacheQueueItem {
|
||||
address: addr,
|
||||
account: data,
|
||||
modified: modified,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get basic copy of the cached account. Does not include storage.
|
||||
/// Returns 'None' if the state is non-canonical and cache is disabled
|
||||
/// or if the account is not cached.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
pub fn get_cached_account(&self, addr: &Address) -> Option<Option<Account>> {
|
||||
if !self.is_canon {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(addr, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
let mut cache = self.account_cache.lock();
|
||||
cache.accounts.get_mut(&addr).map(|a| a.as_ref().map(|a| a.clone_basic()))
|
||||
}
|
||||
|
||||
/// Get value from a cached account.
|
||||
/// Returns 'None' if the state is non-canonical and cache is disabled
|
||||
/// or if the account is not cached.
|
||||
/// Returns 'None' if cache is disabled or if the account is not cached.
|
||||
pub fn get_cached<F, U>(&self, a: &Address, f: F) -> Option<U>
|
||||
where F: FnOnce(Option<&mut Account>) -> U {
|
||||
if !self.is_canon {
|
||||
let mut cache = self.account_cache.lock();
|
||||
if !Self::is_allowed(a, &self.parent_hash, &cache.modifications) {
|
||||
return None;
|
||||
}
|
||||
let mut cache = self.account_cache.lock();
|
||||
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
||||
}
|
||||
|
||||
/// Check if the account can be returned from cache by matching current block parent hash against canonical
|
||||
/// state and filtering out account modified in later blocks.
|
||||
fn is_allowed(addr: &Address, parent_hash: &Option<H256>, modifications: &VecDeque<BlockChanges>) -> bool {
|
||||
let mut parent = match *parent_hash {
|
||||
None => {
|
||||
trace!("Cache lookup skipped for {:?}: no parent hash", addr);
|
||||
return false;
|
||||
}
|
||||
Some(ref parent) => parent,
|
||||
};
|
||||
if modifications.is_empty() {
|
||||
return true;
|
||||
}
|
||||
// Ignore all accounts modified in later blocks
|
||||
// Modifications contains block ordered by the number
|
||||
// We search for our parent in that list first and then for
|
||||
// all its parent until we hit the canonical block,
|
||||
// checking against all the intermediate modifications.
|
||||
let mut iter = modifications.iter();
|
||||
while let Some(ref m) = iter.next() {
|
||||
if &m.hash == parent {
|
||||
if m.is_canon {
|
||||
return true;
|
||||
}
|
||||
parent = &m.parent;
|
||||
}
|
||||
if m.accounts.contains(addr) {
|
||||
trace!("Cache lookup skipped for {:?}: modified in a later block", addr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
trace!("Cache lookup skipped for {:?}: parent hash is unknown", addr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use util::{U256, H256, FixedHash, Address, DBTransaction};
|
||||
use tests::helpers::*;
|
||||
use account::Account;
|
||||
use util::log::init_log;
|
||||
|
||||
#[test]
|
||||
fn state_db_smoke() {
|
||||
init_log();
|
||||
|
||||
let mut state_db_result = get_temp_state_db();
|
||||
let state_db = state_db_result.take();
|
||||
let root_parent = H256::random();
|
||||
let address = Address::random();
|
||||
let h0 = H256::random();
|
||||
let h1a = H256::random();
|
||||
let h1b = H256::random();
|
||||
let h2a = H256::random();
|
||||
let h2b = H256::random();
|
||||
let h3a = H256::random();
|
||||
let h3b = H256::random();
|
||||
let mut batch = DBTransaction::new(state_db.journal_db().backing());
|
||||
|
||||
// blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ]
|
||||
// balance [ 5 5 4 3 2 2 ]
|
||||
let mut s = state_db.boxed_clone_canon(&root_parent);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false);
|
||||
s.commit(&mut batch, 0, &h0, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h0);
|
||||
s.commit(&mut batch, 1, &h1a, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h0);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(3.into(), 0.into())), true);
|
||||
s.commit(&mut batch, 1, &h1b, None).unwrap();
|
||||
s.sync_cache(&[], &[], false);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h1b);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(4.into(), 0.into())), true);
|
||||
s.commit(&mut batch, 2, &h2b, None).unwrap();
|
||||
s.sync_cache(&[], &[], false);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h1a);
|
||||
s.add_to_account_cache(address, Some(Account::new_basic(5.into(), 0.into())), true);
|
||||
s.commit(&mut batch, 2, &h2a, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let mut s = state_db.boxed_clone_canon(&h2a);
|
||||
s.commit(&mut batch, 3, &h3a, None).unwrap();
|
||||
s.sync_cache(&[], &[], true);
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h3a);
|
||||
assert_eq!(s.get_cached_account(&address).unwrap().unwrap().balance(), &U256::from(5));
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h1a);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h2b);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
|
||||
let s = state_db.boxed_clone_canon(&h1b);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
|
||||
// reorg to 3b
|
||||
// blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ]
|
||||
let mut s = state_db.boxed_clone_canon(&h2b);
|
||||
s.commit(&mut batch, 3, &h3b, None).unwrap();
|
||||
s.sync_cache(&[h1b.clone(), h2b.clone(), h3b.clone()], &[h1a.clone(), h2a.clone(), h3a.clone()], true);
|
||||
let s = state_db.boxed_clone_canon(&h3a);
|
||||
assert!(s.get_cached_account(&address).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Blockchain Filter.
|
||||
#[derive(Binary)]
|
||||
#[derive(Binary, Debug, PartialEq)]
|
||||
pub struct Filter {
|
||||
/// Blockchain will be searched from this block.
|
||||
pub from_block: BlockID,
|
||||
|
||||
@@ -23,7 +23,7 @@ use account::{Version, Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Crypto {
|
||||
pub cipher: Cipher,
|
||||
pub ciphertext: [u8; 32],
|
||||
pub ciphertext: Vec<u8>,
|
||||
pub kdf: Kdf,
|
||||
pub mac: [u8; 32],
|
||||
}
|
||||
@@ -95,7 +95,7 @@ impl Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: iv,
|
||||
}),
|
||||
ciphertext: ciphertext,
|
||||
ciphertext: ciphertext.to_vec(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
dklen: crypto::KEY_LENGTH as u32,
|
||||
salt: salt,
|
||||
@@ -107,6 +107,10 @@ impl Crypto {
|
||||
}
|
||||
|
||||
pub fn secret(&self, password: &str) -> Result<Secret, Error> {
|
||||
if self.ciphertext.len() > 32 {
|
||||
return Err(Error::InvalidSecret);
|
||||
}
|
||||
|
||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c),
|
||||
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r),
|
||||
@@ -122,7 +126,8 @@ impl Crypto {
|
||||
|
||||
match self.cipher {
|
||||
Cipher::Aes128Ctr(ref params) => {
|
||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut *secret)
|
||||
let from = 32 - self.ciphertext.len();
|
||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut (&mut *secret)[from..])
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -76,15 +76,14 @@ impl DiskDirectory {
|
||||
.map(|entry| entry.path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
let files: Result<Vec<_>, _> = paths.iter()
|
||||
.map(fs::File::open)
|
||||
.collect();
|
||||
|
||||
let files = try!(files);
|
||||
|
||||
files.into_iter()
|
||||
.map(json::KeyFile::load)
|
||||
.zip(paths.into_iter())
|
||||
paths
|
||||
.iter()
|
||||
.map(|p| (
|
||||
fs::File::open(p)
|
||||
.map_err(Error::from)
|
||||
.and_then(|r| json::KeyFile::load(r).map_err(|e| Error::Custom(format!("{:?}", e)))),
|
||||
p
|
||||
))
|
||||
.map(|(file, path)| match file {
|
||||
Ok(file) => Ok((path.clone(), SafeAccount::from_file(
|
||||
file, path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned()
|
||||
|
||||
58
ethstore/src/json/bytes.rs
Normal file
58
ethstore/src/json/bytes.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::{ops, str};
|
||||
use serde::{Deserialize, Deserializer, Error, Serialize, Serializer};
|
||||
use rustc_serialize::hex::{ToHex, FromHex, FromHexError};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Bytes(Vec<u8>);
|
||||
|
||||
impl ops::Deref for Bytes {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Bytes {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||
where D: Deserializer
|
||||
{
|
||||
let s = try!(String::deserialize(deserializer));
|
||||
let data = try!(s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e))));
|
||||
Ok(Bytes(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Bytes {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
serializer.serialize_str(&self.0.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Bytes {
|
||||
type Err = FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.from_hex().map(Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Bytes {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
Bytes(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bytes> for Vec<u8> {
|
||||
fn from(b: Bytes) -> Self {
|
||||
b.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, Error};
|
||||
use serde::de::{Visitor, MapVisitor};
|
||||
use serde::ser;
|
||||
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256};
|
||||
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes};
|
||||
|
||||
pub type CipherText = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Crypto {
|
||||
pub cipher: Cipher,
|
||||
pub ciphertext: H256,
|
||||
pub ciphertext: CipherText,
|
||||
pub kdf: Kdf,
|
||||
pub mac: H256,
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
use std::str::FromStr;
|
||||
use std::{ops, fmt, str};
|
||||
use rustc_serialize::hex::{FromHex, ToHex};
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError};
|
||||
use serde::de::Visitor;
|
||||
@@ -65,7 +63,7 @@ macro_rules! impl_hash {
|
||||
type Value = $name;
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
FromStr::from_str(value).map_err(SerdeError::custom)
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
@@ -77,7 +75,7 @@ macro_rules! impl_hash {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for $name {
|
||||
impl str::FromStr for $name {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
@@ -92,6 +90,12 @@ macro_rules! impl_hash {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for $name {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; $size]> for $name {
|
||||
fn from(bytes: [u8; $size]) -> Self {
|
||||
$name(bytes)
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Universaly unique identifier.
|
||||
use std::str::FromStr;
|
||||
use std::fmt;
|
||||
use std::{fmt, str};
|
||||
use rustc_serialize::hex::{ToHex, FromHex};
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer, Error as SerdeError};
|
||||
use serde::de::Visitor;
|
||||
@@ -73,7 +72,7 @@ fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl FromStr for UUID {
|
||||
impl str::FromStr for UUID {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
@@ -95,6 +94,12 @@ impl FromStr for UUID {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for UUID {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UUID {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer {
|
||||
@@ -116,7 +121,7 @@ impl Visitor for UUIDVisitor {
|
||||
type Value = UUID;
|
||||
|
||||
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
UUID::from_str(value).map_err(SerdeError::custom)
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
@@ -126,19 +131,18 @@ impl Visitor for UUIDVisitor {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use super::UUID;
|
||||
|
||||
#[test]
|
||||
fn uuid_from_str() {
|
||||
let uuid = UUID::from_str("3198bc9c-6672-5ab3-d995-4942343ae5b6").unwrap();
|
||||
let uuid: UUID = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into();
|
||||
assert_eq!(uuid, UUID::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uuid_from_and_to_str() {
|
||||
let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6";
|
||||
let uuid = UUID::from_str(from).unwrap();
|
||||
let uuid: UUID = from.into();
|
||||
let to: String = uuid.into();
|
||||
assert_eq!(from, &to);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ impl Visitor for KeyFileVisitor {
|
||||
Some(KeyFileField::Version) => { version = Some(try!(visitor.visit_value())); }
|
||||
Some(KeyFileField::Crypto) => { crypto = Some(try!(visitor.visit_value())); }
|
||||
Some(KeyFileField::Address) => { address = Some(try!(visitor.visit_value())); }
|
||||
Some(KeyFileField::Name) => { name = visitor.visit_value().ok(); } // ignore anyhing that is not a string to be permissive.
|
||||
Some(KeyFileField::Name) => { name = visitor.visit_value().ok(); } // ignore anyhing that is not a string to be permissive.
|
||||
Some(KeyFileField::Meta) => { meta = visitor.visit_value().ok(); } // ignore anyhing that is not a string to be permissive.
|
||||
None => { break; }
|
||||
}
|
||||
@@ -153,7 +153,7 @@ impl KeyFile {
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use json::{KeyFile, UUID, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt, H128, H160, H256};
|
||||
use json::{KeyFile, UUID, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt};
|
||||
|
||||
#[test]
|
||||
fn basic_keyfile() {
|
||||
@@ -185,20 +185,20 @@ mod tests {
|
||||
let expected = KeyFile {
|
||||
id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
version: Version::V3,
|
||||
address: H160::from_str("6edddfc6349aff20bc6467ccf276c5b52487f7a8").unwrap(),
|
||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: H128::from_str("b5a7ec855ec9e2c405371356855fec83").unwrap(),
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: H256::from_str("7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc").unwrap(),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: H256::from_str("1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209").unwrap(),
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: H256::from_str("46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f").unwrap(),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: Some("{}".to_owned()),
|
||||
@@ -234,22 +234,22 @@ mod tests {
|
||||
}"#;
|
||||
|
||||
let expected = KeyFile {
|
||||
id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: H160::from_str("6edddfc6349aff20bc6467ccf276c5b52487f7a8").unwrap(),
|
||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: H128::from_str("b5a7ec855ec9e2c405371356855fec83").unwrap(),
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: H256::from_str("7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc").unwrap(),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: H256::from_str("1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209").unwrap(),
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: H256::from_str("46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f").unwrap(),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: None,
|
||||
meta: None,
|
||||
@@ -262,22 +262,22 @@ mod tests {
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = KeyFile {
|
||||
id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: H160::from_str("6edddfc6349aff20bc6467ccf276c5b52487f7a8").unwrap(),
|
||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: H128::from_str("b5a7ec855ec9e2c405371356855fec83").unwrap(),
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: H256::from_str("7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc").unwrap(),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: H256::from_str("1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209").unwrap(),
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: H256::from_str("46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f").unwrap(),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: None,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod bytes;
|
||||
mod cipher;
|
||||
mod crypto;
|
||||
mod error;
|
||||
@@ -8,8 +9,9 @@ mod key_file;
|
||||
mod presale;
|
||||
mod version;
|
||||
|
||||
pub use self::bytes::Bytes;
|
||||
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
|
||||
pub use self::crypto::Crypto;
|
||||
pub use self::crypto::{Crypto, CipherText};
|
||||
pub use self::error::Error;
|
||||
pub use self::hash::{H128, H160, H256};
|
||||
pub use self::id::UUID;
|
||||
|
||||
@@ -1,30 +1,8 @@
|
||||
use std::io::Read;
|
||||
use std::ops::Deref;
|
||||
use serde_json;
|
||||
use serde::{Deserialize, Deserializer, Error};
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use super::{H160};
|
||||
use super::{H160, Bytes};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Encseed(Vec<u8>);
|
||||
|
||||
impl Deref for Encseed {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Encseed {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||
where D: Deserializer
|
||||
{
|
||||
let s = try!(String::deserialize(deserializer));
|
||||
let data = try!(s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e))));
|
||||
Ok(Encseed(data))
|
||||
}
|
||||
}
|
||||
pub type Encseed = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct PresaleWallet {
|
||||
@@ -43,8 +21,7 @@ impl PresaleWallet {
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use json::{PresaleWallet, H160, Encseed};
|
||||
use json::{PresaleWallet, H160};
|
||||
|
||||
#[test]
|
||||
fn presale_wallet() {
|
||||
@@ -57,7 +34,7 @@ mod tests {
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: Encseed("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".from_hex().unwrap()),
|
||||
encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".into(),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
@@ -77,7 +54,7 @@ mod tests {
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: Encseed("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".from_hex().unwrap()),
|
||||
encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".into(),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
|
||||
@@ -19,9 +19,8 @@ extern crate ethstore;
|
||||
|
||||
mod util;
|
||||
|
||||
use std::str::FromStr;
|
||||
use ethstore::{SecretStore, EthStore};
|
||||
use ethstore::ethkey::{Random, Generator, Secret, Address};
|
||||
use ethstore::ethkey::{Random, Generator, Secret, KeyPair, verify_address};
|
||||
use ethstore::dir::DiskDirectory;
|
||||
use util::TransientDir;
|
||||
|
||||
@@ -103,14 +102,21 @@ fn pat_path() -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/ciphertext",
|
||||
Err(_) => "tests/res/ciphertext",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_laod_geth_files() {
|
||||
let dir = DiskDirectory::at(test_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap(),
|
||||
Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap(),
|
||||
Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap(),
|
||||
"3f49624084b67849c7b4e805c5988c21a430f9d9".parse().unwrap(),
|
||||
"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".parse().unwrap(),
|
||||
"63121b431a52f8043c16fcf0d1df9cb7b5f66649".parse().unwrap(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -119,9 +125,30 @@ fn secret_store_load_pat_files() {
|
||||
let dir = DiskDirectory::at(pat_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap(),
|
||||
Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap(),
|
||||
"3f49624084b67849c7b4e805c5988c21a430f9d9".parse().unwrap(),
|
||||
"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".parse().unwrap(),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypting_files_with_short_ciphertext() {
|
||||
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
|
||||
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap();
|
||||
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
|
||||
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap();
|
||||
let dir = DiskDirectory::at(ciphertext_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts, vec![
|
||||
"31e9d1e6d844bd3a536800ef8d8be6a9975db509".parse().unwrap(),
|
||||
"d1e64e5480bfaf733ba7d48712decb8227797a4e".parse().unwrap(),
|
||||
]);
|
||||
|
||||
let message = Default::default();
|
||||
|
||||
let s1 = store.sign(&accounts[0], "foo", &message).unwrap();
|
||||
let s2 = store.sign(&accounts[1], "foo", &message).unwrap();
|
||||
assert!(verify_address(&accounts[0], &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
|
||||
}
|
||||
|
||||
21
ethstore/tests/res/ciphertext/30.json
Normal file
21
ethstore/tests/res/ciphertext/30.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"address" : "31e9d1e6d844bd3a536800ef8d8be6a9975db509",
|
||||
"crypto" : {
|
||||
"cipher" : "aes-128-ctr",
|
||||
"cipherparams" : {
|
||||
"iv" : "3ca92af36ad7c2cd92454c59cea5ef00"
|
||||
},
|
||||
"ciphertext" : "108b7d34f3442fc26ab1ab90ca91476ba6bfa8c00975a49ef9051dc675aa",
|
||||
"kdf" : "scrypt",
|
||||
"kdfparams" : {
|
||||
"dklen" : 32,
|
||||
"n" : 2,
|
||||
"r" : 8,
|
||||
"p" : 1,
|
||||
"salt" : "d0769e608fb86cda848065642a9c6fa046845c928175662b8e356c77f914cd3b"
|
||||
},
|
||||
"mac" : "75d0e6759f7b3cefa319c3be41680ab6beea7d8328653474bd06706d4cc67420"
|
||||
},
|
||||
"id" : "a37e1559-5955-450d-8075-7b8931b392b2",
|
||||
"version" : 3
|
||||
}
|
||||
21
ethstore/tests/res/ciphertext/31.json
Normal file
21
ethstore/tests/res/ciphertext/31.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"address" : "d1e64e5480bfaf733ba7d48712decb8227797a4e",
|
||||
"crypto" : {
|
||||
"cipher" : "aes-128-ctr",
|
||||
"cipherparams" : {
|
||||
"iv" : "e0c41130a323adc1446fc82f724bca2f"
|
||||
},
|
||||
"ciphertext" : "9517cd5bdbe69076f9bf5057248c6c050141e970efa36ce53692d5d59a3984",
|
||||
"kdf" : "scrypt",
|
||||
"kdfparams" : {
|
||||
"dklen" : 32,
|
||||
"n" : 2,
|
||||
"r" : 8,
|
||||
"p" : 1,
|
||||
"salt" : "711f816911c92d649fb4c84b047915679933555030b3552c1212609b38208c63"
|
||||
},
|
||||
"mac" : "d5e116151c6aa71470e67a7d42c9620c75c4d23229847dcc127794f0732b0db5"
|
||||
},
|
||||
"id" : "fecfc4ce-e956-48fd-953b-30f8b52ed66c",
|
||||
"version" : 3
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||
!define VERSIONMAJOR 1
|
||||
!define VERSIONMINOR 3
|
||||
!define VERSIONBUILD 3
|
||||
!define VERSIONBUILD 4
|
||||
|
||||
!addplugindir .\
|
||||
|
||||
|
||||
@@ -193,7 +193,13 @@ Sealing/Mining Options:
|
||||
--extra-data STRING Specify a custom extra-data for authored blocks, no
|
||||
more than 32 characters.
|
||||
--tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting
|
||||
to be included in next block) [default: 1024].
|
||||
to be included in next block) [default: 2048].
|
||||
--tx-queue-strategy S Prioritization strategy used to order transactions
|
||||
in the queue. S may be:
|
||||
gas - Prioritize txs with low gas limit;
|
||||
gas_price - Prioritize txs with high gas price;
|
||||
gas_factor - Prioritize txs using gas price
|
||||
and gas limit ratio [default: gas_factor].
|
||||
--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
|
||||
@@ -372,12 +378,13 @@ pub struct Args {
|
||||
pub flag_gas_cap: String,
|
||||
pub flag_extra_data: Option<String>,
|
||||
pub flag_tx_queue_size: usize,
|
||||
pub flag_tx_queue_strategy: String,
|
||||
pub flag_notify_work: Option<String>,
|
||||
pub flag_logging: Option<String>,
|
||||
pub flag_version: bool,
|
||||
pub flag_from: String,
|
||||
pub flag_to: String,
|
||||
pub flag_at: String,
|
||||
pub flag_at: String,
|
||||
pub flag_format: Option<String>,
|
||||
pub flag_jitvm: bool,
|
||||
pub flag_log_file: Option<String>,
|
||||
|
||||
@@ -25,7 +25,7 @@ use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address};
|
||||
use util::log::Colour;
|
||||
use ethsync::{NetworkConfiguration, is_valid_node_url};
|
||||
use ethcore::client::{VMType, Mode};
|
||||
use ethcore::miner::MinerOptions;
|
||||
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
|
||||
|
||||
use rpc::{IpcConfiguration, HttpConfiguration};
|
||||
use ethcore_rpc::NetworkSettings;
|
||||
@@ -319,6 +319,15 @@ impl Configuration {
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
fn transaction_queue_strategy(&self) -> Result<PrioritizationStrategy, String> {
|
||||
match self.args.flag_tx_queue_strategy.as_str() {
|
||||
"gas" => Ok(PrioritizationStrategy::GasAndGasPrice),
|
||||
"gas_price" => Ok(PrioritizationStrategy::GasPriceOnly),
|
||||
"gas_factor" => Ok(PrioritizationStrategy::GasFactorAndGasPrice),
|
||||
_ => Err(format!("Invalid queue strategy: {}", self.args.flag_tx_queue_strategy)),
|
||||
}
|
||||
}
|
||||
|
||||
fn miner_options(&self) -> Result<MinerOptions, String> {
|
||||
let reseal = try!(self.args.flag_reseal_on_txs.parse::<ResealPolicy>());
|
||||
|
||||
@@ -332,6 +341,7 @@ impl Configuration {
|
||||
None => U256::max_value(),
|
||||
},
|
||||
tx_queue_size: self.args.flag_tx_queue_size,
|
||||
tx_queue_strategy: try!(self.transaction_queue_strategy()),
|
||||
pending_set: try!(to_pending_set(&self.args.flag_relay_set)),
|
||||
reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period),
|
||||
work_queue_size: self.args.flag_work_queue_size,
|
||||
@@ -604,6 +614,7 @@ mod tests {
|
||||
use docopt::Docopt;
|
||||
use ethcore_rpc::NetworkSettings;
|
||||
use ethcore::client::{VMType, BlockID};
|
||||
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
|
||||
use helpers::{replace_home, default_network_config};
|
||||
use run::RunCmd;
|
||||
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat};
|
||||
@@ -777,6 +788,27 @@ mod tests {
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_mining_options() {
|
||||
// given
|
||||
let mut mining_options = MinerOptions::default();
|
||||
|
||||
// when
|
||||
let conf0 = parse(&["parity"]);
|
||||
let conf1 = parse(&["parity", "--tx-queue-strategy", "gas_factor"]);
|
||||
let conf2 = parse(&["parity", "--tx-queue-strategy", "gas_price"]);
|
||||
let conf3 = parse(&["parity", "--tx-queue-strategy", "gas"]);
|
||||
|
||||
// then
|
||||
assert_eq!(conf0.miner_options().unwrap(), mining_options);
|
||||
mining_options.tx_queue_strategy = PrioritizationStrategy::GasFactorAndGasPrice;
|
||||
assert_eq!(conf1.miner_options().unwrap(), mining_options);
|
||||
mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly;
|
||||
assert_eq!(conf2.miner_options().unwrap(), mining_options);
|
||||
mining_options.tx_queue_strategy = PrioritizationStrategy::GasAndGasPrice;
|
||||
assert_eq!(conf3.miner_options().unwrap(), mining_options);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_network_settings() {
|
||||
// given
|
||||
|
||||
@@ -270,10 +270,13 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
||||
println!("Migration finished");
|
||||
}
|
||||
|
||||
// update version file.
|
||||
try!(update_version(path));
|
||||
|
||||
// run any inplace migrations for the fully upgraded database
|
||||
try!(run_inplace_upgrades(consolidated_database_path(path).as_path()));
|
||||
|
||||
// update version file.
|
||||
update_version(path)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Old migrations utilities
|
||||
|
||||
@@ -215,7 +215,7 @@ impl Default for MinerExtras {
|
||||
extra_data: version_data(),
|
||||
gas_floor_target: U256::from(4_700_000),
|
||||
gas_ceil_target: U256::from(6_283_184),
|
||||
transactions_limit: 1024,
|
||||
transactions_limit: 2048,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ rustc-serialize = "0.3"
|
||||
transient-hashmap = "0.1"
|
||||
serde_macros = { version = "0.7.0", optional = true }
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git", branch = "beta" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -32,7 +32,7 @@ use util::rlp::{encode, decode, UntrustedRlp, View};
|
||||
use util::{FromHex, Mutex};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
|
||||
use ethcore::header::Header as BlockHeader;
|
||||
use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber};
|
||||
use ethcore::block::IsBlock;
|
||||
use ethcore::views::*;
|
||||
use ethcore::ethereum::Ethash;
|
||||
@@ -193,8 +193,8 @@ impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||
let receipts = miner.pending_receipts();
|
||||
pub fn pending_logs<M>(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||
let receipts = miner.pending_receipts(best_block);
|
||||
|
||||
let pending_logs = receipts.into_iter()
|
||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
||||
@@ -437,7 +437,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
let miner = take_weak!(self.miner);
|
||||
let client = take_weak!(self.client);
|
||||
let maybe_tx = client.transaction(TransactionID::Hash(hash)).map(Transaction::from)
|
||||
.or_else(|| miner.transaction(&hash).map(Transaction::from));
|
||||
.or_else(|| miner.transaction(client.chain_info().best_block_number, &hash).map(Transaction::from));
|
||||
match maybe_tx {
|
||||
Some(t) => to_value(&t),
|
||||
None => Ok(Value::Null),
|
||||
@@ -462,8 +462,9 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
from_params::<(RpcH256,)>(params)
|
||||
.and_then(|(hash,)| {
|
||||
let miner = take_weak!(self.miner);
|
||||
let best_block = take_weak!(self.client).chain_info().best_block_number;
|
||||
let hash: H256 = hash.into();
|
||||
match (miner.pending_receipt(&hash), self.options.allow_pending_receipt_query) {
|
||||
match (miner.pending_receipt(best_block, &hash), self.options.allow_pending_receipt_query) {
|
||||
(Some(receipt), true) => to_value(&Receipt::from(receipt)),
|
||||
_ => {
|
||||
let client = take_weak!(self.client);
|
||||
@@ -509,7 +510,8 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
if include_pending {
|
||||
let pending = pending_logs(&*take_weak!(self.miner), &filter);
|
||||
let best_block = take_weak!(self.client).chain_info().best_block_number;
|
||||
let pending = pending_logs(&*take_weak!(self.miner), best_block, &filter);
|
||||
logs.extend(pending);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
try!(expect_no_params(params));
|
||||
|
||||
let mut polls = self.polls.lock();
|
||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||
let best_block = take_weak!(self.client).chain_info().best_block_number;
|
||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(best_block);
|
||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||
|
||||
to_value(&RpcU256::from(id))
|
||||
@@ -118,7 +119,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
},
|
||||
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||
// get hashes of pending transactions
|
||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||
let current_number = client.chain_info().best_block_number;
|
||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes(current_number);
|
||||
|
||||
let new_hashes =
|
||||
{
|
||||
@@ -159,7 +161,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
|
||||
// additionally retrieve pending logs
|
||||
if include_pending {
|
||||
let pending_logs = pending_logs(&*take_weak!(self.miner), &filter);
|
||||
let pending_logs = pending_logs(&*take_weak!(self.miner), current_number, &filter);
|
||||
|
||||
// remove logs about which client was already notified about
|
||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||
@@ -177,7 +179,6 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
// save the number of the next block as a first block from which
|
||||
// we want to get logs
|
||||
*block_number = current_number + 1;
|
||||
|
||||
to_value(&logs)
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
if include_pending {
|
||||
logs.extend(pending_logs(&*take_weak!(self.miner), &filter));
|
||||
let best_block = take_weak!(self.client).chain_info().best_block_number;
|
||||
logs.extend(pending_logs(&*take_weak!(self.miner), best_block, &filter));
|
||||
}
|
||||
|
||||
to_value(&logs)
|
||||
|
||||
@@ -25,7 +25,7 @@ use ethcore::spec::{Genesis, Spec};
|
||||
use ethcore::block::Block;
|
||||
use ethcore::views::BlockView;
|
||||
use ethcore::ethereum;
|
||||
use ethcore::miner::{MinerOptions, GasPricer, MinerService, ExternalMiner, Miner, PendingSet};
|
||||
use ethcore::miner::{MinerOptions, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, PrioritizationStrategy};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use devtools::RandomTempPath;
|
||||
use util::Hashable;
|
||||
@@ -59,6 +59,7 @@ fn miner_service(spec: &Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
||||
reseal_on_own_tx: true,
|
||||
tx_queue_size: 1024,
|
||||
tx_gas_limit: !U256::zero(),
|
||||
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
|
||||
pending_set: PendingSet::SealingOrElseQueue,
|
||||
reseal_min_period: Duration::from_secs(0),
|
||||
work_queue_size: 50,
|
||||
|
||||
@@ -21,6 +21,7 @@ use util::standard::*;
|
||||
use ethcore::error::{Error, CallError};
|
||||
use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
|
||||
use ethcore::block::{ClosedBlock, IsBlock};
|
||||
use ethcore::header::BlockNumber;
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use ethcore::receipt::{Receipt, RichReceipt};
|
||||
use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult};
|
||||
@@ -162,7 +163,7 @@ impl MinerService for TestMinerService {
|
||||
}
|
||||
|
||||
/// Returns hashes of transactions currently in pending
|
||||
fn pending_transactions_hashes(&self) -> Vec<H256> {
|
||||
fn pending_transactions_hashes(&self, _best_block: BlockNumber) -> Vec<H256> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
@@ -186,7 +187,7 @@ impl MinerService for TestMinerService {
|
||||
Some(f(&open_block.close()))
|
||||
}
|
||||
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||
fn transaction(&self, _best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction> {
|
||||
self.pending_transactions.lock().get(hash).cloned()
|
||||
}
|
||||
|
||||
@@ -194,13 +195,13 @@ impl MinerService for TestMinerService {
|
||||
self.pending_transactions.lock().values().cloned().collect()
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
fn pending_transactions(&self, _best_block: BlockNumber) -> Vec<SignedTransaction> {
|
||||
self.pending_transactions.lock().values().cloned().collect()
|
||||
}
|
||||
|
||||
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt> {
|
||||
fn pending_receipt(&self, _best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
||||
// Not much point implementing this since the logic is complex and the only thing it relies on is pending_receipts, which is already tested.
|
||||
self.pending_receipts().get(hash).map(|r|
|
||||
self.pending_receipts(0).get(hash).map(|r|
|
||||
RichReceipt {
|
||||
transaction_hash: Default::default(),
|
||||
transaction_index: Default::default(),
|
||||
@@ -212,7 +213,7 @@ impl MinerService for TestMinerService {
|
||||
)
|
||||
}
|
||||
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
||||
fn pending_receipts(&self, _best_block: BlockNumber) -> BTreeMap<H256, Receipt> {
|
||||
self.pending_receipts.lock().clone()
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,15 @@ impl Into<EthFilter> for Filter {
|
||||
VariadicValue::Null => None,
|
||||
VariadicValue::Single(t) => Some(vec![t.into()]),
|
||||
VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect())
|
||||
}).filter_map(|m| m).collect()).into_iter();
|
||||
vec![iter.next(), iter.next(), iter.next(), iter.next()]
|
||||
}
|
||||
}).collect()).into_iter();
|
||||
|
||||
vec![
|
||||
iter.next().unwrap_or(None),
|
||||
iter.next().unwrap_or(None),
|
||||
iter.next().unwrap_or(None),
|
||||
iter.next().unwrap_or(None)
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +103,8 @@ mod tests {
|
||||
use util::hash::*;
|
||||
use super::*;
|
||||
use v1::types::BlockNumber;
|
||||
use ethcore::filter::Filter as EthFilter;
|
||||
use ethcore::client::BlockID;
|
||||
|
||||
#[test]
|
||||
fn topic_deserialization() {
|
||||
@@ -123,4 +131,31 @@ mod tests {
|
||||
topics: None
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_conversion() {
|
||||
let filter = Filter {
|
||||
from_block: Some(BlockNumber::Earliest),
|
||||
to_block: Some(BlockNumber::Latest),
|
||||
address: Some(VariadicValue::Multiple(vec![])),
|
||||
topics: Some(vec![
|
||||
VariadicValue::Null,
|
||||
VariadicValue::Single("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()),
|
||||
VariadicValue::Null,
|
||||
]),
|
||||
};
|
||||
|
||||
let eth_filter: EthFilter = filter.into();
|
||||
assert_eq!(eth_filter, EthFilter {
|
||||
from_block: BlockID::Earliest,
|
||||
to_block: BlockID::Latest,
|
||||
address: Some(vec![]),
|
||||
topics: vec![
|
||||
None,
|
||||
Some(vec!["000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()]),
|
||||
None,
|
||||
None,
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ description = "Ethcore utility library"
|
||||
homepage = "http://ethcore.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-util"
|
||||
version = "1.3.3"
|
||||
version = "1.3.4"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ mod panics;
|
||||
use mio::{EventLoop, Token};
|
||||
use std::fmt;
|
||||
|
||||
pub use worker::LOCAL_STACK_SIZE;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// IO Error
|
||||
pub enum IoError {
|
||||
|
||||
@@ -22,9 +22,19 @@ use crossbeam::sync::chase_lev;
|
||||
use service::{HandlerId, IoChannel, IoContext};
|
||||
use IoHandler;
|
||||
use panics::*;
|
||||
use std::cell::Cell;
|
||||
|
||||
use std::sync::{Condvar as SCondvar, Mutex as SMutex};
|
||||
|
||||
const STACK_SIZE: usize = 16*1024*1024;
|
||||
|
||||
thread_local! {
|
||||
/// Stack size
|
||||
/// Should be modified if it is changed in Rust since it is no way
|
||||
/// to know or get it
|
||||
pub static LOCAL_STACK_SIZE: Cell<usize> = Cell::new(::std::env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()).unwrap_or(2 * 1024 * 1024));
|
||||
}
|
||||
|
||||
pub enum WorkType<Message> {
|
||||
Readable,
|
||||
Writable,
|
||||
@@ -66,8 +76,9 @@ impl Worker {
|
||||
deleting: deleting.clone(),
|
||||
wait_mutex: wait_mutex.clone(),
|
||||
};
|
||||
worker.thread = Some(thread::Builder::new().name(format!("IO Worker #{}", index)).spawn(
|
||||
worker.thread = Some(thread::Builder::new().stack_size(STACK_SIZE).name(format!("IO Worker #{}", index)).spawn(
|
||||
move || {
|
||||
LOCAL_STACK_SIZE.with(|val| val.set(STACK_SIZE));
|
||||
panic_handler.catch_panic(move || {
|
||||
Worker::work_loop(stealer, channel.clone(), wait, wait_mutex.clone(), deleting)
|
||||
}).unwrap()
|
||||
|
||||
@@ -580,7 +580,8 @@ impl Host {
|
||||
}
|
||||
|
||||
fn handshake_count(&self) -> usize {
|
||||
self.sessions.read().count() - self.session_count()
|
||||
// session_count < total_count is possible because of the data race.
|
||||
self.sessions.read().count().saturating_sub(self.session_count())
|
||||
}
|
||||
|
||||
fn keep_alive(&self, io: &IoContext<NetworkIoMessage>) {
|
||||
|
||||
Reference in New Issue
Block a user