diff --git a/Cargo.lock b/Cargo.lock index 8c585ca6c..2425aadfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "parity" -version = "1.5.0" +version = "1.5.1" dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -23,7 +23,7 @@ dependencies = [ "ethcore-rpc 1.5.0", "ethcore-signer 1.5.0", "ethcore-stratum 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "ethsync 1.5.0", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -72,6 +72,11 @@ name = "ansi_term" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "antidote" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "app_dirs" version = "1.1.1" @@ -292,6 +297,11 @@ name = "dtoa" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "dtoa" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "elastic-array" version = "0.6.0" @@ -362,7 +372,7 @@ dependencies = [ "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", "ethcore-ipc-nano 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "ethjson 0.1.0", "ethkey 0.2.0", "ethstore 0.1.0", @@ -410,7 +420,7 @@ dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.5.0", "ethcore-rpc 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "fetch 0.1.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", @@ -458,7 +468,7 @@ name = "ethcore-ipc" version = "1.5.0" dependencies = [ "ethcore-devtools 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -505,7 +515,7 @@ dependencies = [ "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", "ethcore-ipc-nano 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "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.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -520,7 +530,7 @@ dependencies = [ "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", "ethcore-network 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -532,7 +542,7 @@ name = "ethcore-logger" version = "1.5.0" dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "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)", @@ -548,7 +558,7 @@ dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.5.0", "ethcore-io 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "ethcrypto 0.1.0", "ethkey 0.2.0", "igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -575,7 +585,7 @@ dependencies = [ "ethcore-devtools 1.5.0", "ethcore-io 1.5.0", "ethcore-ipc 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "ethcrypto 0.1.0", "ethjson 0.1.0", "ethkey 0.2.0", @@ -610,7 +620,7 @@ dependencies = [ "ethcore-devtools 1.5.0", "ethcore-io 1.5.0", "ethcore-rpc 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -629,7 +639,7 @@ dependencies = [ "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", "ethcore-ipc-nano 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)", "jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -640,7 +650,7 @@ dependencies = [ [[package]] name = "ethcore-util" -version = "1.5.0" +version = "1.5.1" 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)", @@ -689,7 +699,7 @@ dependencies = [ name = "ethjson" version = "0.1.0" dependencies = [ - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -745,7 +755,7 @@ dependencies = [ "ethcore-ipc-nano 1.5.0", "ethcore-light 1.5.0", "ethcore-network 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "ethkey 0.2.0", "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)", @@ -779,7 +789,8 @@ dependencies = [ "futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -896,6 +907,35 @@ dependencies = [ "vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.2.0 (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)", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-native-tls" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.0" @@ -924,7 +964,7 @@ version = "1.5.0" dependencies = [ "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -949,6 +989,11 @@ name = "itoa" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "itoa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "jsonrpc-core" version = "4.0.0" @@ -1444,7 +1489,7 @@ name = "parity-hash-fetch" version = "1.5.0" dependencies = [ "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "fetch 0.1.0", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1469,7 +1514,7 @@ version = "1.4.0" dependencies = [ "ethcore-rpc 1.5.0", "ethcore-signer 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1515,7 +1560,7 @@ dependencies = [ "ethcore 1.5.0", "ethcore-ipc 1.5.0", "ethcore-ipc-codegen 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "ethsync 1.5.0", "ipc-common-types 1.5.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1715,15 +1760,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "reqwest" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1795,7 +1840,7 @@ version = "1.4.0" dependencies = [ "ethcore-bigint 0.1.2", "ethcore-rpc 1.5.0", - "ethcore-util 1.5.0", + "ethcore-util 1.5.1", "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-rpc-client 1.4.0", "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1909,6 +1954,11 @@ name = "serde" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde_codegen" version = "0.8.4" @@ -1943,11 +1993,24 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.3.0" +name = "serde_json" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_urlencoded" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2364,6 +2427,7 @@ dependencies = [ "checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" "checksum aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67077478f0a03952bed2e6786338d400d40c25e9836e08ad50af96607317fd03" "checksum ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1f46cd5b1d660c938e3f92dfe7a73d832b3281479363dd0cd9c1c2fbf60f7962" +"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4" "checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975" "checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a" @@ -2393,6 +2457,7 @@ dependencies = [ "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 dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" +"checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" "checksum elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)" = "" "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)" = "" @@ -2409,12 +2474,15 @@ dependencies = [ "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" "checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "" +"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1" "checksum hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb3fc65554155980167fb821d05c7c66177f92464976c0b676a19d9e03387a7" +"checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409" "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" "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 itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" +"checksum itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" "checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "" "checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "" "checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "" @@ -2490,7 +2558,7 @@ dependencies = [ "checksum rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "655df67c314c30fa3055a365eae276eb88aa4f3413a352a1ab32c1320eda41ea" "checksum regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)" = "b4329b8928a284580a1c63ec9d846b12f6d3472317243ff7077aff11f23f2b29" "checksum regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "841591b1e05609a643e3b4d0045fce04f701daba7151ddcd3ad47b080693d5a9" -"checksum reqwest 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83186fee0d4dbeb95e610b77b05b05cf5b31703dd375222acb74c3dff4be957c" +"checksum reqwest 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bef9ed8fdfcc30947d6b774938dc0c3f369a474efe440df2c7f278180b2d2e6" "checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "" "checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "" "checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "" @@ -2509,10 +2577,12 @@ dependencies = [ "checksum semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae2ff60ecdb19c255841c066cbfa5f8c2a4ada1eb3ae47c77ab6667128da71f5" "checksum semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e88e43a5a74dd2a11707f9c21dfd4a423c66bd871df813227bb0a3e78f3a1ae9" "checksum serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "58a19c0871c298847e6b68318484685cd51fa5478c0c905095647540031356e5" +"checksum serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4d8f810025e9d09c4eaa49c16eaf878f34a947889e878cd7d3b5bef3197cc119" "checksum serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e422ae53d7933f59c6ff57e7b5870b5c9094b1f473f78ec33d89f8a692c3ec02" "checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21" "checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc" -"checksum serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53d4ebaa8d1d4f90d1b63dfca81ccd98ac20e1e479dbae393cbaf60f6fecd8d8" +"checksum serde_json 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fea48f4d4df4e620e3c81fd2bf28c93dd0d266361a76bac4f254b71f0e13f3cd" +"checksum serde_urlencoded 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a81f15da4b9780e1524697f73b09076b6e42298ef673bead9ca8f848b334ef84" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" "checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" "checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd" diff --git a/Cargo.toml b/Cargo.toml index 18ada1185..a952bc014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity Ethereum client" name = "parity" -version = "1.5.0" +version = "1.5.1" license = "GPL-3.0" authors = ["Parity Technologies "] build = "build.rs" diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 06c3054a9..111bdfde7 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -300,22 +300,29 @@ impl TestBlockChainClient { } } - /// Inserts a transaction to miners transactions queue. - pub fn insert_transaction_to_queue(&self) { + /// Inserts a transaction with given gas price to miners transactions queue. + pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 { let keypair = Random.generate().unwrap(); let tx = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), gas: U256::from(100_000), - gas_price: U256::from(20_000_000_000u64), + gas_price: gas_price, nonce: U256::zero() }; let signed_tx = tx.sign(keypair.secret(), None); self.set_balance(signed_tx.sender().unwrap(), 10_000_000_000_000_000_000u64.into()); - let res = self.miner.import_external_transactions(self, vec![signed_tx]); + let hash = signed_tx.hash(); + let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]); let res = res.into_iter().next().unwrap().expect("Successful import"); assert_eq!(res, TransactionImportResult::Current); + hash + } + + /// Inserts a transaction to miners transactions queue. + pub fn insert_transaction_to_queue(&self) -> H256 { + self.insert_transaction_with_gas_price_to_queue(U256::from(20_000_000_000u64)) } /// Set reported history size. diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index b10bf3246..e22197df0 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -37,7 +37,6 @@ use bit_set::BitSet; use util::*; -type CodePosition = usize; type ProgramCounter = usize; const ONE: U256 = U256([1, 0, 0, 0]); diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs index 7d5f5c299..193b2086b 100644 --- a/ethcore/src/miner/banning_queue.rs +++ b/ethcore/src/miner/banning_queue.rs @@ -22,7 +22,7 @@ use std::ops::{Deref, DerefMut}; use std::cell::Cell; use transaction::{SignedTransaction, Action}; use transient_hashmap::TransientHashMap; -use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails}; +use miner::{TransactionQueue, TransactionQueueDetailsProvider, TransactionImportResult, TransactionOrigin}; use miner::transaction_queue::QueuingInstant; use error::{Error, TransactionError}; use util::{Uint, U256, H256, Address, Hashable}; @@ -76,16 +76,12 @@ impl BanningTransactionQueue { /// Add to the queue taking bans into consideration. /// May reject transaction because of the banlist. - pub fn add_with_banlist( + pub fn add_with_banlist( &mut self, transaction: SignedTransaction, time: QueuingInstant, - account_details: &F, - gas_estimator: &G, - ) -> Result where - F: Fn(&Address) -> AccountDetails, - G: Fn(&SignedTransaction) -> U256, - { + details_provider: &TransactionQueueDetailsProvider, + ) -> Result { if let Threshold::BanAfter(threshold) = self.ban_threshold { // NOTE In all checks use direct query to avoid increasing ban timeout. @@ -117,7 +113,7 @@ impl BanningTransactionQueue { } } } - self.queue.add(transaction, TransactionOrigin::External, time, None, account_details, gas_estimator) + self.queue.add(transaction, TransactionOrigin::External, time, None, details_provider) } /// Ban transaction with given hash. @@ -220,22 +216,16 @@ mod tests { use transaction::{Transaction, SignedTransaction, Action}; use error::{Error, TransactionError}; use client::TransactionImportResult; - use miner::{TransactionQueue, TransactionOrigin, AccountDetails}; + use miner::{TransactionQueue, TransactionOrigin}; use util::{Uint, U256, Address, FromHex, Hashable}; + use miner::transaction_queue::test::DummyTransactionDetailsProvider; fn queue() -> BanningTransactionQueue { BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180)) } - fn default_account_details(_address: &Address) -> AccountDetails { - AccountDetails { - nonce: U256::zero(), - balance: !U256::zero(), - } - } - - fn gas_required(_tx: &SignedTransaction) -> U256 { - 0.into() + fn default_tx_provider() -> DummyTransactionDetailsProvider { + DummyTransactionDetailsProvider::default().with_account_nonce(U256::zero()) } fn transaction(action: Action) -> SignedTransaction { @@ -265,7 +255,7 @@ mod tests { let mut txq = queue(); // when - txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_required).unwrap(); + txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then // should also deref to queue @@ -281,12 +271,12 @@ mod tests { let banlist1 = txq.ban_sender(tx.sender().unwrap()); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_sender(tx.sender().unwrap()); - let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required); + let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()); // then assert!(banlist2, "Threshold should be reached - banned."); @@ -305,12 +295,12 @@ mod tests { let banlist1 = txq.ban_recipient(recipient); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_recipient(recipient); - let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required); + let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()); // then assert!(banlist2, "Threshold should be reached - banned."); @@ -327,12 +317,12 @@ mod tests { let banlist1 = txq.ban_codehash(codehash); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_codehash(codehash); - let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required); + let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()); // then assert!(banlist2, "Threshold should be reached - banned."); diff --git a/ethcore/src/miner/local_transactions.rs b/ethcore/src/miner/local_transactions.rs index 62cec7151..1bd551a0b 100644 --- a/ethcore/src/miner/local_transactions.rs +++ b/ethcore/src/miner/local_transactions.rs @@ -70,36 +70,43 @@ impl LocalTransactionsList { } pub fn mark_pending(&mut self, hash: H256) { + debug!(target: "own_tx", "Imported to Current (hash {:?})", hash); self.clear_old(); self.transactions.insert(hash, Status::Pending); } pub fn mark_future(&mut self, hash: H256) { + debug!(target: "own_tx", "Imported to Future (hash {:?})", hash); self.transactions.insert(hash, Status::Future); self.clear_old(); } pub fn mark_rejected(&mut self, tx: SignedTransaction, err: TransactionError) { + debug!(target: "own_tx", "Transaction rejected (hash {:?}): {:?}", tx.hash(), err); self.transactions.insert(tx.hash(), Status::Rejected(tx, err)); self.clear_old(); } pub fn mark_replaced(&mut self, tx: SignedTransaction, gas_price: U256, hash: H256) { + debug!(target: "own_tx", "Transaction replaced (hash {:?}) by {:?} (new gas price: {:?})", tx.hash(), hash, gas_price); self.transactions.insert(tx.hash(), Status::Replaced(tx, gas_price, hash)); self.clear_old(); } pub fn mark_invalid(&mut self, tx: SignedTransaction) { + warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash()); self.transactions.insert(tx.hash(), Status::Invalid(tx)); self.clear_old(); } pub fn mark_dropped(&mut self, tx: SignedTransaction) { + warn!(target: "own_tx", "Transaction dropped (hash {:?})", tx.hash()); self.transactions.insert(tx.hash(), Status::Dropped(tx)); self.clear_old(); } pub fn mark_mined(&mut self, tx: SignedTransaction) { + info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash()); self.transactions.insert(tx.hash(), Status::Mined(tx)); self.clear_old(); } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 2f1e80058..19b81a839 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -30,11 +30,13 @@ use transaction::{Action, SignedTransaction, PendingTransaction}; use receipt::{Receipt, RichReceipt}; use spec::Spec; use engines::{Engine, Seal}; -use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin}; +use miner::{MinerService, MinerStatus, TransactionQueue, TransactionQueueDetailsProvider, PrioritizationStrategy, + AccountDetails, TransactionOrigin}; use miner::banning_queue::{BanningTransactionQueue, Threshold}; use miner::work_notify::WorkPoster; use miner::price_info::PriceInfo; use miner::local_transactions::{Status as LocalTransactionStatus}; +use miner::service_transaction_checker::ServiceTransactionChecker; use header::BlockNumber; /// Different possible definitions for pending transaction set. @@ -103,8 +105,10 @@ pub struct MinerOptions { pub enable_resubmission: bool, /// Global gas limit for all transaction in the queue except for local and retracted. pub tx_queue_gas_limit: GasLimit, - /// Banning settings + /// Banning settings. pub tx_queue_banning: Banning, + /// Do we refuse to accept service transactions even if sender is certified. + pub refuse_service_transactions: bool, } impl Default for MinerOptions { @@ -123,6 +127,7 @@ impl Default for MinerOptions { work_queue_size: 20, enable_resubmission: true, tx_queue_banning: Banning::Disabled, + refuse_service_transactions: false, } } } @@ -222,6 +227,7 @@ pub struct Miner { accounts: Option>, work_poster: Option, gas_pricer: Mutex, + service_transaction_action: ServiceTransactionAction, } impl Miner { @@ -245,6 +251,10 @@ impl Miner { ban_duration, ), }; + let service_transaction_action = match options.refuse_service_transactions { + true => ServiceTransactionAction::Refuse, + false => ServiceTransactionAction::Check(ServiceTransactionChecker::default()), + }; Miner { transaction_queue: Arc::new(Mutex::new(txq)), next_allowed_reseal: Mutex::new(Instant::now()), @@ -264,6 +274,7 @@ impl Miner { engine: spec.engine.clone(), work_poster: work_poster, gas_pricer: Mutex::new(gas_pricer), + service_transaction_action: service_transaction_action, } } @@ -527,8 +538,8 @@ impl Miner { } } - fn update_gas_limit(&self, chain: &MiningBlockChainClient) { - let gas_limit = chain.best_block_header().gas_limit(); + fn update_gas_limit(&self, client: &MiningBlockChainClient) { + let gas_limit = client.best_block_header().gas_limit(); let mut queue = self.transaction_queue.lock(); queue.set_gas_limit(gas_limit); if let GasLimit::Auto = self.options.tx_queue_gas_limit { @@ -538,7 +549,7 @@ impl Miner { } /// Returns true if we had to prepare new pending block. - fn prepare_work_sealing(&self, chain: &MiningBlockChainClient) -> bool { + fn prepare_work_sealing(&self, client: &MiningBlockChainClient) -> bool { trace!(target: "miner", "prepare_work_sealing: entering"); let prepare_new = { let mut sealing_work = self.sealing_work.lock(); @@ -556,11 +567,11 @@ impl Miner { // | NOTE Code below requires transaction_queue and sealing_work locks. | // | Make sure to release the locks before calling that method. | // -------------------------------------------------------------------------- - let (block, original_work_hash) = self.prepare_block(chain); + let (block, original_work_hash) = self.prepare_block(client); self.prepare_work(block, original_work_hash); } let mut sealing_block_last_request = self.sealing_block_last_request.lock(); - let best_number = chain.chain_info().best_block_number; + let best_number = client.chain_info().best_block_number; if *sealing_block_last_request != best_number { trace!(target: "miner", "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", *sealing_block_last_request, best_number); *sealing_block_last_request = best_number; @@ -572,54 +583,42 @@ impl Miner { fn add_transactions_to_queue( &self, - chain: &MiningBlockChainClient, + client: &MiningBlockChainClient, transactions: Vec, default_origin: TransactionOrigin, min_block: Option, - transaction_queue: &mut BanningTransactionQueue) - -> Vec> { - - let fetch_account = |a: &Address| AccountDetails { - nonce: chain.latest_nonce(a), - balance: chain.latest_balance(a), - }; - + transaction_queue: &mut BanningTransactionQueue, + ) -> Vec> { let accounts = self.accounts.as_ref() .and_then(|provider| provider.accounts().ok()) .map(|accounts| accounts.into_iter().collect::>()); - let schedule = chain.latest_schedule(); - let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); - let best_block_header = chain.best_block_header().decode(); - let insertion_time = chain.chain_info().best_block_number; + let insertion_time = client.chain_info().best_block_number; transactions.into_iter() .map(|tx| { - if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() { - debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", tx.hash()); + let hash = tx.hash(); + if client.transaction_block(TransactionId::Hash(hash)).is_some() { + debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", hash); return Err(Error::Transaction(TransactionError::AlreadyImported)); } - match self.engine.verify_transaction_basic(&tx, &best_block_header) { - Err(e) => { - debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e); - Err(e) - }, - Ok(()) => { - let origin = accounts.as_ref().and_then(|accounts| { - tx.sender().ok().and_then(|sender| match accounts.contains(&sender) { - true => Some(TransactionOrigin::Local), - false => None, - }) - }).unwrap_or(default_origin); + let origin = accounts.as_ref().and_then(|accounts| { + tx.sender().ok().and_then(|sender| match accounts.contains(&sender) { + true => Some(TransactionOrigin::Local), + false => None, + }) + }).unwrap_or(default_origin); - match origin { - TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { - transaction_queue.add(tx, origin, insertion_time, min_block, &fetch_account, &gas_required) - }, - TransactionOrigin::External => { - transaction_queue.add_with_banlist(tx, insertion_time, &fetch_account, &gas_required) - } - } + // try to install service transaction checker before appending transactions + self.service_transaction_action.update_from_chain_client(client); + + let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action); + match origin { + TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { + transaction_queue.add(tx, origin, insertion_time, min_block, &details_provider) + }, + TransactionOrigin::External => { + transaction_queue.add_with_banlist(tx, insertion_time, &details_provider) }, } }) @@ -867,7 +866,6 @@ impl MinerService for Miner { pending: PendingTransaction, ) -> Result { - let hash = pending.transaction.hash(); trace!(target: "own_tx", "Importing transaction: {:?}", pending); let imported = { @@ -878,12 +876,10 @@ impl MinerService for Miner { ).pop().expect("one result returned per added transaction; one added => one result; qed"); match import { - Ok(ref res) => { - trace!(target: "own_tx", "Imported transaction to {:?} (hash: {:?})", res, hash); + Ok(_) => { trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); }, Err(ref e) => { - trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash); trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); warn!(target: "own_tx", "Error importing transaction: {:?}", e); }, @@ -1165,6 +1161,60 @@ impl MinerService for Miner { } } +/// Action when service transaction is received +enum ServiceTransactionAction { + /// Refuse service transaction immediately + Refuse, + /// Accept if sender is certified to send service transactions + Check(ServiceTransactionChecker), +} + +impl ServiceTransactionAction { + pub fn update_from_chain_client(&self, client: &MiningBlockChainClient) { + if let ServiceTransactionAction::Check(ref checker) = *self { + checker.update_from_chain_client(client); + } + } + + pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result { + match *self { + ServiceTransactionAction::Refuse => Err("configured to refuse service transactions".to_owned()), + ServiceTransactionAction::Check(ref checker) => checker.check(client, tx), + } + } +} + +struct TransactionDetailsProvider<'a> { + client: &'a MiningBlockChainClient, + service_transaction_action: &'a ServiceTransactionAction, +} + +impl<'a> TransactionDetailsProvider<'a> { + pub fn new(client: &'a MiningBlockChainClient, service_transaction_action: &'a ServiceTransactionAction) -> Self { + TransactionDetailsProvider { + client: client, + service_transaction_action: service_transaction_action, + } + } +} + +impl<'a> TransactionQueueDetailsProvider for TransactionDetailsProvider<'a> { + fn fetch_account(&self, address: &Address) -> AccountDetails { + AccountDetails { + nonce: self.client.latest_nonce(address), + balance: self.client.latest_balance(address), + } + } + + fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256 { + tx.gas_required(&self.client.latest_schedule()).into() + } + + fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result { + self.service_transaction_action.check(self.client, tx) + } +} + #[cfg(test)] mod tests { @@ -1229,6 +1279,7 @@ mod tests { work_queue_size: 5, enable_resubmission: true, tx_queue_banning: Banning::Disabled, + refuse_service_transactions: false, }, GasPricer::new_fixed(0u64.into()), &Spec::new_test(), diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 563e068a6..375bed3bf 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -46,12 +46,14 @@ mod external; mod local_transactions; mod miner; mod price_info; +mod service_transaction_checker; mod transaction_queue; mod work_notify; pub use self::external::{ExternalMiner, ExternalMinerService}; pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit}; -pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin}; +pub use self::transaction_queue::{TransactionQueue, TransactionDetailsProvider as TransactionQueueDetailsProvider, + PrioritizationStrategy, AccountDetails, TransactionOrigin}; pub use self::local_transactions::{Status as LocalTransactionStatus}; pub use client::TransactionImportResult; diff --git a/ethcore/src/miner/service_transaction_checker.rs b/ethcore/src/miner/service_transaction_checker.rs new file mode 100644 index 000000000..341a4eef8 --- /dev/null +++ b/ethcore/src/miner/service_transaction_checker.rs @@ -0,0 +1,212 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use client::MiningBlockChainClient; +use transaction::SignedTransaction; +use util::{U256, Uint, Mutex}; + +const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker"; + +/// Service transactions checker. +#[derive(Default)] +pub struct ServiceTransactionChecker { + contract: Mutex>, +} + +impl ServiceTransactionChecker { + /// Try to create instance, reading contract address from given chain client. + pub fn update_from_chain_client(&self, client: &MiningBlockChainClient) { + let mut contract = self.contract.lock(); + if contract.is_none() { + *contract = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned()) + .and_then(|contract_addr| { + trace!(target: "txqueue", "Configuring for service transaction checker contract from {}", contract_addr); + + Some(provider::Contract::new(contract_addr)) + }) + } + } + + /// Checks if service transaction can be appended to the transaction queue. + pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result { + debug_assert_eq!(tx.gas_price, U256::zero()); + + if let Some(ref contract) = *self.contract.lock() { + let do_call = |a, d| client.call_contract(a, d); + contract.certified(&do_call, &tx.sender().unwrap()) + } else { + Err("contract is not configured".to_owned()) + } + } +} + +mod provider { + // Autogenerated from JSON contract definition using Rust contract convertor. + // Command line: --jsonabi=SimpleCertifier.abi --explicit-do-call + #![allow(unused_imports)] + use std::string::String; + use std::result::Result; + use std::fmt; + use {util, ethabi}; + use util::{FixedHash, Uint}; + + pub struct Contract { + contract: ethabi::Contract, + address: util::Address, + + } + impl Contract { + pub fn new(address: util::Address) -> Self + { + Contract { + contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"certify\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"getAddress\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"revoke\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"delegate\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"getUint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setDelegate\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"certified\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")), + address: address, + + } + } + fn as_string(e: T) -> String { format!("{:?}", e) } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set_owner(&self, do_call: &F, _new: &util::Address) -> Result<(), String> + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("setOwner".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_new.clone().0)] + ).map_err(Self::as_string)?; + call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn certify(&self, do_call: &F, _who: &util::Address) -> Result<(), String> + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("certify".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_who.clone().0)] + ).map_err(Self::as_string)?; + call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get_address(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("getAddress".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())] + ).map_err(Self::as_string)?; + let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + let mut result = output.into_iter().rev().collect::>(); + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn revoke(&self, do_call: &F, _who: &util::Address) -> Result<(), String> + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("revoke".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_who.clone().0)] + ).map_err(Self::as_string)?; + call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn owner(&self, do_call: &F) -> Result + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("owner".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![] + ).map_err(Self::as_string)?; + let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + let mut result = output.into_iter().rev().collect::>(); + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn delegate(&self, do_call: &F) -> Result + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("delegate".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![] + ).map_err(Self::as_string)?; + let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + let mut result = output.into_iter().rev().collect::>(); + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get_uint(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("getUint".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())] + ).map_err(Self::as_string)?; + let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + let mut result = output.into_iter().rev().collect::>(); + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_uint().ok_or("Invalid type returned")?; util::U256::from(r.as_ref()) })) + } + + /// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn set_delegate(&self, do_call: &F, _new: &util::Address) -> Result<(), String> + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("setDelegate".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_new.clone().0)] + ).map_err(Self::as_string)?; + call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + + Ok(()) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn certified(&self, do_call: &F, _who: &util::Address) -> Result + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("certified".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_who.clone().0)] + ).map_err(Self::as_string)?; + let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + let mut result = output.into_iter().rev().collect::>(); + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r })) + } + + /// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}` + #[allow(dead_code)] + pub fn get(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result + where F: Fn(util::Address, Vec) -> Result, String> + Send { + let call = self.contract.function("get".into()).map_err(Self::as_string)?; + let data = call.encode_call( + vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())] + ).map_err(Self::as_string)?; + let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + let mut result = output.into_iter().rev().collect::>(); + Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_fixed_bytes().ok_or("Invalid type returned")?; util::H256::from_slice(r.as_ref()) })) + } + } +} diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index fe3af79dc..a8eba6a01 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -31,10 +31,30 @@ //! //! use util::{Uint, U256, Address}; //! use ethkey::{Random, Generator}; -//! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin}; +//! use ethcore::miner::{TransactionQueue, TransactionQueueDetailsProvider, AccountDetails, TransactionOrigin}; //! use ethcore::transaction::*; //! use rustc_serialize::hex::FromHex; //! +//! #[derive(Default)] +//! struct DummyTransactionDetailsProvider; +//! +//! impl TransactionQueueDetailsProvider for DummyTransactionDetailsProvider { +//! fn fetch_account(&self, _address: &Address) -> AccountDetails { +//! AccountDetails { +//! nonce: U256::from(10), +//! balance: U256::from(1_000_000) +//! } +//! } +//! +//! fn estimate_gas_required(&self, _tx: &SignedTransaction) -> U256 { +//! 2.into() +//! } +//! +//! fn is_service_transaction_acceptable(&self, _tx: &SignedTransaction) -> Result { +//! Ok(true) +//! } +//! } +//! //! fn main() { //! let key = Random.generate().unwrap(); //! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(), @@ -44,15 +64,11 @@ //! //! let st1 = t1.sign(&key.secret(), None); //! let st2 = t2.sign(&key.secret(), None); -//! let default_account_details = |_a: &Address| AccountDetails { -//! nonce: U256::from(10), -//! balance: U256::from(1_000_000), -//! }; -//! let gas_estimator = |_tx: &SignedTransaction| 2.into(); +//! let details_provider = DummyTransactionDetailsProvider::default(); //! //! let mut txq = TransactionQueue::default(); -//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); -//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); +//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &details_provider).unwrap(); +//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &details_provider).unwrap(); //! //! // Check status //! assert_eq!(txq.status().pending, 2); @@ -499,6 +515,16 @@ pub enum PrioritizationStrategy { pub type QueuingInstant = BlockNumber; const DEFAULT_QUEUING_PERIOD: BlockNumber = 128; +/// `TransactionQueue` transaction details provider. +pub trait TransactionDetailsProvider { + /// Fetch transaction-related account details. + fn fetch_account(&self, address: &Address) -> AccountDetails; + /// Estimate gas required for transaction. + fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256; + /// Check if this service transaction can be accepted by `TransactionQueue`. + fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result; +} + /// `TransactionQueue` implementation pub struct TransactionQueue { /// Prioritization strategy for this queue @@ -634,25 +660,21 @@ impl TransactionQueue { /// Add signed transaction to queue to be verified and imported. /// - /// NOTE fetch_account and gas_estimator should be cheap to compute + /// NOTE details_provider methods should be cheap to compute /// otherwise it might open up an attack vector. - pub fn add( + pub fn add( &mut self, tx: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option, - fetch_account: &F, - gas_estimator: &G, - ) -> Result where - F: Fn(&Address) -> AccountDetails, - G: Fn(&SignedTransaction) -> U256, - { + details_provider: &TransactionDetailsProvider, + ) -> Result { if origin == TransactionOrigin::Local { let hash = tx.hash(); let cloned_tx = tx.clone(); - let result = self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator); + let result = self.add_internal(tx, origin, time, min_block, details_provider); match result { Ok(TransactionImportResult::Current) => { self.local_transactions.mark_pending(hash); @@ -673,36 +695,64 @@ impl TransactionQueue { } result } else { - self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator) + self.add_internal(tx, origin, time, min_block, details_provider) } } /// Adds signed transaction to the queue. - fn add_internal( + fn add_internal( &mut self, tx: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option, - fetch_account: &F, - gas_estimator: &G, - ) -> Result where - F: Fn(&Address) -> AccountDetails, - G: Fn(&SignedTransaction) -> U256, - { + details_provider: &TransactionDetailsProvider, + ) -> Result { + if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price { + // if it is non-service-transaction => drop + let is_service_transaction = tx.gas_price.is_zero(); + if !is_service_transaction { + trace!(target: "txqueue", + "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", + tx.hash(), + tx.gas_price, + self.minimal_gas_price + ); - if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local { - trace!(target: "txqueue", - "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", - tx.hash(), - tx.gas_price, - self.minimal_gas_price - ); + return Err(Error::Transaction(TransactionError::InsufficientGasPrice { + minimal: self.minimal_gas_price, + got: tx.gas_price, + })); + } - return Err(Error::Transaction(TransactionError::InsufficientGasPrice { - minimal: self.minimal_gas_price, - got: tx.gas_price, - })); + let is_service_transaction_accepted = match details_provider.is_service_transaction_acceptable(&tx) { + Ok(true) => true, + Ok(false) => { + trace!(target: "txqueue", + "Dropping service transaction as sender is not certified to send service transactions: {:?} (sender: {:?})", + tx.hash(), + tx.sender(), + ); + + false + }, + Err(contract_err) => { + trace!(target: "txqueue", + "Dropping service transaction as service contract returned error: {:?} (error: {:?})", + tx.hash(), + contract_err, + ); + + false + }, + }; + + if !is_service_transaction_accepted { + return Err(Error::Transaction(TransactionError::InsufficientGasPrice { + minimal: self.minimal_gas_price, + got: tx.gas_price, + })); + } } let full_queues_lowest = self.effective_minimum_gas_price(); @@ -734,7 +784,7 @@ impl TransactionQueue { })); } - let minimal_gas = gas_estimator(&tx); + let minimal_gas = details_provider.estimate_gas_required(&tx); if tx.gas < minimal_gas { trace!(target: "txqueue", "Dropping transaction with insufficient gas: {:?} ({} > {})", @@ -752,10 +802,9 @@ impl TransactionQueue { // Verify signature tx.check_low_s()?; + let client_account = details_provider.fetch_account(&tx.sender()?); + let cost = tx.value + tx.gas_price * tx.gas; let vtx = VerifiedTransaction::new(tx, origin, time, min_block)?; - let client_account = fetch_account(&vtx.sender()); - - let cost = vtx.cost(); if client_account.balance < cost { trace!(target: "txqueue", "Dropping transaction without sufficient balance: {:?} ({} < {})", @@ -899,8 +948,8 @@ impl TransactionQueue { /// so transactions left in queue are processed according to client nonce. /// /// If gap is introduced marks subsequent transactions as future - pub fn remove_invalid(&mut self, transaction_hash: &H256, fetch_nonce: &T) - where T: Fn(&Address) -> U256 { + pub fn remove_invalid(&mut self, transaction_hash: &H256, fetch_nonce: &F) + where F: Fn(&Address) -> U256 { assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); let transaction = self.by_hash.remove(transaction_hash); @@ -1326,7 +1375,7 @@ fn check_if_removed(sender: &Address, nonce: &U256, dropped: Option, + } + + impl Default for DummyTransactionDetailsProvider { + fn default() -> Self { + DummyTransactionDetailsProvider { + account_details: default_account_details(), + gas_required: U256::zero(), + service_transactions_check_result: Ok(false), + } + } + } + + impl DummyTransactionDetailsProvider { + pub fn with_account(mut self, account_details: AccountDetails) -> Self { + self.account_details = account_details; + self + } + + pub fn with_account_nonce(mut self, nonce: U256) -> Self { + self.account_details.nonce = nonce; + self + } + + pub fn with_tx_gas_required(mut self, gas_required: U256) -> Self { + self.gas_required = gas_required; + self + } + + pub fn service_transaction_checker_returns_error(mut self, error: &str) -> Self { + self.service_transactions_check_result = Err(error.to_owned()); + self + } + + pub fn service_transaction_checker_accepts(mut self, accepts: bool) -> Self { + self.service_transactions_check_result = Ok(accepts); + self + } + } + + impl TransactionDetailsProvider for DummyTransactionDetailsProvider { + fn fetch_account(&self, _address: &Address) -> AccountDetails { + AccountDetails { + nonce: self.account_details.nonce, + balance: self.account_details.balance, + } + } + + fn estimate_gas_required(&self, _tx: &SignedTransaction) -> U256 { + self.gas_required + } + + fn is_service_transaction_acceptable(&self, _tx: &SignedTransaction) -> Result { + self.service_transactions_check_result.clone() + } + } + fn unwrap_tx_err(err: Result) -> TransactionError { match err.unwrap_err() { Error::Transaction(e) => e, @@ -1374,15 +1483,19 @@ mod test { new_tx(default_nonce(), default_gas_price()) } - fn default_account_details(_address: &Address) -> AccountDetails { + fn default_account_details() -> AccountDetails { AccountDetails { nonce: default_nonce(), balance: !U256::zero() } } - fn gas_estimator(_tx: &SignedTransaction) -> U256 { - U256::zero() + fn default_account_details_for_addr(_a: &Address) -> AccountDetails { + default_account_details() + } + + fn default_tx_provider() -> DummyTransactionDetailsProvider { + DummyTransactionDetailsProvider::default() } fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { @@ -1436,14 +1549,14 @@ mod test { let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); let sender = tx1.sender().unwrap(); let nonce = tx1.nonce; - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 2); assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into())); // when let tx = new_tx(123.into(), 1.into()); - let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); // then // No longer the case as we don't even consider a transaction that isn't above a full @@ -1596,16 +1709,15 @@ mod test { // given let mut txq = TransactionQueue::default(); let (tx, tx2) = new_similar_tx_pair(); - let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: - !U256::zero() }; + let prev_nonce = default_account_details().nonce - U256::one(); // First insert one transaction to future - let res = txq.add(tx, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)); assert_eq!(res.unwrap(), TransactionImportResult::Future); assert_eq!(txq.status().future, 1); // now import second transaction to current - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); // and then there should be only one transaction in current (the one with higher gas_price) assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1621,16 +1733,15 @@ mod test { // given let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 1.into()); - let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: - !U256::zero() }; + let prev_nonce = default_account_details().nonce - U256::one(); // First insert one transaction to future - let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator); + let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)); assert_eq!(res.unwrap(), TransactionImportResult::Future); assert_eq!(txq.status().future, 1); // now import second transaction to current - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1649,7 +1760,7 @@ mod test { let tx = new_tx_default(); // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1668,10 +1779,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); - let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); - let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()); + let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()); + let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()); + let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1702,10 +1813,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); - let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); - let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()); + let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()); + let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()); + let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1748,7 +1859,7 @@ mod test { txq.set_gas_limit(limit); // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { @@ -1766,13 +1877,13 @@ mod test { // given let mut txq = TransactionQueue::default(); let tx = new_tx_default(); - let account = |a: &Address| AccountDetails { - nonce: default_account_details(a).nonce, + let account = AccountDetails { + nonce: default_account_details().nonce, balance: U256::one() }; // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &account, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account(account)); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance { @@ -1792,7 +1903,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { @@ -1812,7 +1923,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_tx_provider()); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1842,7 +1953,7 @@ mod test { rlp::decode(s.as_raw()) }; // when - let res = txq.add(stx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(stx, TransactionOrigin::External, 0, None, &default_tx_provider()); // then assert!(res.is_err()); @@ -1856,8 +1967,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then let top = txq.top_transactions(); @@ -1876,9 +1987,9 @@ mod test { // when // first insert the one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then the one with lower gas price, but local - txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); // then let top = txq.top_transactions(); @@ -1895,15 +2006,15 @@ mod test { // the second one has same nonce but higher `gas_price` let (_, tx0) = new_similar_tx_pair(); - txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // the one with higher gas price is first assert_eq!(txq.top_transactions()[0], tx0); assert_eq!(txq.top_transactions()[1], tx1); // when // insert second as local - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); // then // the order should be updated @@ -1922,9 +2033,9 @@ mod test { // when // first insert local one with higher gas price - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); // then the one with lower gas price, but from retracted block - txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_tx_provider()).unwrap(); // then let top = txq.top_transactions(); @@ -1940,8 +2051,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); // then let top = txq.top_transactions(); @@ -1953,17 +2064,17 @@ mod test { #[test] fn should_penalize_transactions_from_sender_in_future() { // given - let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() }; + let prev_nonce = default_account_details().nonce - U256::one(); let mut txq = TransactionQueue::default(); // txa, txb - slightly bigger gas price to have consistent ordering let (txa, txb) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); + txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); assert_eq!(txq.status().future, 4); @@ -1988,10 +2099,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -2021,10 +2132,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -2047,14 +2158,14 @@ mod test { #[test] fn should_return_pending_hashes() { - // given + // given let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then let top = txq.pending_hashes(); @@ -2071,8 +2182,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then assert_eq!(res1, TransactionImportResult::Current); @@ -2093,8 +2204,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_account_details, &gas_estimator).unwrap(); - let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_tx_provider()).unwrap(); + let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then assert_eq!(res1, TransactionImportResult::Current); @@ -2108,15 +2219,14 @@ mod test { #[test] fn should_correctly_update_futures_when_removing() { // given - let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: - !U256::zero() }; + let prev_nonce = default_account_details().nonce - U256::one(); let next2_nonce = default_nonce() + U256::from(3); let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(); assert_eq!(txq.status().future, 2); // when @@ -2138,13 +2248,13 @@ mod test { let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None); let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None); - txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 1); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().future, 1); // when - txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then let stats = txq.status(); @@ -2160,8 +2270,8 @@ mod test { // given let mut txq2 = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); - txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq2.status().pending, 1); assert_eq!(txq2.status().future, 1); @@ -2182,10 +2292,10 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let tx3 = new_tx_default(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 3); // when @@ -2204,8 +2314,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // add - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); let stats = txq.status(); assert_eq!(stats.pending, 2); @@ -2224,11 +2334,11 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let sender = tx.sender().unwrap(); let nonce = tx.nonce; - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 1); // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()); // then let t = txq.top_transactions(); @@ -2245,14 +2355,14 @@ mod test { txq.current.set_limit(10); let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 2); // when - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then assert_eq!(txq.status().future, 1); @@ -2263,11 +2373,11 @@ mod test { let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero()); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // limited by gas - txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err(); + txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap_err(); assert_eq!(txq.status().pending, 2); } @@ -2277,12 +2387,12 @@ mod test { let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); // Not accepted because of limit - txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err(); - txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap_err(); + txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 4); } @@ -2291,10 +2401,9 @@ mod test { let mut txq = TransactionQueue::default(); let tx = new_tx_default(); let last_nonce = tx.nonce + U256::one(); - let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() }; // when - let res = txq.add(tx, TransactionOrigin::External, 0, None, &fetch_last_nonce, &gas_estimator); + let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(last_nonce)); // then assert_eq!(unwrap_tx_err(res), TransactionError::Old); @@ -2306,16 +2415,15 @@ mod test { #[test] fn should_not_insert_same_transaction_twice() { // given - let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(), - balance: !U256::zero() }; + let nonce = default_account_details().nonce + U256::one(); let mut txq = TransactionQueue::default(); let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().future, 1); assert_eq!(txq.status().pending, 0); // when - let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &nonce, &gas_estimator); + let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce)); // then assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported); @@ -2329,15 +2437,15 @@ mod test { // given let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 2); // when txq.remove_invalid(&tx1.hash(), &|_| default_nonce()); assert_eq!(txq.status().pending, 0); assert_eq!(txq.status().future, 1); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then let stats = txq.status(); @@ -2351,10 +2459,10 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let tx3 = new_tx_default(); - txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().pending, 3); // when @@ -2381,8 +2489,8 @@ mod test { }; // when - txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then let stats = txq.status(); @@ -2409,10 +2517,10 @@ mod test { }; // when - txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx0, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx0, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then let stats = txq.status(); @@ -2424,12 +2532,11 @@ mod test { #[test] fn should_recalculate_height_when_removing_from_future() { // given - let previous_nonce = |a: &Address| - AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() }; + let previous_nonce = default_account_details().nonce - U256::one(); let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(previous_nonce)).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(previous_nonce)).unwrap(); assert_eq!(txq.status().future, 2); // when @@ -2457,10 +2564,9 @@ mod test { let tx = new_tx_default(); let from = tx.sender().unwrap(); let nonce = tx.nonce; - let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() }; // when - txq.add(tx, TransactionOrigin::External, 0, None, &details, &gas_estimator).unwrap(); + txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce)).unwrap(); // then assert_eq!(txq.last_nonce(&from), Some(nonce)); @@ -2472,10 +2578,9 @@ mod test { let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (nonce1, nonce2) = (tx1.nonce, tx2.nonce); - let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; // Insert first transaction - txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(); // when txq.cull(tx2.sender().unwrap(), nonce2 + U256::one()); @@ -2491,13 +2596,12 @@ mod test { let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into()); let sender = tx1.sender().unwrap(); let (nonce1, nonce2) = (tx1.nonce, tx2.nonce); - let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; // when // Insert first transaction - assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(), TransactionImportResult::Current); // Second should go to future - assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(), TransactionImportResult::Future); // Now block is imported txq.cull(sender, nonce2 - U256::from(1)); // tx2 should be not be promoted to current @@ -2516,9 +2620,9 @@ mod test { assert_eq!(txq.has_local_pending_transactions(), false); // when - assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(), TransactionImportResult::Current); assert_eq!(txq.has_local_pending_transactions(), false); - assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(), TransactionImportResult::Current); // then assert_eq!(txq.has_local_pending_transactions(), true); @@ -2529,12 +2633,11 @@ mod test { // given let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance: - default_account_details(a).balance }; + let prev_nonce = default_account_details().nonce - U256::one(); // when - assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); - assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(), TransactionImportResult::Future); // then assert_eq!(txq.future.by_priority.len(), 1); @@ -2559,14 +2662,14 @@ mod test { (tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None)) }; let sender = tx1.sender().unwrap(); - txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx3, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.future.by_priority.len(), 0); assert_eq!(txq.current.by_priority.len(), 3); // when - let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); + let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_tx_provider()); // then assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); @@ -2579,11 +2682,11 @@ mod test { // given let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - let high_gas = |_: &SignedTransaction| 100_001.into(); + let high_gas = 100_001.into(); // when - let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); - let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &high_gas); + let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_tx_provider()); + let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider().with_tx_gas_required(high_gas)); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -2604,10 +2707,10 @@ mod test { AccountDetails { nonce: default_nonce() + U256::one(), balance: !U256::zero() }; // Insert all transactions - txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.top_transactions().len(), 4); // when @@ -2625,15 +2728,15 @@ mod test { let (tx3, tx4) = new_tx_pair_default(2.into(), 0.into()); // Insert all transactions - txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx2, TransactionOrigin::External, 5, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_account_details, &gas_estimator).unwrap(); - txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + txq.add(tx2, TransactionOrigin::External, 5, None, &default_tx_provider()).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_tx_provider()).unwrap(); + txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); assert_eq!(txq.top_transactions().len(), 3); assert_eq!(txq.future_transactions().len(), 1); // when - txq.remove_old(&default_account_details, 9 + super::DEFAULT_QUEUING_PERIOD); + txq.remove_old(&default_account_details_for_addr, 9 + super::DEFAULT_QUEUING_PERIOD); // then assert_eq!(txq.top_transactions().len(), 2); @@ -2641,4 +2744,75 @@ mod test { assert_eq!(txq.top_transactions(), vec![tx1, tx3]); } + #[test] + fn should_accept_local_service_transaction() { + // given + let tx = new_tx(123.into(), 0.into()); + let mut txq = TransactionQueue::default(); + txq.set_minimal_gas_price(100.into()); + + // when + txq.add(tx, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(); + + // then + assert_eq!(txq.top_transactions().len(), 1); + } + + #[test] + fn should_not_accept_external_service_transaction_if_sender_not_certified() { + // given + let tx1 = new_tx(123.into(), 0.into()); + let tx2 = new_tx(456.into(), 0.into()); + let mut txq = TransactionQueue::default(); + txq.set_minimal_gas_price(100.into()); + + // when + assert_eq!(unwrap_tx_err(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider())), + TransactionError::InsufficientGasPrice { + minimal: 100.into(), + got: 0.into(), + }); + assert_eq!(unwrap_tx_err(txq.add(tx2, TransactionOrigin::RetractedBlock, 0, None, &default_tx_provider())), + TransactionError::InsufficientGasPrice { + minimal: 100.into(), + got: 0.into(), + }); + + // then + assert_eq!(txq.top_transactions().len(), 0); + } + + #[test] + fn should_not_accept_external_service_transaction_if_contract_returns_error() { + // given + let tx = new_tx(123.into(), 0.into()); + let mut txq = TransactionQueue::default(); + txq.set_minimal_gas_price(100.into()); + + // when + let details_provider = default_tx_provider().service_transaction_checker_returns_error("Contract error"); + assert_eq!(unwrap_tx_err(txq.add(tx, TransactionOrigin::External, 0, None, &details_provider)), + TransactionError::InsufficientGasPrice { + minimal: 100.into(), + got: 0.into(), + }); + + // then + assert_eq!(txq.top_transactions().len(), 0); + } + + #[test] + fn should_accept_external_service_transaction_if_sender_is_certified() { + // given + let tx = new_tx(123.into(), 0.into()); + let mut txq = TransactionQueue::default(); + txq.set_minimal_gas_price(100.into()); + + // when + let details_provider = default_tx_provider().service_transaction_checker_accepts(true); + txq.add(tx, TransactionOrigin::External, 0, None, &details_provider).unwrap(); + + // then + assert_eq!(txq.top_transactions().len(), 1); + } } diff --git a/ipc-common-types/src/types/version_info.rs b/ipc-common-types/src/types/version_info.rs index bd869bf01..a0b7e6246 100644 --- a/ipc-common-types/src/types/version_info.rs +++ b/ipc-common-types/src/types/version_info.rs @@ -17,8 +17,9 @@ //! Types used in the public API use std::fmt; +use std::str::FromStr; use semver::{Version}; -use util::{H160}; +use util::{H160, FixedHash}; use util::misc::raw_package_info; use release_track::ReleaseTrack; @@ -47,7 +48,7 @@ impl VersionInfo { VersionInfo { track: raw.0.into(), version: { let mut v = Version::parse(raw.1).expect("Environment variables are known to be valid; qed"); v.build = vec![]; v.pre = vec![]; v }, - hash: raw.2.into(), + hash: H160::from_str(raw.2).unwrap_or_else(|_| H160::zero()), } } diff --git a/js/src/api/rpc/personal/personal.js b/js/src/api/rpc/personal/personal.js index 61022bdb4..b52a1de97 100644 --- a/js/src/api/rpc/personal/personal.js +++ b/js/src/api/rpc/personal/personal.js @@ -34,9 +34,9 @@ export default class Personal { .then(outAddress); } - signAndSendTransaction (options, password) { + sendTransaction (options, password) { return this._transport - .execute('personal_signAndSendTransaction', inOptions(options), password); + .execute('personal_sendTransaction', inOptions(options), password); } unlockAccount (account, password, duration = 1) { diff --git a/js/src/dapps/static/console.html b/js/src/dapps/static/console.html index 3c60d5217..908b5964d 100755 --- a/js/src/dapps/static/console.html +++ b/js/src/dapps/static/console.html @@ -4,8 +4,7 @@ JS Console dapp - - +
diff --git a/js/src/jsonrpc/interfaces/personal.js b/js/src/jsonrpc/interfaces/personal.js index 945057084..cc7b31a36 100644 --- a/js/src/jsonrpc/interfaces/personal.js +++ b/js/src/jsonrpc/interfaces/personal.js @@ -40,8 +40,8 @@ export default { } }, - signAndSendTransaction: { - desc: 'Sends and signs a transaction given account passphrase. Does not require the account to be unlocked nor unlocks the account for future transactions. ', + sendTransaction: { + desc: 'Sends transaction and signs it in a single call. The account does not need to be unlocked to make this call, and will not be left unlocked after.', params: [ { type: Object, diff --git a/js/src/modals/Transfer/Details/details.js b/js/src/modals/Transfer/Details/details.js index d8ddb0b25..a6d769ec6 100644 --- a/js/src/modals/Transfer/Details/details.js +++ b/js/src/modals/Transfer/Details/details.js @@ -96,7 +96,7 @@ class TokenSelect extends Component { return ( { label } diff --git a/js/src/redux/providers/balancesActions.js b/js/src/redux/providers/balancesActions.js index 932ddfb8d..4a88bb1be 100644 --- a/js/src/redux/providers/balancesActions.js +++ b/js/src/redux/providers/balancesActions.js @@ -31,7 +31,8 @@ const log = getLogger(LOG_KEYS.Balances); const ETH = { name: 'Ethereum', tag: 'ETH', - image: imagesEthereum + image: imagesEthereum, + native: true }; function setBalances (_balances, skipNotifications = false) { @@ -39,10 +40,9 @@ function setBalances (_balances, skipNotifications = false) { const state = getState(); const currentTokens = Object.values(state.balances.tokens || {}); - const currentTags = [ 'eth' ] - .concat(currentTokens.map((token) => token.tag)) - .filter((tag) => tag) - .map((tag) => tag.toLowerCase()); + const tokensAddresses = currentTokens + .map((token) => token.address) + .filter((address) => address); const accounts = state.personal.accounts; const nextBalances = _balances; @@ -61,50 +61,59 @@ function setBalances (_balances, skipNotifications = false) { const prevTokens = balance.tokens.slice(); const nextTokens = []; - currentTags - .forEach((tag) => { - const prevToken = prevTokens.find((tok) => tok.token.tag.toLowerCase() === tag); - const nextToken = tokens.find((tok) => tok.token.tag.toLowerCase() === tag); + const handleToken = (prevToken, nextToken) => { + // If the given token is not in the current tokens, skip + if (!nextToken && !prevToken) { + return false; + } - // If the given token is not in the current tokens, skip - if (!nextToken && !prevToken) { - return false; - } + // No updates + if (!nextToken) { + return nextTokens.push(prevToken); + } - // No updates - if (!nextToken) { - return nextTokens.push(prevToken); - } - - const { token, value } = nextToken; - - // If it's a new token, push it - if (!prevToken) { - return nextTokens.push({ - token, value - }); - } - - // Otherwise, update the value - const prevValue = prevToken.value; - - // If received a token/eth (old value < new value), notify - if (prevValue.lt(value) && accounts[address] && !skipNotifications) { - const account = accounts[address]; - const txValue = value.minus(prevValue); - - const redirectToAccount = () => { - const route = `/accounts/${account.address}`; - dispatch(push(route)); - }; - - notifyTransaction(account, token, txValue, redirectToAccount); - } + const { token, value } = nextToken; + // If it's a new token, push it + if (!prevToken) { return nextTokens.push({ - ...prevToken, - value + token, value }); + } + + // Otherwise, update the value + const prevValue = prevToken.value; + + // If received a token/eth (old value < new value), notify + if (prevValue.lt(value) && accounts[address] && !skipNotifications) { + const account = accounts[address]; + const txValue = value.minus(prevValue); + + const redirectToAccount = () => { + const route = `/accounts/${account.address}`; + dispatch(push(route)); + }; + + notifyTransaction(account, token, txValue, redirectToAccount); + } + + return nextTokens.push({ + ...prevToken, + value + }); + }; + + const prevEthToken = prevTokens.find((tok) => tok.token.native); + const nextEthToken = tokens.find((tok) => tok.token.native); + + handleToken(prevEthToken, nextEthToken); + + tokensAddresses + .forEach((address) => { + const prevToken = prevTokens.find((tok) => tok.token.address === address); + const nextToken = tokens.find((tok) => tok.token.address === address); + + handleToken(prevToken, nextToken); }); balances[address] = { txCount: txCount || new BigNumber(0), tokens: nextTokens }; @@ -176,6 +185,8 @@ export function fetchTokens (_tokenIds, options = {}) { return Promise .all(tokenIds.map((id) => fetchTokenInfo(tokenreg, id, api))) + // FIXME ; shouldn't have to filter out tokens... + .then((tokens) => tokens.filter((token) => token.tag && token.tag.toLowerCase() !== 'eth')) .then((tokens) => { // dispatch only the changed images tokens diff --git a/js/src/ui/Balance/balance.js b/js/src/ui/Balance/balance.js index 2010bdc78..d4c624b6a 100644 --- a/js/src/ui/Balance/balance.js +++ b/js/src/ui/Balance/balance.js @@ -41,7 +41,7 @@ class Balance extends Component { let body = (balance.tokens || []) .filter((balance) => new BigNumber(balance.value).gt(0)) - .map((balance) => { + .map((balance, index) => { const token = balance.token; let value; @@ -76,7 +76,8 @@ class Balance extends Component { return (
+ key={ `${index}_${token.tag}` } + > { diff --git a/js/src/ui/GasPriceEditor/store.js b/js/src/ui/GasPriceEditor/store.js index bb8373854..87b275637 100644 --- a/js/src/ui/GasPriceEditor/store.js +++ b/js/src/ui/GasPriceEditor/store.js @@ -124,7 +124,12 @@ export default class GasPriceEditor { @action loadDefaults () { Promise .all([ - this._api.parity.gasPriceHistogram(), + // NOTE fetching histogram may fail if there is not enough data. + // We fallback to empty histogram. + this._api.parity.gasPriceHistogram().catch(() => ({ + bucket_bounds: [], + counts: [] + })), this._api.eth.gasPrice() ]) .then(([histogram, _price]) => { diff --git a/js/src/ui/GasPriceEditor/store.spec.js b/js/src/ui/GasPriceEditor/store.spec.js index 3dcf2e22c..cee8a4d4b 100644 --- a/js/src/ui/GasPriceEditor/store.spec.js +++ b/js/src/ui/GasPriceEditor/store.spec.js @@ -62,6 +62,31 @@ describe('ui/GasPriceEditor/store', () => { }); }); + describe('constructor (defaults) when histogram not available', () => { + const api = { + eth: { + gasPrice: sinon.stub().resolves(GASPRICE) + }, + parity: { + gasPriceHistogram: sinon.stub().rejects('Data not available') + } + }; + + beforeEach(() => { + store = new Store(api, { gasLimit: GASLIMIT }); + }); + + it('retrieves the histogram and gasPrice', done => { + expect(api.eth.gasPrice).to.have.been.called; + expect(api.parity.gasPriceHistogram).to.have.been.called; + + setImmediate(() => { + expect(store.histogram).not.to.be.null; + done(); + }); + }); + }); + describe('setters', () => { beforeEach(() => { store = new Store(null, { gasLimit: GASLIMIT }); diff --git a/js/src/util/web3.extensions.js b/js/src/util/web3.extensions.js index f48aa6c25..461b9b813 100644 --- a/js/src/util/web3.extensions.js +++ b/js/src/util/web3.extensions.js @@ -21,8 +21,8 @@ export default function web3extensions (web3) { property: 'personal', methods: [ new Method({ - name: 'signAndSendTransaction', - call: 'personal_signAndSendTransaction', + name: 'sendTransaction', + call: 'personal_sendTransaction', params: 2, inputFormatter: [formatters.inputTransactionFormatter, null] }), diff --git a/js/src/views/Status/data/rpc.json b/js/src/views/Status/data/rpc.json index ebb2800e8..ec85b7c5b 100644 --- a/js/src/views/Status/data/rpc.json +++ b/js/src/views/Status/data/rpc.json @@ -829,7 +829,7 @@ "outputFormatter": null }, { - "name": "personal_signAndSendTransaction", + "name": "personal_sendTransaction", "desc": "Sends and signs a transaction given account passphrase. Does not require the account to be unlocked nor unlocks the account for future transactions. ", "params": [ { diff --git a/mac/Parity.pkgproj b/mac/Parity.pkgproj index 3cd4199a5..3a319c4dc 100755 --- a/mac/Parity.pkgproj +++ b/mac/Parity.pkgproj @@ -578,7 +578,7 @@ OVERWRITE_PERMISSIONS VERSION - 1.5.0 + 1.5.1 UUID 2DCD5B81-7BAF-4DA1-9251-6274B089FD36 diff --git a/nsis/installer.nsi b/nsis/installer.nsi index 685b4a936..271f6003b 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -10,7 +10,7 @@ !define DESCRIPTION "Fast, light, robust Ethereum implementation" !define VERSIONMAJOR 1 !define VERSIONMINOR 5 -!define VERSIONBUILD 0 +!define VERSIONBUILD 1 !define ARGS "--warp" !define FIRST_START_ARGS "ui --warp --mode=passive" diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 67cba6a48..eb8822358 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -90,6 +90,7 @@ tx_time_limit = 100 #ms extra_data = "Parity" remove_solved = false notify_work = ["http://localhost:3001"] +refuse_service_transactions = false [footprint] tracing = "auto" diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 0802cb67f..a91aa4bc7 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -230,6 +230,8 @@ usage! { or |c: &Config| otry!(c.mining).remove_solved.clone(), flag_notify_work: Option = None, or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))), + flag_refuse_service_transactions: bool = false, + or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(), // -- Footprint Options flag_tracing: String = "auto", @@ -414,6 +416,7 @@ struct Mining { tx_queue_ban_time: Option, remove_solved: Option, notify_work: Option>, + refuse_service_transactions: Option, } #[derive(Default, Debug, PartialEq, RustcDecodable)] @@ -630,6 +633,7 @@ mod tests { flag_tx_queue_ban_time: 180u16, flag_remove_solved: false, flag_notify_work: Some("http://localhost:3001".into()), + flag_refuse_service_transactions: false, // -- Footprint Options flag_tracing: "auto".into(), @@ -807,6 +811,7 @@ mod tests { extra_data: None, remove_solved: None, notify_work: None, + refuse_service_transactions: None, }), footprint: Some(Footprint { tracing: Some("on".into()), diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index f75dda524..e2e79dd59 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -22,357 +22,359 @@ Usage: parity db kill [options] Operating Options: - --mode MODE Set the operating mode. MODE can be one of: - last - Uses the last-used mode, active if none. - active - Parity continuously syncs the chain. - passive - Parity syncs initially, then sleeps and - wakes regularly to resync. - dark - Parity syncs only when the RPC is active. - offline - Parity doesn't sync. (default: {flag_mode}). - --mode-timeout SECS Specify the number of seconds before inactivity - timeout occurs when mode is dark or passive - (default: {flag_mode_timeout}). - --mode-alarm SECS Specify the number of seconds before auto sleep - reawake timeout occurs when mode is passive - (default: {flag_mode_alarm}). - --auto-update SET Set a releases set to automatically update and - install. - all - All updates in the our release track. - critical - Only consensus/security updates. - none - No updates will be auto-installed. - (default: {flag_auto_update}). - --release-track TRACK Set which release track we should use for updates. - stable - Stable releases. - beta - Beta releases. - nightly - Nightly releases (unstable). - testing - Testing releases (do not use). - current - Whatever track this executable was - released on (default: {flag_release_track}). - --no-download Normally new releases will be downloaded ready for - updating. This disables it. Not recommended. - (default: {flag_no_download}). - --no-consensus Force the binary to run even if there are known - issues regarding consensus. Not recommended. - (default: {flag_no_consensus}). - --force-direct Run the originally installed version of Parity, - ignoring any updates that have since been installed. - --chain CHAIN Specify the blockchain type. CHAIN may be either a - JSON chain specification file or olympic, frontier, - homestead, mainnet, morden, ropsten, classic, expanse, - testnet or dev (default: {flag_chain}). - -d --base-path PATH Specify the base data storage path. - (default: {flag_base_path}). - --db-path PATH Specify the database directory path - (default: {flag_db_path}). - --keys-path PATH Specify the path for JSON key files to be found - (default: {flag_keys_path}). - --identity NAME Specify your node's name. (default: {flag_identity}) + --mode MODE Set the operating mode. MODE can be one of: + last - Uses the last-used mode, active if none. + active - Parity continuously syncs the chain. + passive - Parity syncs initially, then sleeps and + wakes regularly to resync. + dark - Parity syncs only when the RPC is active. + offline - Parity doesn't sync. (default: {flag_mode}). + --mode-timeout SECS Specify the number of seconds before inactivity + timeout occurs when mode is dark or passive + (default: {flag_mode_timeout}). + --mode-alarm SECS Specify the number of seconds before auto sleep + reawake timeout occurs when mode is passive + (default: {flag_mode_alarm}). + --auto-update SET Set a releases set to automatically update and + install. + all - All updates in the our release track. + critical - Only consensus/security updates. + none - No updates will be auto-installed. + (default: {flag_auto_update}). + --release-track TRACK Set which release track we should use for updates. + stable - Stable releases. + beta - Beta releases. + nightly - Nightly releases (unstable). + testing - Testing releases (do not use). + current - Whatever track this executable was + released on (default: {flag_release_track}). + --no-download Normally new releases will be downloaded ready for + updating. This disables it. Not recommended. + (default: {flag_no_download}). + --no-consensus Force the binary to run even if there are known + issues regarding consensus. Not recommended. + (default: {flag_no_consensus}). + --force-direct Run the originally installed version of Parity, + ignoring any updates that have since been installed. + --chain CHAIN Specify the blockchain type. CHAIN may be either a + JSON chain specification file or olympic, frontier, + homestead, mainnet, morden, ropsten, classic, expanse, + testnet or dev (default: {flag_chain}). + -d --base-path PATH Specify the base data storage path. + (default: {flag_base_path}). + --db-path PATH Specify the database directory path + (default: {flag_db_path}). + --keys-path PATH Specify the path for JSON key files to be found + (default: {flag_keys_path}). + --identity NAME Specify your node's name. (default: {flag_identity}) Account Options: - --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. - ACCOUNTS is a comma-delimited list of addresses. - Implies --no-ui. (default: {flag_unlock:?}) - --password FILE Provide a file containing a password for unlocking - an account. Leading and trailing whitespace is trimmed. - (default: {flag_password:?}) - --keys-iterations NUM Specify the number of iterations to use when - deriving key from the password (bigger is more - secure) (default: {flag_keys_iterations}). + --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. + ACCOUNTS is a comma-delimited list of addresses. + Implies --no-ui. (default: {flag_unlock:?}) + --password FILE Provide a file containing a password for unlocking + an account. Leading and trailing whitespace is trimmed. + (default: {flag_password:?}) + --keys-iterations NUM Specify the number of iterations to use when + deriving key from the password (bigger is more + secure) (default: {flag_keys_iterations}). UI Options: - --force-ui Enable Trusted UI WebSocket endpoint, - even when --unlock is in use. (default: ${flag_force_ui}) - --no-ui Disable Trusted UI WebSocket endpoint. - (default: ${flag_no_ui}) - --ui-port PORT Specify the port of Trusted UI server - (default: {flag_ui_port}). - --ui-interface IP Specify the hostname portion of the Trusted UI - server, IP should be an interface's IP address, - or local (default: {flag_ui_interface}). - --ui-path PATH Specify directory where Trusted UIs tokens should - be stored. (default: {flag_ui_path}) - --ui-no-validation Disable Origin and Host headers validation for - Trusted UI. WARNING: INSECURE. Used only for - development. (default: {flag_ui_no_validation}) + --force-ui Enable Trusted UI WebSocket endpoint, + even when --unlock is in use. (default: ${flag_force_ui}) + --no-ui Disable Trusted UI WebSocket endpoint. + (default: ${flag_no_ui}) + --ui-port PORT Specify the port of Trusted UI server + (default: {flag_ui_port}). + --ui-interface IP Specify the hostname portion of the Trusted UI + server, IP should be an interface's IP address, + or local (default: {flag_ui_interface}). + --ui-path PATH Specify directory where Trusted UIs tokens should + be stored. (default: {flag_ui_path}) + --ui-no-validation Disable Origin and Host headers validation for + Trusted UI. WARNING: INSECURE. Used only for + development. (default: {flag_ui_no_validation}) Networking Options: - --warp Enable syncing from the snapshot over the network. (default: {flag_warp}) - --port PORT Override the port on which the node should listen - (default: {flag_port}). - --min-peers NUM Try to maintain at least NUM peers (default: {flag_min_peers}). - --max-peers NUM Allow up to NUM peers (default: {flag_max_peers}). - --snapshot-peers NUM Allow additional NUM peers for a snapshot sync - (default: {flag_snapshot_peers}). - --nat METHOD Specify method to use for determining public - address. Must be one of: any, none, upnp, - extip: (default: {flag_nat}). - --network-id INDEX Override the network identifier from the chain we - are on. (default: {flag_network_id:?}) - --bootnodes NODES Override the bootnodes from our chain. NODES should - be comma-delimited enodes. (default: {flag_bootnodes:?}) - --no-discovery Disable new peer discovery. (default: {flag_no_discovery}) - --node-key KEY Specify node secret key, either as 64-character hex - string or input to SHA3 operation. (default: {flag_node_key:?}) - --reserved-peers FILE Provide a file containing enodes, one per line. - These nodes will always have a reserved slot on top - of the normal maximum peers. (default: {flag_reserved_peers:?}) - --reserved-only Connect only to reserved nodes. (default: {flag_reserved_only}) - --allow-ips FILTER Filter outbound connections. Must be one of: - private - connect to private network IP addresses only; - public - connect to public network IP addresses only; - all - connect to any IP address. - (default: {flag_allow_ips}) - --max-pending-peers NUM Allow up to NUM pending connections. (default: {flag_max_pending_peers}) - --no-ancient-blocks Disable downloading old blocks after snapshot restoration - or warp sync. (default: {flag_no_ancient_blocks}) + --warp Enable syncing from the snapshot over the network. (default: {flag_warp}) + --port PORT Override the port on which the node should listen + (default: {flag_port}). + --min-peers NUM Try to maintain at least NUM peers (default: {flag_min_peers}). + --max-peers NUM Allow up to NUM peers (default: {flag_max_peers}). + --snapshot-peers NUM Allow additional NUM peers for a snapshot sync + (default: {flag_snapshot_peers}). + --nat METHOD Specify method to use for determining public + address. Must be one of: any, none, upnp, + extip: (default: {flag_nat}). + --network-id INDEX Override the network identifier from the chain we + are on. (default: {flag_network_id:?}) + --bootnodes NODES Override the bootnodes from our chain. NODES should + be comma-delimited enodes. (default: {flag_bootnodes:?}) + --no-discovery Disable new peer discovery. (default: {flag_no_discovery}) + --node-key KEY Specify node secret key, either as 64-character hex + string or input to SHA3 operation. (default: {flag_node_key:?}) + --reserved-peers FILE Provide a file containing enodes, one per line. + These nodes will always have a reserved slot on top + of the normal maximum peers. (default: {flag_reserved_peers:?}) + --reserved-only Connect only to reserved nodes. (default: {flag_reserved_only}) + --allow-ips FILTER Filter outbound connections. Must be one of: + private - connect to private network IP addresses only; + public - connect to public network IP addresses only; + all - connect to any IP address. + (default: {flag_allow_ips}) + --max-pending-peers NUM Allow up to NUM pending connections. (default: {flag_max_pending_peers}) + --no-ancient-blocks Disable downloading old blocks after snapshot restoration + or warp sync. (default: {flag_no_ancient_blocks}) API and Console Options: - --no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc}) - --jsonrpc-port PORT Specify the port portion of the JSONRPC API server - (default: {flag_jsonrpc_port}). - --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API - server, IP should be an interface's IP address, or - all (all interfaces) or local (default: {flag_jsonrpc_interface}). - --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses. - (default: {flag_jsonrpc_cors:?}) - --jsonrpc-apis APIS Specify the APIs available through the JSONRPC - interface. APIS is a comma-delimited list of API - name. Possible name are web3, eth, net, personal, - parity, parity_set, traces, rpc, parity_accounts. - (default: {flag_jsonrpc_apis}). - --jsonrpc-hosts HOSTS List of allowed Host header values. This option will - validate the Host header sent by the browser, it - is additional security against some attack - vectors. Special options: "all", "none", - (default: {flag_jsonrpc_hosts}). + --no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc}) + --jsonrpc-port PORT Specify the port portion of the JSONRPC API server + (default: {flag_jsonrpc_port}). + --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API + server, IP should be an interface's IP address, or + all (all interfaces) or local (default: {flag_jsonrpc_interface}). + --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses. + (default: {flag_jsonrpc_cors:?}) + --jsonrpc-apis APIS Specify the APIs available through the JSONRPC + interface. APIS is a comma-delimited list of API + name. Possible name are web3, eth, net, personal, + parity, parity_set, traces, rpc, parity_accounts. + (default: {flag_jsonrpc_apis}). + --jsonrpc-hosts HOSTS List of allowed Host header values. This option will + validate the Host header sent by the browser, it + is additional security against some attack + vectors. Special options: "all", "none", + (default: {flag_jsonrpc_hosts}). - --no-ipc Disable JSON-RPC over IPC service. (default: {flag_no_ipc}) - --ipc-path PATH Specify custom path for JSON-RPC over IPC service - (default: {flag_ipc_path}). - --ipc-apis APIS Specify custom API set available via JSON-RPC over - IPC (default: {flag_ipc_apis}). + --no-ipc Disable JSON-RPC over IPC service. (default: {flag_no_ipc}) + --ipc-path PATH Specify custom path for JSON-RPC over IPC service + (default: {flag_ipc_path}). + --ipc-apis APIS Specify custom API set available via JSON-RPC over + IPC (default: {flag_ipc_apis}). - --no-dapps Disable the Dapps server (e.g. status page). (default: {flag_no_dapps}) - --dapps-port PORT Specify the port portion of the Dapps server - (default: {flag_dapps_port}). - --dapps-interface IP Specify the hostname portion of the Dapps - server, IP should be an interface's IP address, - or local (default: {flag_dapps_interface}). - --dapps-hosts HOSTS List of allowed Host header values. This option will - validate the Host header sent by the browser, it - is additional security against some attack - vectors. Special options: "all", "none", - (default: {flag_dapps_hosts}). - --dapps-user USERNAME Specify username for Dapps server. It will be - used in HTTP Basic Authentication Scheme. - If --dapps-pass is not specified you will be - asked for password on startup. (default: {flag_dapps_user:?}) - --dapps-pass PASSWORD Specify password for Dapps server. Use only in - conjunction with --dapps-user. (default: {flag_dapps_pass:?}) - --dapps-path PATH Specify directory where dapps should be installed. - (default: {flag_dapps_path}) + --no-dapps Disable the Dapps server (e.g. status page). (default: {flag_no_dapps}) + --dapps-port PORT Specify the port portion of the Dapps server + (default: {flag_dapps_port}). + --dapps-interface IP Specify the hostname portion of the Dapps + server, IP should be an interface's IP address, + or local (default: {flag_dapps_interface}). + --dapps-hosts HOSTS List of allowed Host header values. This option will + validate the Host header sent by the browser, it + is additional security against some attack + vectors. Special options: "all", "none", + (default: {flag_dapps_hosts}). + --dapps-user USERNAME Specify username for Dapps server. It will be + used in HTTP Basic Authentication Scheme. + If --dapps-pass is not specified you will be + asked for password on startup. (default: {flag_dapps_user:?}) + --dapps-pass PASSWORD Specify password for Dapps server. Use only in + conjunction with --dapps-user. (default: {flag_dapps_pass:?}) + --dapps-path PATH Specify directory where dapps should be installed. + (default: {flag_dapps_path}) Sealing/Mining Options: - --author ADDRESS Specify the block author (aka "coinbase") address - for sending block rewards from sealed blocks. - NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. - (default: {flag_author:?}) - --engine-signer ADDRESS Specify the address which should be used to - sign consensus messages and issue blocks. - Relevant only to non-PoW chains. - (default: {flag_engine_signer:?}) - --force-sealing Force the node to author new blocks as if it were - always sealing/mining. - (default: {flag_force_sealing}) - --reseal-on-txs SET Specify which transactions should force the node - to reseal a block. SET is one of: - none - never reseal on new transactions; - own - reseal only on a new local transaction; - ext - reseal only on a new external transaction; - all - reseal on all new transactions - (default: {flag_reseal_on_txs}). - --reseal-min-period MS Specify the minimum time between reseals from - incoming transactions. MS is time measured in - milliseconds (default: {flag_reseal_min_period}). - --work-queue-size ITEMS Specify the number of historical work packages - which are kept cached lest a solution is found for - them later. High values take more memory but result - in fewer unusable solutions (default: {flag_work_queue_size}). - --tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas - a single transaction may have for it to be mined. - (default: {flag_tx_gas_limit:?}) - --tx-time-limit MS Maximal time for processing single transaction. - If enabled senders/recipients/code of transactions - offending the limit will be banned from being included - in transaction queue for 180 seconds. - (default: {flag_tx_time_limit:?}) - --relay-set SET Set of transactions to relay. SET may be: - cheap - Relay any transaction in the queue (this - may include invalid transactions); - strict - Relay only executed transactions (this - guarantees we don't relay invalid transactions, but - means we relay nothing if not mining); - lenient - Same as strict when mining, and cheap - when not (default: {flag_relay_set}). - --usd-per-tx USD Amount of USD to be paid for a basic transaction - (default: {flag_usd_per_tx}). The minimum gas price is set - accordingly. - --usd-per-eth SOURCE USD value of a single ETH. SOURCE may be either an - amount in USD, a web service or 'auto' to use each - web service in turn and fallback on the last known - good value (default: {flag_usd_per_eth}). - --price-update-period T T will be allowed to pass between each gas price - update. T may be daily, hourly, a number of seconds, - or a time string of the form "2 days", "30 minutes" - etc. (default: {flag_price_update_period}). - --gas-floor-target GAS Amount of gas per block to target when sealing a new - block (default: {flag_gas_floor_target}). - --gas-cap GAS A cap on how large we will raise the gas limit per - block due to transaction volume (default: {flag_gas_cap}). - --extra-data STRING Specify a custom extra-data for authored blocks, no - more than 32 characters. (default: {flag_extra_data:?}) - --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting - to be included in next block) (default: {flag_tx_queue_size}). - --tx-queue-gas LIMIT Maximum amount of total gas for external transactions in - the queue. LIMIT can be either an amount of gas or - 'auto' or 'off'. 'auto' sets the limit to be 20x - the current block gas limit. (default: {flag_tx_queue_gas}). - --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: {flag_tx_queue_strategy}). - --tx-queue-ban-count C Number of times maximal time for execution (--tx-time-limit) - can be exceeded before banning sender/recipient/code. - (default: {flag_tx_queue_ban_count}) - --tx-queue-ban-time SEC Banning time (in seconds) for offenders of specified - execution time limit. Also number of offending actions - have to reach the threshold within that time. - (default: {flag_tx_queue_ban_time} seconds) - --remove-solved Move solved blocks from the work package queue - instead of cloning them. This gives a slightly - faster import speed, but means that extra solutions - submitted for the same work package will go unused. - (default: {flag_remove_solved}) - --notify-work URLS URLs to which work package notifications are pushed. - URLS should be a comma-delimited list of HTTP URLs. - (default: {flag_notify_work:?}) + --author ADDRESS Specify the block author (aka "coinbase") address + for sending block rewards from sealed blocks. + NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. + (default: {flag_author:?}) + --engine-signer ADDRESS Specify the address which should be used to + sign consensus messages and issue blocks. + Relevant only to non-PoW chains. + (default: {flag_engine_signer:?}) + --force-sealing Force the node to author new blocks as if it were + always sealing/mining. + (default: {flag_force_sealing}) + --reseal-on-txs SET Specify which transactions should force the node + to reseal a block. SET is one of: + none - never reseal on new transactions; + own - reseal only on a new local transaction; + ext - reseal only on a new external transaction; + all - reseal on all new transactions + (default: {flag_reseal_on_txs}). + --reseal-min-period MS Specify the minimum time between reseals from + incoming transactions. MS is time measured in + milliseconds (default: {flag_reseal_min_period}). + --work-queue-size ITEMS Specify the number of historical work packages + which are kept cached lest a solution is found for + them later. High values take more memory but result + in fewer unusable solutions (default: {flag_work_queue_size}). + --tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas + a single transaction may have for it to be mined. + (default: {flag_tx_gas_limit:?}) + --tx-time-limit MS Maximal time for processing single transaction. + If enabled senders/recipients/code of transactions + offending the limit will be banned from being included + in transaction queue for 180 seconds. + (default: {flag_tx_time_limit:?}) + --relay-set SET Set of transactions to relay. SET may be: + cheap - Relay any transaction in the queue (this + may include invalid transactions); + strict - Relay only executed transactions (this + guarantees we don't relay invalid transactions, but + means we relay nothing if not mining); + lenient - Same as strict when mining, and cheap + when not (default: {flag_relay_set}). + --usd-per-tx USD Amount of USD to be paid for a basic transaction + (default: {flag_usd_per_tx}). The minimum gas price is set + accordingly. + --usd-per-eth SOURCE USD value of a single ETH. SOURCE may be either an + amount in USD, a web service or 'auto' to use each + web service in turn and fallback on the last known + good value (default: {flag_usd_per_eth}). + --price-update-period T T will be allowed to pass between each gas price + update. T may be daily, hourly, a number of seconds, + or a time string of the form "2 days", "30 minutes" + etc. (default: {flag_price_update_period}). + --gas-floor-target GAS Amount of gas per block to target when sealing a new + block (default: {flag_gas_floor_target}). + --gas-cap GAS A cap on how large we will raise the gas limit per + block due to transaction volume (default: {flag_gas_cap}). + --extra-data STRING Specify a custom extra-data for authored blocks, no + more than 32 characters. (default: {flag_extra_data:?}) + --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting + to be included in next block) (default: {flag_tx_queue_size}). + --tx-queue-gas LIMIT Maximum amount of total gas for external transactions in + the queue. LIMIT can be either an amount of gas or + 'auto' or 'off'. 'auto' sets the limit to be 20x + the current block gas limit. (default: {flag_tx_queue_gas}). + --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: {flag_tx_queue_strategy}). + --tx-queue-ban-count C Number of times maximal time for execution (--tx-time-limit) + can be exceeded before banning sender/recipient/code. + (default: {flag_tx_queue_ban_count}) + --tx-queue-ban-time SEC Banning time (in seconds) for offenders of specified + execution time limit. Also number of offending actions + have to reach the threshold within that time. + (default: {flag_tx_queue_ban_time} seconds) + --remove-solved Move solved blocks from the work package queue + instead of cloning them. This gives a slightly + faster import speed, but means that extra solutions + submitted for the same work package will go unused. + (default: {flag_remove_solved}) + --notify-work URLS URLs to which work package notifications are pushed. + URLS should be a comma-delimited list of HTTP URLs. + (default: {flag_notify_work:?}) + --refuse-service-transactions Always refuse service transactions. + (default: {flag_refuse_service_transactions}). Footprint Options: - --tracing BOOL Indicates if full transaction tracing should be - enabled. Works only if client had been fully synced - with tracing enabled. BOOL may be one of auto, on, - off. auto uses last used value of this option (off - if it does not exist) (default: {flag_tracing}). - --pruning METHOD Configure pruning of the state/storage trie. METHOD - may be one of auto, archive, fast: - archive - keep all state trie data. No pruning. - fast - maintain journal overlay. Fast but 50MB used. - auto - use the method most recently synced or - default to fast if none synced (default: {flag_pruning}). - --pruning-history NUM Set a number of recent states to keep when pruning - is active. (default: {flag_pruning_history}). - --cache-size-db MB Override database cache size (default: {flag_cache_size_db}). - --cache-size-blocks MB Specify the prefered size of the blockchain cache in - megabytes (default: {flag_cache_size_blocks}). - --cache-size-queue MB Specify the maximum size of memory to use for block - queue (default: {flag_cache_size_queue}). - --cache-size-state MB Specify the maximum size of memory to use for - the state cache (default: {flag_cache_size_state}). - --cache-size MB Set total amount of discretionary memory to use for - the entire system, overrides other cache and queue - options. (default: {flag_cache_size:?}) - --fast-and-loose Disables DB WAL, which gives a significant speed up - but means an unclean exit is unrecoverable. (default: {flag_fast_and_loose}) - --db-compaction TYPE Database compaction type. TYPE may be one of: - ssd - suitable for SSDs and fast HDDs; - hdd - suitable for slow HDDs; - auto - determine automatically (default: {flag_db_compaction}). - --fat-db BOOL Build appropriate information to allow enumeration - of all accounts and storage keys. Doubles the size - of the state database. BOOL may be one of on, off - or auto. (default: {flag_fat_db}) - --scale-verifiers Automatically scale amount of verifier threads based on - workload. Not guaranteed to be faster. - (default: {flag_scale_verifiers}) - --num-verifiers INT Amount of verifier threads to use or to begin with, if verifier - auto-scaling is enabled. (default: {flag_num_verifiers:?}) + --tracing BOOL Indicates if full transaction tracing should be + enabled. Works only if client had been fully synced + with tracing enabled. BOOL may be one of auto, on, + off. auto uses last used value of this option (off + if it does not exist) (default: {flag_tracing}). + --pruning METHOD Configure pruning of the state/storage trie. METHOD + may be one of auto, archive, fast: + archive - keep all state trie data. No pruning. + fast - maintain journal overlay. Fast but 50MB used. + auto - use the method most recently synced or + default to fast if none synced (default: {flag_pruning}). + --pruning-history NUM Set a minimum number of recent states to keep when pruning + is active. (default: {flag_pruning_history}). + --cache-size-db MB Override database cache size (default: {flag_cache_size_db}). + --cache-size-blocks MB Specify the prefered size of the blockchain cache in + megabytes (default: {flag_cache_size_blocks}). + --cache-size-queue MB Specify the maximum size of memory to use for block + queue (default: {flag_cache_size_queue}). + --cache-size-state MB Specify the maximum size of memory to use for + the state cache (default: {flag_cache_size_state}). + --cache-size MB Set total amount of discretionary memory to use for + the entire system, overrides other cache and queue + options. (default: {flag_cache_size:?}) + --fast-and-loose Disables DB WAL, which gives a significant speed up + but means an unclean exit is unrecoverable. (default: {flag_fast_and_loose}) + --db-compaction TYPE Database compaction type. TYPE may be one of: + ssd - suitable for SSDs and fast HDDs; + hdd - suitable for slow HDDs; + auto - determine automatically (default: {flag_db_compaction}). + --fat-db BOOL Build appropriate information to allow enumeration + of all accounts and storage keys. Doubles the size + of the state database. BOOL may be one of on, off + or auto. (default: {flag_fat_db}) + --scale-verifiers Automatically scale amount of verifier threads based on + workload. Not guaranteed to be faster. + (default: {flag_scale_verifiers}) + --num-verifiers INT Amount of verifier threads to use or to begin with, if verifier + auto-scaling is enabled. (default: {flag_num_verifiers:?}) Import/Export Options: - --from BLOCK Export from block BLOCK, which may be an index or - hash (default: {flag_from}). - --to BLOCK Export to (including) block BLOCK, which may be an - index, hash or 'latest' (default: {flag_to}). - --format FORMAT For import/export in given format. FORMAT must be - one of 'hex' and 'binary'. - (default: {flag_format:?} = Import: auto, Export: binary) - --no-seal-check Skip block seal check. (default: {flag_no_seal_check}) - --at BLOCK Export state at the given block, which may be an - index, hash, or 'latest'. (default: {flag_at}) - --no-storage Don't export account storage. (default: {flag_no_storage}) - --no-code Don't export account code. (default: {flag_no_code}) - --min-balance WEI Don't export accounts with balance less than specified. - (default: {flag_min_balance:?}) - --max-balance WEI Don't export accounts with balance greater than specified. - (default: {flag_max_balance:?}) + --from BLOCK Export from block BLOCK, which may be an index or + hash (default: {flag_from}). + --to BLOCK Export to (including) block BLOCK, which may be an + index, hash or 'latest' (default: {flag_to}). + --format FORMAT For import/export in given format. FORMAT must be + one of 'hex' and 'binary'. + (default: {flag_format:?} = Import: auto, Export: binary) + --no-seal-check Skip block seal check. (default: {flag_no_seal_check}) + --at BLOCK Export state at the given block, which may be an + index, hash, or 'latest'. (default: {flag_at}) + --no-storage Don't export account storage. (default: {flag_no_storage}) + --no-code Don't export account code. (default: {flag_no_code}) + --min-balance WEI Don't export accounts with balance less than specified. + (default: {flag_min_balance:?}) + --max-balance WEI Don't export accounts with balance greater than specified. + (default: {flag_max_balance:?}) Snapshot Options: - --at BLOCK Take a snapshot at the given block, which may be an - index, hash, or 'latest'. Note that taking snapshots at - non-recent blocks will only work with --pruning archive - (default: {flag_at}) - --no-periodic-snapshot Disable automated snapshots which usually occur once - every 10000 blocks. (default: {flag_no_periodic_snapshot}) + --at BLOCK Take a snapshot at the given block, which may be an + index, hash, or 'latest'. Note that taking snapshots at + non-recent blocks will only work with --pruning archive + (default: {flag_at}) + --no-periodic-snapshot Disable automated snapshots which usually occur once + every 10000 blocks. (default: {flag_no_periodic_snapshot}) Virtual Machine Options: - --jitvm Enable the JIT VM. (default: {flag_jitvm}) + --jitvm Enable the JIT VM. (default: {flag_jitvm}) Legacy Options: - --geth Run in Geth-compatibility mode. Sets the IPC path - to be the same as Geth's. Overrides the --ipc-path - and --ipcpath options. Alters RPCs to reflect Geth - bugs. Includes the personal_ RPC by default. - --testnet Geth-compatible testnet mode. Equivalent to --chain - testnet --keys-path $HOME/parity/testnet-keys. - Overrides the --keys-path option. - --import-geth-keys Attempt to import keys from Geth client. - --datadir PATH Equivalent to --base-path PATH. - --networkid INDEX Equivalent to --network-id INDEX. - --peers NUM Equivalent to --min-peers NUM. - --nodekey KEY Equivalent to --node-key KEY. - --nodiscover Equivalent to --no-discovery. - -j --jsonrpc Does nothing; JSON-RPC is on by default now. - --jsonrpc-off Equivalent to --no-jsonrpc. - -w --webapp Does nothing; dapps server is on by default now. - --dapps-off Equivalent to --no-dapps. - --rpc Does nothing; JSON-RPC is on by default now. - --rpcaddr IP Equivalent to --jsonrpc-interface IP. - --rpcport PORT Equivalent to --jsonrpc-port PORT. - --rpcapi APIS Equivalent to --jsonrpc-apis APIS. - --rpccorsdomain URL Equivalent to --jsonrpc-cors URL. - --ipcdisable Equivalent to --no-ipc. - --ipc-off Equivalent to --no-ipc. - --ipcapi APIS Equivalent to --ipc-apis APIS. - --ipcpath PATH Equivalent to --ipc-path PATH. - --gasprice WEI Minimum amount of Wei per GAS to be paid for a - transaction to be accepted for mining. Overrides - --basic-tx-usd. - --etherbase ADDRESS Equivalent to --author ADDRESS. - --extradata STRING Equivalent to --extra-data STRING. - --cache MB Equivalent to --cache-size MB. + --geth Run in Geth-compatibility mode. Sets the IPC path + to be the same as Geth's. Overrides the --ipc-path + and --ipcpath options. Alters RPCs to reflect Geth + bugs. Includes the personal_ RPC by default. + --testnet Geth-compatible testnet mode. Equivalent to --chain + testnet --keys-path $HOME/parity/testnet-keys. + Overrides the --keys-path option. + --import-geth-keys Attempt to import keys from Geth client. + --datadir PATH Equivalent to --base-path PATH. + --networkid INDEX Equivalent to --network-id INDEX. + --peers NUM Equivalent to --min-peers NUM. + --nodekey KEY Equivalent to --node-key KEY. + --nodiscover Equivalent to --no-discovery. + -j --jsonrpc Does nothing; JSON-RPC is on by default now. + --jsonrpc-off Equivalent to --no-jsonrpc. + -w --webapp Does nothing; dapps server is on by default now. + --dapps-off Equivalent to --no-dapps. + --rpc Does nothing; JSON-RPC is on by default now. + --rpcaddr IP Equivalent to --jsonrpc-interface IP. + --rpcport PORT Equivalent to --jsonrpc-port PORT. + --rpcapi APIS Equivalent to --jsonrpc-apis APIS. + --rpccorsdomain URL Equivalent to --jsonrpc-cors URL. + --ipcdisable Equivalent to --no-ipc. + --ipc-off Equivalent to --no-ipc. + --ipcapi APIS Equivalent to --ipc-apis APIS. + --ipcpath PATH Equivalent to --ipc-path PATH. + --gasprice WEI Minimum amount of Wei per GAS to be paid for a + transaction to be accepted for mining. Overrides + --basic-tx-usd. + --etherbase ADDRESS Equivalent to --author ADDRESS. + --extradata STRING Equivalent to --extra-data STRING. + --cache MB Equivalent to --cache-size MB. Internal Options: - --can-restart Executable will auto-restart if exiting with 69. + --can-restart Executable will auto-restart if exiting with 69. Miscellaneous Options: - -c --config CONFIG Specify a filename containing a configuration file. - (default: {flag_config}) - -l --logging LOGGING Specify the logging level. Must conform to the same - format as RUST_LOG. (default: {flag_logging:?}) - --log-file FILENAME Specify a filename into which logging should be - appended. (default: {flag_log_file:?}) - --no-config Don't load a configuration file. - --no-color Don't use terminal color codes in output. (default: {flag_no_color}) - -v --version Show information about version. - -h --help Show this screen. + -c --config CONFIG Specify a filename containing a configuration file. + (default: {flag_config}) + -l --logging LOGGING Specify the logging level. Must conform to the same + format as RUST_LOG. (default: {flag_logging:?}) + --log-file FILENAME Specify a filename into which logging should be + appended. (default: {flag_log_file:?}) + --no-config Don't load a configuration file. + --no-color Don't use terminal color codes in output. (default: {flag_no_color}) + -v --version Show information about version. + -h --help Show this screen. diff --git a/parity/configuration.rs b/parity/configuration.rs index 6d23579c1..b8c23341d 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -485,7 +485,8 @@ impl Configuration { ban_duration: Duration::from_secs(self.args.flag_tx_queue_ban_time as u64), }, None => Banning::Disabled, - } + }, + refuse_service_transactions: self.args.flag_refuse_service_transactions, }; Ok(options) diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 58b3740cf..4120522ba 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -54,9 +54,6 @@ pub enum QueueAddError { LimitReached, } -/// Message Receiver type -pub type QueueEventReceiver = mpsc::Receiver; - // TODO [todr] to consider: timeout instead of limit? const QUEUE_LIMIT: usize = 50; diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 5033c06e0..a877879e7 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -102,7 +102,7 @@ impl Personal for PersonalClient where C: MiningBl } } - fn sign_and_send_transaction(&self, request: TransactionRequest, password: String) -> Result { + fn send_transaction(&self, request: TransactionRequest, password: String) -> Result { self.active()?; let client = take_weak!(self.client); let miner = take_weak!(self.miner); diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 27e25b64e..b1346391e 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -66,6 +66,7 @@ fn miner_service(spec: &Spec, accounts: Arc) -> Arc { reseal_min_period: Duration::from_secs(0), work_queue_size: 50, enable_resubmission: true, + refuse_service_transactions: false, }, GasPricer::new_fixed(20_000_000_000u64.into()), &spec, diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index c2984240f..cc8372004 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -96,7 +96,7 @@ fn sign_and_send_transaction_with_invalid_password() { let address = tester.accounts.new_account("password123").unwrap(); let request = r#"{ "jsonrpc": "2.0", - "method": "personal_signAndSendTransaction", + "method": "personal_sendTransaction", "params": [{ "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", @@ -119,7 +119,7 @@ fn sign_and_send_transaction() { let request = r#"{ "jsonrpc": "2.0", - "method": "personal_signAndSendTransaction", + "method": "personal_sendTransaction", "params": [{ "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index a7cc996ea..ca9871f57 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -36,7 +36,7 @@ build_rpc_trait! { fn unlock_account(&self, H160, String, Option) -> Result; /// Sends transaction and signs it in single call. The account is not unlocked in such case. - #[rpc(name = "personal_signAndSendTransaction")] - fn sign_and_send_transaction(&self, TransactionRequest, String) -> Result; + #[rpc(name = "personal_sendTransaction")] + fn send_transaction(&self, TransactionRequest, String) -> Result; } } diff --git a/scripts/contractABI.js b/scripts/contractABI.js index 9a8225ccd..a779ad0a2 100644 --- a/scripts/contractABI.js +++ b/scripts/contractABI.js @@ -17,6 +17,8 @@ // Rust/Parity ABI struct autogenerator. // By Gav Wood, 2016. +var fs = require('fs'); + String.prototype.replaceAll = function(f, t) { return this.split(f).join(t); } String.prototype.toSnake = function(){ return this.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();}); @@ -24,6 +26,7 @@ String.prototype.toSnake = function(){ function makeContractFile(name, json, prefs) { return `// Autogenerated from JSON contract definition using Rust contract convertor. +// Command line: ${process.argv.slice(2).join(' ')} #![allow(unused_imports)] use std::string::String; use std::result::Result; @@ -39,14 +42,15 @@ function convertContract(name, json, prefs) { return `${prefs._pub ? "pub " : ""}struct ${name} { contract: ethabi::Contract, address: util::Address, - do_call: Box) -> Result, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static>, + ${prefs._explicit_do_call ? "" : `do_call: Box) -> Result, String> + Send${prefs._sync ? " + Sync " : ""}+ 'static>,`} } impl ${name} { - pub fn new(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec) -> Result, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static { + pub fn new${prefs._explicit_do_call ? "" : ""}(address: util::Address${prefs._explicit_do_call ? "" : `", do_call: F"`}) -> Self + ${prefs._explicit_do_call ? "" : `where F: Fn(util::Address, Vec) -> Result, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static`} { ${name} { contract: ethabi::Contract::new(ethabi::Interface::load(b"${JSON.stringify(json.filter(a => a.type == 'function')).replaceAll('"', '\\"')}").expect("JSON is autogenerated; qed")), address: address, - do_call: Box::new(do_call), + ${prefs._explicit_do_call ? "" : `do_call: Box::new(do_call),`} } } fn as_string(e: T) -> String { format!("{:?}", e) } @@ -205,6 +209,7 @@ function tokenExtract(expr, type, _prefs) { } function convertFunction(json, _prefs) { + let cprefs = _prefs || {}; let prefs = (_prefs || {})[json.name] || (_prefs || {})['_'] || {}; let snakeName = json.name.toSnake(); let params = json.inputs.map((x, i) => (x.name ? x.name.toSnake() : ("_" + (i + 1))) + ": " + mapType(x.name, x.type, prefs[x.name])); @@ -212,18 +217,35 @@ function convertFunction(json, _prefs) { return ` /// Auto-generated from: \`${JSON.stringify(json)}\` #[allow(dead_code)] - pub fn ${snakeName}(&self${params.length > 0 ? ', ' + params.join(", ") : ''}) -> Result<${returns}, String> { + pub fn ${snakeName}${cprefs._explicit_do_call ? "" : ""}(&self${cprefs._explicit_do_call ? `, do_call: &F` : ""}${params.length > 0 ? ', ' + params.join(", ") : ''}) -> Result<${returns}, String> + ${cprefs._explicit_do_call ? `where F: Fn(util::Address, Vec) -> Result, String> + Send ${prefs._sync ? "+ Sync " : ""}` : ""} { let call = self.contract.function("${json.name}".into()).map_err(Self::as_string)?; let data = call.encode_call( vec![${json.inputs.map((x, i) => convertToken(x.name ? x.name.toSnake() : ("_" + (i + 1)), x.type, prefs[x.name])).join(', ')}] ).map_err(Self::as_string)?; - ${json.outputs.length > 0 ? 'let output = ' : ''}call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; + ${json.outputs.length > 0 ? 'let output = ' : ''}call.decode_output((${cprefs._explicit_do_call ? "" : "self."}do_call)(self.address.clone(), data)?).map_err(Self::as_string)?; ${json.outputs.length > 0 ? 'let mut result = output.into_iter().rev().collect::>();' : ''} Ok((${json.outputs.map((o, i) => tokenExtract('result.pop().ok_or("Invalid return arity")?', o.type, prefs[o.name])).join(', ')})) }`; } +// default preferences: +let prefs = {"_pub": true, "_": {"_client": {"string": true}, "_platform": {"string": true}}, "_sync": true}; +// default contract json ABI let jsonabi = [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]; -let out = makeContractFile("Contract", jsonabi, {"_pub": true, "_": {"_client": {"string": true}, "_platform": {"string": true}}, "_sync": true}); +// parse command line options +for (let i = 1; i < process.argv.length; ++i) { + let arg = process.argv[i]; + if (arg.indexOf("--jsonabi") == 0) { + jsonabi = arg.slice(10); + if (fs.existsSync(jsonabi)) { + jsonabi = JSON.parse(fs.readFileSync(jsonabi).toString()); + } + } else if (arg.indexOf("--explicit-do-call") == 0) { + prefs._explicit_do_call = true; + } +} + +let out = makeContractFile("Contract", jsonabi, prefs); console.log(`${out}`); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index f3ab5f3b3..2db0b2ae6 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -96,6 +96,7 @@ use ethcore::header::{BlockNumber, Header as BlockHeader}; use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockImportError, BlockQueueInfo}; use ethcore::error::*; use ethcore::snapshot::{ManifestData, RestorationStatus}; +use ethcore::transaction::PendingTransaction; use sync_io::SyncIo; use time; use super::SyncConfig; @@ -1959,7 +1960,46 @@ impl ChainSync { return 0; } - let all_transactions_hashes = transactions.iter().map(|tx| tx.transaction.hash()).collect::>(); + let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions.into_iter() + .partition(|tx| !tx.transaction.gas_price.is_zero()); + + // usual transactions could be propagated to all peers + let mut affected_peers = HashSet::new(); + if !transactions.is_empty() { + let peers = self.select_peers_for_transactions(|_| true); + affected_peers = self.propagate_transactions_to_peers(io, peers, transactions); + } + + // most of times service_transactions will be empty + // => there's no need to merge packets + if !service_transactions.is_empty() { + let service_transactions_peers = self.select_peers_for_transactions(|peer_id| accepts_service_transaction(&io.peer_info(*peer_id))); + let service_transactions_affected_peers = self.propagate_transactions_to_peers(io, service_transactions_peers, service_transactions); + affected_peers.extend(&service_transactions_affected_peers); + } + + affected_peers.len() + } + + fn select_peers_for_transactions(&self, filter: F) -> Vec + where F: Fn(&PeerId) -> bool { + // sqrt(x)/x scaled to max u32 + let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32; + let small = self.peers.len() < MIN_PEERS_PROPAGATION; + + let mut random = random::new(); + self.peers.keys() + .cloned() + .filter(filter) + .filter(|_| small || random.next_u32() < fraction) + .take(MAX_PEERS_PROPAGATION) + .collect() + } + + fn propagate_transactions_to_peers(&mut self, io: &mut SyncIo, peers: Vec, transactions: Vec) -> HashSet { + let all_transactions_hashes = transactions.iter() + .map(|tx| tx.transaction.hash()) + .collect::>(); let all_transactions_rlp = { let mut packet = RlpStream::new_list(transactions.len()); for tx in &transactions { packet.append(&tx.transaction); } @@ -1970,26 +2010,24 @@ impl ChainSync { self.transactions_stats.retain(&all_transactions_hashes); // sqrt(x)/x scaled to max u32 - let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32; - let small = self.peers.len() < MIN_PEERS_PROPAGATION; let block_number = io.chain().chain_info().best_block_number; - let mut random = random::new(); let lucky_peers = { - let stats = &mut self.transactions_stats; - self.peers.iter_mut() - .filter(|_| small || random.next_u32() < fraction) - .take(MAX_PEERS_PROPAGATION) - .filter_map(|(peer_id, mut peer_info)| { + peers.into_iter() + .filter_map(|peer_id| { + let stats = &mut self.transactions_stats; + let peer_info = self.peers.get_mut(&peer_id) + .expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed"); + // Send all transactions if peer_info.last_sent_transactions.is_empty() { // update stats for hash in &all_transactions_hashes { - let id = io.peer_session_info(*peer_id).and_then(|info| info.id); + let id = io.peer_session_info(peer_id).and_then(|info| info.id); stats.propagated(*hash, id, block_number); } peer_info.last_sent_transactions = all_transactions_hashes.clone(); - return Some((*peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone())); + return Some((peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone())); } // Get hashes of all transactions to send to this peer @@ -2007,7 +2045,7 @@ impl ChainSync { if to_send.contains(&tx.transaction.hash()) { packet.append(&tx.transaction); // update stats - let id = io.peer_session_info(*peer_id).and_then(|info| info.id); + let id = io.peer_session_info(peer_id).and_then(|info| info.id); stats.propagated(tx.transaction.hash(), id, block_number); } } @@ -2017,22 +2055,25 @@ impl ChainSync { .chain(&to_send) .cloned() .collect(); - Some((*peer_id, to_send.len(), packet.out())) + Some((peer_id, to_send.len(), packet.out())) }) .collect::>() }; // Send RLPs - let peers = lucky_peers.len(); - if peers > 0 { + let mut peers = HashSet::new(); + if lucky_peers.len() > 0 { let mut max_sent = 0; + let lucky_peers_len = lucky_peers.len(); for (peer_id, sent, rlp) in lucky_peers { + peers.insert(peer_id); self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp); trace!(target: "sync", "{:02} <- Transactions ({} entries)", peer_id, sent); max_sent = max(max_sent, sent); } - debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, peers); + debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, lucky_peers_len); } + peers } @@ -2119,12 +2160,30 @@ impl ChainSync { } } +/// Checks if peer is able to process service transactions +fn accepts_service_transaction(client_id: &str) -> bool { + // Parity versions starting from this will accept service-transactions + const SERVICE_TRANSACTIONS_VERSION: (u32, u32) = (1u32, 6u32); + // Parity client string prefix + const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity/v"; + + if !client_id.starts_with(PARITY_CLIENT_ID_PREFIX) { + return false; + } + let ver: Vec = client_id[PARITY_CLIENT_ID_PREFIX.len()..].split('.') + .take(2) + .filter_map(|s| s.parse().ok()) + .collect(); + ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1)) +} + #[cfg(test)] mod tests { use std::collections::{HashSet, VecDeque}; + use network::PeerId; use tests::helpers::*; use tests::snapshot::TestSnapshotService; - use util::{U256, RwLock}; + use util::{Uint, U256, RwLock}; use util::sha3::Hashable; use util::hash::{H256, FixedHash}; use util::bytes::Bytes; @@ -2134,6 +2193,7 @@ mod tests { use super::{PeerInfo, PeerAsking}; use ethcore::header::*; use ethcore::client::*; + use ethcore::transaction::SignedTransaction; use ethcore::miner::MinerService; fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { @@ -2359,7 +2419,12 @@ mod tests { fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync { let mut sync = ChainSync::new(SyncConfig::default(), client); - sync.peers.insert(0, + insert_dummy_peer(&mut sync, 0, peer_latest_hash); + sync + } + + fn insert_dummy_peer(sync: &mut ChainSync, peer_id: PeerId, peer_latest_hash: H256) { + sync.peers.insert(peer_id, PeerInfo { protocol_version: 0, genesis: H256::zero(), @@ -2378,7 +2443,7 @@ mod tests { asking_snapshot_data: None, block_set: None, }); - sync + } #[test] @@ -2630,6 +2695,79 @@ mod tests { assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.") } + #[test] + fn should_propagate_service_transaction_to_selected_peers_only() { + let mut client = TestBlockChainClient::new(); + client.insert_transaction_with_gas_price_to_queue(U256::zero()); + let block_hash = client.block_hash_delta_minus(1); + let mut sync = ChainSync::new(SyncConfig::default(), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when peer#1 is Geth + insert_dummy_peer(&mut sync, 1, block_hash); + io.peers_info.insert(1, "Geth".to_owned()); + // and peer#2 is Parity, accepting service transactions + insert_dummy_peer(&mut sync, 2, block_hash); + io.peers_info.insert(2, "Parity/v1.6".to_owned()); + // and peer#3 is Parity, discarding service transactions + insert_dummy_peer(&mut sync, 3, block_hash); + io.peers_info.insert(3, "Parity/v1.5".to_owned()); + // and peer#4 is Parity, accepting service transactions + insert_dummy_peer(&mut sync, 4, block_hash); + io.peers_info.insert(4, "Parity/v1.7.3-ABCDEFGH".to_owned()); + + // and new service transaction is propagated to peers + sync.propagate_new_transactions(&mut io); + + // peer#2 && peer#4 are receiving service transaction + assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET + assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 4)); // TRANSACTIONS_PACKET + assert_eq!(io.packets.len(), 2); + } + + #[test] + fn should_propagate_service_transaction_is_sent_as_separate_message() { + let mut client = TestBlockChainClient::new(); + let tx1_hash = client.insert_transaction_to_queue(); + let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); + let block_hash = client.block_hash_delta_minus(1); + let mut sync = ChainSync::new(SyncConfig::default(), &client); + let queue = RwLock::new(VecDeque::new()); + let ss = TestSnapshotService::new(); + let mut io = TestIo::new(&mut client, &ss, &queue, None); + + // when peer#1 is Parity, accepting service transactions + insert_dummy_peer(&mut sync, 1, block_hash); + io.peers_info.insert(1, "Parity/v1.6".to_owned()); + + // and service + non-service transactions are propagated to peers + sync.propagate_new_transactions(&mut io); + + // two separate packets for peer are queued: + // 1) with non-service-transaction + // 2) with service transaction + let sent_transactions: Vec = io.packets.iter() + .filter_map(|p| { + if p.packet_id != 0x02 || p.recipient != 1 { // TRANSACTIONS_PACKET + return None; + } + + let rlp = UntrustedRlp::new(&*p.data); + let item_count = rlp.item_count(); + if item_count != 1 { + return None; + } + + rlp.at(0).ok().and_then(|r| r.as_val().ok()) + }) + .collect(); + assert_eq!(sent_transactions.len(), 2); + assert!(sent_transactions.iter().any(|tx| tx.hash() == tx1_hash)); + assert!(sent_transactions.iter().any(|tx| tx.hash() == tx2_hash)); + } + #[test] fn handles_peer_new_block_malformed() { let mut client = TestBlockChainClient::new(); diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index b18b0cf61..43a7558a3 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -49,6 +49,7 @@ pub struct TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { pub sender: Option, pub to_disconnect: HashSet, pub packets: Vec, + pub peers_info: HashMap, overlay: RwLock>, } @@ -62,6 +63,7 @@ impl<'p, C> TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { to_disconnect: HashSet::new(), overlay: RwLock::new(HashMap::new()), packets: Vec::new(), + peers_info: HashMap::new(), } } } @@ -111,6 +113,12 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { &*self.chain } + fn peer_info(&self, peer_id: PeerId) -> String { + self.peers_info.get(&peer_id) + .cloned() + .unwrap_or_else(|| peer_id.to_string()) + } + fn snapshot_service(&self) -> &SnapshotService { self.snapshot_service } diff --git a/util/Cargo.toml b/util/Cargo.toml index f019a9554..0f431c7ac 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore utility library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-util" -version = "1.5.0" +version = "1.5.1" authors = ["Parity Technologies "] build = "build.rs" diff --git a/util/fetch/Cargo.toml b/util/fetch/Cargo.toml index 308816ed7..96c1317f2 100644 --- a/util/fetch/Cargo.toml +++ b/util/fetch/Cargo.toml @@ -9,8 +9,9 @@ authors = ["Parity Technologies "] [dependencies] futures = "0.1" futures-cpupool = "0.1" +parking_lot = "0.3" log = "0.3" -reqwest = "0.2" +reqwest = "0.4" mime = "0.2" clippy = { version = "0.0.90", optional = true} diff --git a/util/fetch/src/client.rs b/util/fetch/src/client.rs index bbc9f11e8..761c04e69 100644 --- a/util/fetch/src/client.rs +++ b/util/fetch/src/client.rs @@ -16,13 +16,14 @@ //! Fetching -use std::{io, fmt}; +use std::{io, fmt, time}; use std::sync::Arc; use std::sync::atomic::{self, AtomicBool}; use futures::{self, BoxFuture, Future}; use futures_cpupool::{CpuPool, CpuFuture}; use mime::{self, Mime}; +use parking_lot::RwLock; use reqwest; #[derive(Default, Debug, Clone)] @@ -71,24 +72,52 @@ pub trait Fetch: Clone + Send + Sync + 'static { fn close(self) where Self: Sized {} } -#[derive(Clone)] +const CLIENT_TIMEOUT_SECONDS: u64 = 5; + pub struct Client { - client: Arc, + client: RwLock<(time::Instant, Arc)>, pool: CpuPool, limit: Option, } +impl Clone for Client { + fn clone(&self) -> Self { + let (ref time, ref client) = *self.client.read(); + Client { + client: RwLock::new((time.clone(), client.clone())), + pool: self.pool.clone(), + limit: self.limit.clone(), + } + } +} + impl Client { - fn with_limit(limit: Option) -> Result { + fn new_client() -> Result, Error> { let mut client = reqwest::Client::new()?; client.redirect(reqwest::RedirectPolicy::limited(5)); + Ok(Arc::new(client)) + } + fn with_limit(limit: Option) -> Result { Ok(Client { - client: Arc::new(client), + client: RwLock::new((time::Instant::now(), Self::new_client()?)), pool: CpuPool::new(4), limit: limit, }) } + + fn client(&self) -> Result, Error> { + { + let (ref time, ref client) = *self.client.read(); + if time.elapsed() < time::Duration::from_secs(CLIENT_TIMEOUT_SECONDS) { + return Ok(client.clone()); + } + } + + let client = Self::new_client()?; + *self.client.write() = (time::Instant::now(), client.clone()); + Ok(client) + } } impl Fetch for Client { @@ -108,12 +137,19 @@ impl Fetch for Client { fn fetch_with_abort(&self, url: &str, abort: Abort) -> Self::Result { debug!(target: "fetch", "Fetching from: {:?}", url); - self.pool.spawn(FetchTask { - url: url.into(), - client: self.client.clone(), - limit: self.limit, - abort: abort, - }) + match self.client() { + Ok(client) => { + self.pool.spawn(FetchTask { + url: url.into(), + client: client, + limit: self.limit, + abort: abort, + }) + }, + Err(err) => { + self.pool.spawn(futures::future::err(err)) + }, + } } } diff --git a/util/fetch/src/lib.rs b/util/fetch/src/lib.rs index 9bd402a1b..0089c6fa8 100644 --- a/util/fetch/src/lib.rs +++ b/util/fetch/src/lib.rs @@ -21,6 +21,7 @@ extern crate log; extern crate futures; extern crate futures_cpupool; +extern crate parking_lot; extern crate reqwest; pub extern crate mime; diff --git a/util/network/src/host.rs b/util/network/src/host.rs index 05f0e83a4..bdaaba4ab 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -517,13 +517,13 @@ impl Host { } pub fn external_url(&self) -> Option { - self.info.read().public_endpoint.as_ref().map(|e| format!("{}", Node::new(self.info.read().id().clone(), e.clone()))) + let info = self.info.read(); + info.public_endpoint.as_ref().map(|e| format!("{}", Node::new(info.id().clone(), e.clone()))) } pub fn local_url(&self) -> String { - let r = format!("{}", Node::new(self.info.read().id().clone(), self.info.read().local_endpoint.clone())); - println!("{}", r); - r + let info = self.info.read(); + format!("{}", Node::new(info.id().clone(), info.local_endpoint.clone())) } pub fn stop(&self, io: &IoContext) -> Result<(), NetworkError> {