Merge branch 'master' into periodic_snapshot
This commit is contained in:
commit
46b1224f7c
60
Cargo.lock
generated
60
Cargo.lock
generated
@ -79,6 +79,11 @@ dependencies = [
|
|||||||
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bigint"
|
name = "bigint"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -307,6 +312,7 @@ dependencies = [
|
|||||||
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-rpc 1.4.0",
|
"ethcore-rpc 1.4.0",
|
||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
|
"https-fetch 0.1.0",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
@ -661,6 +667,15 @@ name = "httparse"
|
|||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "https-fetch"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||||
|
"rustls 0.1.1 (git+https://github.com/ctz/rustls)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@ -1311,6 +1326,15 @@ name = "regex-syntax"
|
|||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"untrusted 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rlp"
|
name = "rlp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1386,6 +1410,19 @@ dependencies = [
|
|||||||
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "git+https://github.com/ctz/rustls#a9c5a79f49337e22ac05bb1ea114240bdbe0fdd2"
|
||||||
|
dependencies = [
|
||||||
|
"base64 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)",
|
||||||
|
"ring 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"untrusted 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"webpki 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@ -1678,6 +1715,11 @@ name = "unicode-xid"
|
|||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -1719,6 +1761,17 @@ name = "void"
|
|||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"ring 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"untrusted 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@ -1786,6 +1839,7 @@ dependencies = [
|
|||||||
"checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975"
|
"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"
|
"checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a"
|
||||||
"checksum aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4df293303e8a52e1df7984ac1415e195f5fcbf51e4bb7bda54557861a3954a08"
|
"checksum aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4df293303e8a52e1df7984ac1415e195f5fcbf51e4bb7bda54557861a3954a08"
|
||||||
|
"checksum base64 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2015e3793554aa5b6007e3a72959e84c1070039e74f13dde08fa64afe1ddd892"
|
||||||
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
|
||||||
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
|
||||||
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
|
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
|
||||||
@ -1804,7 +1858,7 @@ dependencies = [
|
|||||||
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
||||||
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
|
"checksum docopt 0.6.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.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
|
||||||
"checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
|
"checksum elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bc9250a632e7c001b741eb0ec6cee93c9a5b6d5f1879696a4b94d62b012210a"
|
||||||
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
||||||
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
||||||
"checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f"
|
"checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f"
|
||||||
@ -1885,6 +1939,7 @@ dependencies = [
|
|||||||
"checksum rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "941deb43a6254b9867fec1e0caeda38a2ad905ab18c57f7c68c396ca68998c07"
|
"checksum rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "941deb43a6254b9867fec1e0caeda38a2ad905ab18c57f7c68c396ca68998c07"
|
||||||
"checksum regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)" = "b4329b8928a284580a1c63ec9d846b12f6d3472317243ff7077aff11f23f2b29"
|
"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 regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "841591b1e05609a643e3b4d0045fce04f701daba7151ddcd3ad47b080693d5a9"
|
||||||
|
"checksum ring 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d059a6a96d3be79042e3f70eb97945912839265f9d8ab45b921abaf266c70dbb"
|
||||||
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||||
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
|
||||||
"checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "<none>"
|
"checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "<none>"
|
||||||
@ -1892,6 +1947,7 @@ dependencies = [
|
|||||||
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
||||||
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
||||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||||
|
"checksum rustls 0.1.1 (git+https://github.com/ctz/rustls)" = "<none>"
|
||||||
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
||||||
"checksum semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d5b7638a1f03815d94e88cb3b3c08e87f0db4d683ef499d1836aaf70a45623f"
|
"checksum semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d5b7638a1f03815d94e88cb3b3c08e87f0db4d683ef499d1836aaf70a45623f"
|
||||||
"checksum serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b1dfda9ebb31d29fa8b94d7eb3031a86a8dcec065f0fe268a30f98867bf45775"
|
"checksum serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b1dfda9ebb31d29fa8b94d7eb3031a86a8dcec065f0fe268a30f98867bf45775"
|
||||||
@ -1929,11 +1985,13 @@ dependencies = [
|
|||||||
"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
|
"checksum unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c1f7ceb96afdfeedee42bade65a0d585a6a0106f681b6749c8ff4daa8df30b3f"
|
||||||
"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
|
"checksum unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26643a2f83bac55f1976fb716c10234485f9202dcd65cfbdf9da49867b271172"
|
||||||
"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb"
|
"checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb"
|
||||||
|
"checksum untrusted 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d9bc0e6e73a10975d1fbff8ac3541e221181b0d8998351600fb5523de634c0d"
|
||||||
"checksum url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afe9ec54bc4db14bc8744b7fed060d785ac756791450959b2248443319d5b119"
|
"checksum url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afe9ec54bc4db14bc8744b7fed060d785ac756791450959b2248443319d5b119"
|
||||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||||
"checksum vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0795a11576d29ae80525a3fda315bf7b534f8feb9d34101e5fe63fb95bb2fd24"
|
"checksum vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0795a11576d29ae80525a3fda315bf7b534f8feb9d34101e5fe63fb95bb2fd24"
|
||||||
"checksum vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56b639f935488eb40f06d17c3e3bcc3054f6f75d264e187b1107c8d1cba8d31c"
|
"checksum vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56b639f935488eb40f06d17c3e3bcc3054f6f75d264e187b1107c8d1cba8d31c"
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
"checksum webpki 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc10a815fabbb0c3145c1153240528f3a8703a47e26e8dbb4a5d4f6386200ad"
|
||||||
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
||||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||||
|
@ -25,6 +25,7 @@ ethabi = "0.2.2"
|
|||||||
linked-hash-map = "0.3"
|
linked-hash-map = "0.3"
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
|
https-fetch = { path = "../util/https-fetch" }
|
||||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
# List of apps
|
# List of apps
|
||||||
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
@ -37,7 +38,7 @@ clippy = { version = "0.0.85", optional = true}
|
|||||||
serde_codegen = { version = "0.8", optional = true }
|
serde_codegen = { version = "0.8", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serde_codegen", "extra-dapps"]
|
default = ["serde_codegen", "extra-dapps", "https-fetch/ca-github-only"]
|
||||||
extra-dapps = ["parity-dapps-wallet"]
|
extra-dapps = ["parity-dapps-wallet"]
|
||||||
nightly = ["serde_macros"]
|
nightly = ["serde_macros"]
|
||||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||||
|
@ -60,7 +60,7 @@ impl ContentCache {
|
|||||||
ContentStatus::Fetching(ref abort) => {
|
ContentStatus::Fetching(ref abort) => {
|
||||||
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
||||||
// Mark as aborted
|
// Mark as aborted
|
||||||
abort.store(true, Ordering::Relaxed);
|
abort.store(true, Ordering::SeqCst);
|
||||||
},
|
},
|
||||||
ContentStatus::Ready(ref endpoint) => {
|
ContentStatus::Ready(ref endpoint) => {
|
||||||
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
//! Uses `URLHint` to resolve addresses into Dapps bundle file location.
|
//! Uses `URLHint` to resolve addresses into Dapps bundle file location.
|
||||||
|
|
||||||
use zip;
|
use zip;
|
||||||
use std::{fs, env};
|
use std::{fs, env, fmt};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -147,6 +147,23 @@ pub enum ValidationError {
|
|||||||
HashMismatch { expected: H256, got: H256, },
|
HashMismatch { expected: H256, got: H256, },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ValidationError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
match *self {
|
||||||
|
ValidationError::Io(ref io) => write!(f, "Unexpected IO error occured: {:?}", io),
|
||||||
|
ValidationError::Zip(ref zip) => write!(f, "Unable to read ZIP archive: {:?}", zip),
|
||||||
|
ValidationError::InvalidDappId => write!(f, "Dapp ID is invalid. It should be 32 bytes hash of content."),
|
||||||
|
ValidationError::ManifestNotFound => write!(f, "Downloaded Dapp bundle did not contain valid manifest.json file."),
|
||||||
|
ValidationError::ManifestSerialization(ref err) => {
|
||||||
|
write!(f, "There was an error during Dapp Manifest serialization: {:?}", err)
|
||||||
|
},
|
||||||
|
ValidationError::HashMismatch { ref expected, ref got } => {
|
||||||
|
write!(f, "Hash of downloaded content did not match. Expected:{:?}, Got:{:?}.", expected, got)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for ValidationError {
|
impl From<io::Error> for ValidationError {
|
||||||
fn from(err: io::Error) -> Self {
|
fn from(err: io::Error) -> Self {
|
||||||
ValidationError::Io(err)
|
ValidationError::Io(err)
|
||||||
@ -202,8 +219,8 @@ impl ContentValidator for DappInstaller {
|
|||||||
|
|
||||||
fn validate_and_install(&self, app_path: PathBuf) -> Result<Manifest, ValidationError> {
|
fn validate_and_install(&self, app_path: PathBuf) -> Result<Manifest, ValidationError> {
|
||||||
trace!(target: "dapps", "Opening dapp bundle at {:?}", app_path);
|
trace!(target: "dapps", "Opening dapp bundle at {:?}", app_path);
|
||||||
let mut file = try!(fs::File::open(app_path));
|
let mut file_reader = io::BufReader::new(try!(fs::File::open(app_path)));
|
||||||
let hash = try!(sha3(&mut file));
|
let hash = try!(sha3(&mut file_reader));
|
||||||
let dapp_id = try!(self.dapp_id.as_str().parse().map_err(|_| ValidationError::InvalidDappId));
|
let dapp_id = try!(self.dapp_id.as_str().parse().map_err(|_| ValidationError::InvalidDappId));
|
||||||
if dapp_id != hash {
|
if dapp_id != hash {
|
||||||
return Err(ValidationError::HashMismatch {
|
return Err(ValidationError::HashMismatch {
|
||||||
@ -211,6 +228,7 @@ impl ContentValidator for DappInstaller {
|
|||||||
got: hash,
|
got: hash,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let file = file_reader.into_inner();
|
||||||
// Unpack archive
|
// Unpack archive
|
||||||
let mut zip = try!(zip::ZipArchive::new(file));
|
let mut zip = try!(zip::ZipArchive::new(file));
|
||||||
// First find manifest file
|
// First find manifest file
|
||||||
|
@ -33,8 +33,9 @@ pub struct GithubApp {
|
|||||||
|
|
||||||
impl GithubApp {
|
impl GithubApp {
|
||||||
pub fn url(&self) -> String {
|
pub fn url(&self) -> String {
|
||||||
|
// Since https fetcher doesn't support redirections we use direct link
|
||||||
// format!("https://github.com/{}/{}/archive/{}.zip", self.account, self.repo, self.commit.to_hex())
|
// format!("https://github.com/{}/{}/archive/{}.zip", self.account, self.repo, self.commit.to_hex())
|
||||||
format!("http://github.todr.me/{}/{}/zip/{}", self.account, self.repo, self.commit.to_hex())
|
format!("https://codeload.github.com/{}/{}/zip/{}", self.account, self.repo, self.commit.to_hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit(bytes: &[u8]) -> Option<[u8;COMMIT_LEN]> {
|
fn commit(bytes: &[u8]) -> Option<[u8;COMMIT_LEN]> {
|
||||||
@ -300,6 +301,6 @@ mod tests {
|
|||||||
let url = app.url();
|
let url = app.url();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(url, "http://github.todr.me/test/xyz/zip/000102030405060708090a0b0c0d0e0f10111213".to_owned());
|
assert_eq!(url, "https://codeload.github.com/test/xyz/zip/000102030405060708090a0b0c0d0e0f10111213".to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ use hyper::client::{Request, Response, DefaultTransport as HttpStream};
|
|||||||
use hyper::header::Connection;
|
use hyper::header::Connection;
|
||||||
use hyper::{self, Decoder, Encoder, Next};
|
use hyper::{self, Decoder, Encoder, Next};
|
||||||
|
|
||||||
|
use super::FetchError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Aborted,
|
Aborted,
|
||||||
@ -37,7 +39,7 @@ pub enum Error {
|
|||||||
HyperError(hyper::Error),
|
HyperError(hyper::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FetchResult = Result<PathBuf, Error>;
|
pub type FetchResult = Result<PathBuf, FetchError>;
|
||||||
pub type OnDone = Box<Fn() + Send>;
|
pub type OnDone = Box<Fn() + Send>;
|
||||||
|
|
||||||
pub struct Fetch {
|
pub struct Fetch {
|
||||||
@ -57,7 +59,7 @@ impl fmt::Debug for Fetch {
|
|||||||
|
|
||||||
impl Drop for Fetch {
|
impl Drop for Fetch {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let res = self.result.take().unwrap_or(Err(Error::NotStarted));
|
let res = self.result.take().unwrap_or(Err(Error::NotStarted.into()));
|
||||||
// Remove file if there was an error
|
// Remove file if there was an error
|
||||||
if res.is_err() || self.is_aborted() {
|
if res.is_err() || self.is_aborted() {
|
||||||
if let Some(file) = self.file.take() {
|
if let Some(file) = self.file.take() {
|
||||||
@ -92,10 +94,10 @@ impl Fetch {
|
|||||||
|
|
||||||
impl Fetch {
|
impl Fetch {
|
||||||
fn is_aborted(&self) -> bool {
|
fn is_aborted(&self) -> bool {
|
||||||
self.abort.load(Ordering::Relaxed)
|
self.abort.load(Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
fn mark_aborted(&mut self) -> Next {
|
fn mark_aborted(&mut self) -> Next {
|
||||||
self.result = Some(Err(Error::Aborted));
|
self.result = Some(Err(Error::Aborted.into()));
|
||||||
Next::end()
|
Next::end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,7 +123,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
|
|||||||
return self.mark_aborted();
|
return self.mark_aborted();
|
||||||
}
|
}
|
||||||
if *res.status() != StatusCode::Ok {
|
if *res.status() != StatusCode::Ok {
|
||||||
self.result = Some(Err(Error::UnexpectedStatus(*res.status())));
|
self.result = Some(Err(Error::UnexpectedStatus(*res.status()).into()));
|
||||||
return Next::end();
|
return Next::end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +135,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
|
|||||||
read()
|
read()
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.result = Some(Err(Error::IoError(err)));
|
self.result = Some(Err(Error::IoError(err).into()));
|
||||||
Next::end()
|
Next::end()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -149,7 +151,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
|
|||||||
Err(e) => match e.kind() {
|
Err(e) => match e.kind() {
|
||||||
io::ErrorKind::WouldBlock => Next::read(),
|
io::ErrorKind::WouldBlock => Next::read(),
|
||||||
_ => {
|
_ => {
|
||||||
self.result = Some(Err(Error::IoError(e)));
|
self.result = Some(Err(Error::IoError(e).into()));
|
||||||
Next::end()
|
Next::end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +159,7 @@ impl hyper::client::Handler<HttpStream> for Fetch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_error(&mut self, err: hyper::Error) -> Next {
|
fn on_error(&mut self, err: hyper::Error) -> Next {
|
||||||
self.result = Some(Err(Error::HyperError(err)));
|
self.result = Some(Err(Error::HyperError(err).into()));
|
||||||
Next::remove()
|
Next::remove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,98 @@
|
|||||||
|
|
||||||
//! Hyper Client Handlers
|
//! Hyper Client Handlers
|
||||||
|
|
||||||
mod fetch_file;
|
pub mod fetch_file;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::sync::{mpsc, Arc};
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use hyper;
|
||||||
|
use https_fetch as https;
|
||||||
|
|
||||||
|
use random_filename;
|
||||||
|
use self::fetch_file::{Fetch, Error as HttpFetchError};
|
||||||
|
|
||||||
|
pub type FetchResult = Result<PathBuf, FetchError>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FetchError {
|
||||||
|
InvalidUrl,
|
||||||
|
Http(HttpFetchError),
|
||||||
|
Https(https::FetchError),
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HttpFetchError> for FetchError {
|
||||||
|
fn from(e: HttpFetchError) -> Self {
|
||||||
|
FetchError::Http(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
http_client: hyper::Client<Fetch>,
|
||||||
|
https_client: https::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Client {
|
||||||
|
http_client: hyper::Client::new().expect("Unable to initialize http client."),
|
||||||
|
https_client: https::Client::new().expect("Unable to initialize https client."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) {
|
||||||
|
self.http_client.close();
|
||||||
|
self.https_client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(&mut self, url: String, abort: Arc<AtomicBool>, on_done: Box<Fn() + Send>) -> Result<mpsc::Receiver<FetchResult>, FetchError> {
|
||||||
|
let is_https = url.starts_with("https://");
|
||||||
|
let url = try!(url.parse().map_err(|_| FetchError::InvalidUrl));
|
||||||
|
trace!(target: "dapps", "Fetching from: {:?}", url);
|
||||||
|
if is_https {
|
||||||
|
let url = try!(Self::convert_url(url));
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let temp_path = Self::temp_path();
|
||||||
|
let res = self.https_client.fetch_to_file(url, temp_path.clone(), abort, move |result| {
|
||||||
|
let res = tx.send(
|
||||||
|
result.map(|_| temp_path).map_err(FetchError::Https)
|
||||||
|
);
|
||||||
|
if let Err(_) = res {
|
||||||
|
warn!("Fetch finished, but no one was listening");
|
||||||
|
}
|
||||||
|
on_done();
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => Ok(rx),
|
||||||
|
Err(e) => Err(FetchError::Other(format!("{:?}", e))),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let res = self.http_client.request(url, Fetch::new(tx, abort, on_done));
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => Ok(rx),
|
||||||
|
Err(e) => Err(FetchError::Other(format!("{:?}", e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_url(url: hyper::Url) -> Result<https::Url, FetchError> {
|
||||||
|
let host = format!("{}", try!(url.host().ok_or(FetchError::InvalidUrl)));
|
||||||
|
let port = try!(url.port_or_known_default().ok_or(FetchError::InvalidUrl));
|
||||||
|
https::Url::new(&host, port, url.path()).map_err(|_| FetchError::InvalidUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn temp_path() -> PathBuf {
|
||||||
|
let mut dir = env::temp_dir();
|
||||||
|
dir.push(random_filename());
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub use self::fetch_file::{Fetch, FetchResult, OnDone};
|
|
||||||
|
|
||||||
|
@ -16,18 +16,18 @@
|
|||||||
|
|
||||||
//! Hyper Server Handler that fetches a file during a request (proxy).
|
//! Hyper Server Handler that fetches a file during a request (proxy).
|
||||||
|
|
||||||
use std::{fs, fmt};
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{mpsc, Arc};
|
use std::sync::{mpsc, Arc};
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
use hyper::{header, server, Decoder, Encoder, Next, Method, Control, Client};
|
use hyper::{header, server, Decoder, Encoder, Next, Method, Control};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
use handlers::client::{Fetch, FetchResult};
|
use handlers::client::{Client, FetchResult};
|
||||||
use apps::redirection_address;
|
use apps::redirection_address;
|
||||||
use apps::urlhint::GithubApp;
|
use apps::urlhint::GithubApp;
|
||||||
use apps::manifest::Manifest;
|
use apps::manifest::Manifest;
|
||||||
@ -45,7 +45,7 @@ enum FetchState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ContentValidator {
|
pub trait ContentValidator {
|
||||||
type Error: fmt::Debug;
|
type Error: fmt::Debug + fmt::Display;
|
||||||
|
|
||||||
fn validate_and_install(&self, app: PathBuf) -> Result<Manifest, Self::Error>;
|
fn validate_and_install(&self, app: PathBuf) -> Result<Manifest, Self::Error>;
|
||||||
fn done(&self, Option<&Manifest>);
|
fn done(&self, Option<&Manifest>);
|
||||||
@ -55,7 +55,7 @@ pub struct ContentFetcherHandler<H: ContentValidator> {
|
|||||||
abort: Arc<AtomicBool>,
|
abort: Arc<AtomicBool>,
|
||||||
control: Option<Control>,
|
control: Option<Control>,
|
||||||
status: FetchState,
|
status: FetchState,
|
||||||
client: Option<Client<Fetch>>,
|
client: Option<Client>,
|
||||||
using_dapps_domains: bool,
|
using_dapps_domains: bool,
|
||||||
dapp: H,
|
dapp: H,
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
|
|||||||
using_dapps_domains: bool,
|
using_dapps_domains: bool,
|
||||||
handler: H) -> Self {
|
handler: H) -> Self {
|
||||||
|
|
||||||
let client = Client::new().expect("Failed to create a Client");
|
let client = Client::new();
|
||||||
ContentFetcherHandler {
|
ContentFetcherHandler {
|
||||||
abort: abort,
|
abort: abort,
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
@ -90,28 +90,19 @@ impl<H: ContentValidator> ContentFetcherHandler<H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_client(client: &mut Option<Client<Fetch>>) {
|
fn close_client(client: &mut Option<Client>) {
|
||||||
client.take()
|
client.take()
|
||||||
.expect("After client is closed we are going into write, hence we can never close it again")
|
.expect("After client is closed we are going into write, hence we can never close it again")
|
||||||
.close();
|
.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO [todr] https support
|
fn fetch_app(client: &mut Client, app: &GithubApp, abort: Arc<AtomicBool>, control: Control) -> Result<mpsc::Receiver<FetchResult>, String> {
|
||||||
fn fetch_app(client: &mut Client<Fetch>, app: &GithubApp, abort: Arc<AtomicBool>, control: Control) -> Result<mpsc::Receiver<FetchResult>, String> {
|
client.request(app.url(), abort, Box::new(move || {
|
||||||
let url = try!(app.url().parse().map_err(|e| format!("{:?}", e)));
|
|
||||||
trace!(target: "dapps", "Fetching from: {:?}", url);
|
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
let res = client.request(url, Fetch::new(tx, abort, Box::new(move || {
|
|
||||||
trace!(target: "dapps", "Fetching finished.");
|
trace!(target: "dapps", "Fetching finished.");
|
||||||
// Ignoring control errors
|
// Ignoring control errors
|
||||||
let _ = control.ready(Next::read());
|
let _ = control.ready(Next::read());
|
||||||
})));
|
})).map_err(|e| format!("{:?}", e))
|
||||||
match res {
|
|
||||||
Ok(_) => Ok(rx),
|
|
||||||
Err(e) => Err(format!("{:?}", e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,13 +168,14 @@ impl<H: ContentValidator> server::Handler<HttpStream> for ContentFetcherHandler<
|
|||||||
trace!(target: "dapps", "Error while validating dapp: {:?}", e);
|
trace!(target: "dapps", "Error while validating dapp: {:?}", e);
|
||||||
FetchState::Error(ContentHandler::html(
|
FetchState::Error(ContentHandler::html(
|
||||||
StatusCode::BadGateway,
|
StatusCode::BadGateway,
|
||||||
format!("<h1>Downloaded bundle does not contain valid app.</h1><pre>{:?}</pre>", e),
|
format!("<h1>Downloaded bundle does not contain valid app.</h1><pre>{}</pre>", e),
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
Ok(manifest) => FetchState::Done(manifest)
|
Ok(manifest) => FetchState::Done(manifest)
|
||||||
};
|
};
|
||||||
// Remove temporary zip file
|
// Remove temporary zip file
|
||||||
let _ = fs::remove_file(path);
|
// TODO [todr] Uncomment me
|
||||||
|
// let _ = fs::remove_file(path);
|
||||||
(Some(state), Next::write())
|
(Some(state), Next::write())
|
||||||
},
|
},
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
|
@ -58,6 +58,7 @@ extern crate jsonrpc_http_server;
|
|||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate parity_dapps;
|
extern crate parity_dapps;
|
||||||
|
extern crate https_fetch;
|
||||||
extern crate ethcore_rpc;
|
extern crate ethcore_rpc;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate linked_hash_map;
|
extern crate linked_hash_map;
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
//! Evm interface.
|
//! Evm interface.
|
||||||
|
|
||||||
use common::*;
|
use std::{ops, cmp, fmt};
|
||||||
|
use util::{U128, U256, U512, Uint};
|
||||||
|
use action_params::ActionParams;
|
||||||
use evm::Ext;
|
use evm::Ext;
|
||||||
|
|
||||||
/// Evm errors.
|
/// Evm errors.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// `OutOfGas` is returned when transaction execution runs out of gas.
|
/// `OutOfGas` is returned when transaction execution runs out of gas.
|
||||||
/// The state should be reverted to the state from before the
|
/// The state should be reverted to the state from before the
|
||||||
@ -63,6 +65,21 @@ pub enum Error {
|
|||||||
Internal,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::Error::*;
|
||||||
|
let message = match *self {
|
||||||
|
OutOfGas => "Out of gas",
|
||||||
|
BadJumpDestination { .. } => "Bad jump destination",
|
||||||
|
BadInstruction { .. } => "Bad instruction",
|
||||||
|
StackUnderflow { .. } => "Stack underflow",
|
||||||
|
OutOfStack { .. } => "Out of stack",
|
||||||
|
Internal => "Internal error",
|
||||||
|
};
|
||||||
|
message.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A specialized version of Result over EVM errors.
|
/// A specialized version of Result over EVM errors.
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
|
|
||||||
@ -193,10 +210,13 @@ pub trait Evm {
|
|||||||
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
|
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn should_calculate_overflow_mul_shr_without_overflow() {
|
mod tests {
|
||||||
|
use util::{U256, Uint};
|
||||||
|
use super::CostType;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_calculate_overflow_mul_shr_without_overflow() {
|
||||||
// given
|
// given
|
||||||
let num = 1048576;
|
let num = 1048576;
|
||||||
|
|
||||||
@ -209,15 +229,14 @@ fn should_calculate_overflow_mul_shr_without_overflow() {
|
|||||||
assert!(!o1);
|
assert!(!o1);
|
||||||
assert_eq!(res2, num);
|
assert_eq!(res2, num);
|
||||||
assert!(!o2);
|
assert!(!o2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(test)]
|
fn should_calculate_overflow_mul_shr_with_overflow() {
|
||||||
fn should_calculate_overflow_mul_shr_with_overflow() {
|
|
||||||
// given
|
// given
|
||||||
let max = ::std::u64::MAX;
|
let max = u64::max_value();
|
||||||
let num1 = U256([max, max, max, max]);
|
let num1 = U256([max, max, max, max]);
|
||||||
let num2 = ::std::usize::MAX;
|
let num2 = usize::max_value();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
|
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
|
||||||
@ -229,17 +248,17 @@ fn should_calculate_overflow_mul_shr_with_overflow() {
|
|||||||
|
|
||||||
assert_eq!(res1, !U256::zero() - U256::one());
|
assert_eq!(res1, !U256::zero() - U256::one());
|
||||||
assert!(o1);
|
assert!(o1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(test)]
|
fn should_validate_u256_to_usize_conversion() {
|
||||||
fn should_validate_u256_to_usize_conversion() {
|
|
||||||
// given
|
// given
|
||||||
let v = U256::from(::std::usize::MAX) + U256::from(1);
|
let v = U256::from(usize::max_value()) + U256::from(1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = usize::from_u256(v);
|
let res = usize::from_u256(v);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@ impl<'a> Executive<'a> {
|
|||||||
// just drain the whole gas
|
// just drain the whole gas
|
||||||
self.state.revert_snapshot();
|
self.state.revert_snapshot();
|
||||||
|
|
||||||
tracer.trace_failed_call(trace_info, vec![]);
|
tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into());
|
||||||
|
|
||||||
Err(evm::Error::OutOfGas)
|
Err(evm::Error::OutOfGas)
|
||||||
}
|
}
|
||||||
@ -320,7 +320,7 @@ impl<'a> Executive<'a> {
|
|||||||
trace_output,
|
trace_output,
|
||||||
traces
|
traces
|
||||||
),
|
),
|
||||||
_ => tracer.trace_failed_call(trace_info, traces),
|
Err(e) => tracer.trace_failed_call(trace_info, traces, e.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
|
trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
|
||||||
@ -385,7 +385,7 @@ impl<'a> Executive<'a> {
|
|||||||
created,
|
created,
|
||||||
subtracer.traces()
|
subtracer.traces()
|
||||||
),
|
),
|
||||||
_ => tracer.trace_failed_create(trace_info, subtracer.traces())
|
Err(e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into())
|
||||||
};
|
};
|
||||||
|
|
||||||
self.enact_result(&res, substate, unconfirmed_substate);
|
self.enact_result(&res, substate, unconfirmed_substate);
|
||||||
|
@ -341,3 +341,91 @@ impl SnapshotReader for LooseReader {
|
|||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use devtools::RandomTempPath;
|
||||||
|
use util::sha3::Hashable;
|
||||||
|
|
||||||
|
use snapshot::ManifestData;
|
||||||
|
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader};
|
||||||
|
|
||||||
|
const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"];
|
||||||
|
const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn packed_write_and_read() {
|
||||||
|
let path = RandomTempPath::new();
|
||||||
|
let mut writer = PackedWriter::new(path.as_path()).unwrap();
|
||||||
|
|
||||||
|
let mut state_hashes = Vec::new();
|
||||||
|
let mut block_hashes = Vec::new();
|
||||||
|
|
||||||
|
for chunk in STATE_CHUNKS {
|
||||||
|
let hash = chunk.sha3();
|
||||||
|
state_hashes.push(hash.clone());
|
||||||
|
writer.write_state_chunk(hash, chunk).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for chunk in BLOCK_CHUNKS {
|
||||||
|
let hash = chunk.sha3();
|
||||||
|
block_hashes.push(hash.clone());
|
||||||
|
writer.write_block_chunk(chunk.sha3(), chunk).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest = ManifestData {
|
||||||
|
state_hashes: state_hashes,
|
||||||
|
block_hashes: block_hashes,
|
||||||
|
state_root: b"notarealroot".sha3(),
|
||||||
|
block_number: 12345678987654321,
|
||||||
|
block_hash: b"notarealblock".sha3(),
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.finish(manifest.clone()).unwrap();
|
||||||
|
|
||||||
|
let reader = PackedReader::new(path.as_path()).unwrap().unwrap();
|
||||||
|
assert_eq!(reader.manifest(), &manifest);
|
||||||
|
|
||||||
|
for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) {
|
||||||
|
reader.chunk(hash.clone()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loose_write_and_read() {
|
||||||
|
let path = RandomTempPath::new();
|
||||||
|
let mut writer = LooseWriter::new(path.as_path().into()).unwrap();
|
||||||
|
|
||||||
|
let mut state_hashes = Vec::new();
|
||||||
|
let mut block_hashes = Vec::new();
|
||||||
|
|
||||||
|
for chunk in STATE_CHUNKS {
|
||||||
|
let hash = chunk.sha3();
|
||||||
|
state_hashes.push(hash.clone());
|
||||||
|
writer.write_state_chunk(hash, chunk).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for chunk in BLOCK_CHUNKS {
|
||||||
|
let hash = chunk.sha3();
|
||||||
|
block_hashes.push(hash.clone());
|
||||||
|
writer.write_block_chunk(chunk.sha3(), chunk).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest = ManifestData {
|
||||||
|
state_hashes: state_hashes,
|
||||||
|
block_hashes: block_hashes,
|
||||||
|
state_root: b"notarealroot".sha3(),
|
||||||
|
block_number: 12345678987654321,
|
||||||
|
block_hash: b"notarealblock".sha3(),
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.finish(manifest.clone()).unwrap();
|
||||||
|
|
||||||
|
let reader = LooseReader::new(path.as_path().into()).unwrap();
|
||||||
|
assert_eq!(reader.manifest(), &manifest);
|
||||||
|
|
||||||
|
for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) {
|
||||||
|
reader.chunk(hash.clone()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -59,7 +59,7 @@ impl Drop for Guard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Statuses for restorations.
|
/// Statuses for restorations.
|
||||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
pub enum RestorationStatus {
|
pub enum RestorationStatus {
|
||||||
/// No restoration.
|
/// No restoration.
|
||||||
Inactive,
|
Inactive,
|
||||||
@ -277,7 +277,7 @@ impl Service {
|
|||||||
|
|
||||||
// delete the temporary snapshot dir if it does exist.
|
// delete the temporary snapshot dir if it does exist.
|
||||||
if let Err(e) = fs::remove_dir_all(service.temp_snapshot_dir()) {
|
if let Err(e) = fs::remove_dir_all(service.temp_snapshot_dir()) {
|
||||||
if e.kind() != ErrorKind {
|
if e.kind() != ErrorKind::NotFound {
|
||||||
return Err(e.into())
|
return Err(e.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -581,3 +581,50 @@ impl SnapshotService for Service {
|
|||||||
.expect("snapshot service and io service are kept alive by client service; qed");
|
.expect("snapshot service and io service are kept alive by client service; qed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use service::ClientIoMessage;
|
||||||
|
use io::{IoService};
|
||||||
|
use devtools::RandomTempPath;
|
||||||
|
use tests::helpers::get_test_spec;
|
||||||
|
use util::journaldb::Algorithm;
|
||||||
|
|
||||||
|
use snapshot::ManifestData;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sends_async_messages() {
|
||||||
|
let service = IoService::<ClientIoMessage>::start().unwrap();
|
||||||
|
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
let mut dir = dir.as_path().to_owned();
|
||||||
|
dir.push("pruning");
|
||||||
|
dir.push("db");
|
||||||
|
|
||||||
|
let service = Service::new(
|
||||||
|
&get_test_spec(),
|
||||||
|
Algorithm::Archive,
|
||||||
|
dir,
|
||||||
|
service.channel()
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert!(service.manifest().is_none());
|
||||||
|
assert!(service.chunk(Default::default()).is_none());
|
||||||
|
assert_eq!(service.status(), RestorationStatus::Inactive);
|
||||||
|
assert_eq!(service.chunks_done(), (0, 0));
|
||||||
|
|
||||||
|
let manifest = ManifestData {
|
||||||
|
state_hashes: vec![],
|
||||||
|
block_hashes: vec![],
|
||||||
|
state_root: Default::default(),
|
||||||
|
block_number: 0,
|
||||||
|
block_hash: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
service.begin_restore(manifest);
|
||||||
|
service.abort_restore();
|
||||||
|
service.restore_state_chunk(Default::default(), vec![]);
|
||||||
|
service.restore_block_chunk(Default::default(), vec![]);
|
||||||
|
}
|
||||||
|
}
|
@ -78,7 +78,7 @@ impl StateProducer {
|
|||||||
let new_accs = rng.gen::<u32>() % 5;
|
let new_accs = rng.gen::<u32>() % 5;
|
||||||
|
|
||||||
for _ in 0..new_accs {
|
for _ in 0..new_accs {
|
||||||
let address_hash = H256::random();
|
let address_hash = H256(rng.gen());
|
||||||
let balance: usize = rng.gen();
|
let balance: usize = rng.gen();
|
||||||
let nonce: usize = rng.gen();
|
let nonce: usize = rng.gen();
|
||||||
let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp();
|
let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp();
|
||||||
|
@ -20,3 +20,18 @@ mod blocks;
|
|||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
|
|
||||||
|
use super::ManifestData;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn manifest_rlp() {
|
||||||
|
let manifest = ManifestData {
|
||||||
|
block_hashes: Vec::new(),
|
||||||
|
state_hashes: Vec::new(),
|
||||||
|
block_number: 1234567,
|
||||||
|
state_root: Default::default(),
|
||||||
|
block_hash: Default::default(),
|
||||||
|
};
|
||||||
|
let raw = manifest.clone().into_rlp();
|
||||||
|
assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest);
|
||||||
|
}
|
@ -20,7 +20,7 @@ use snapshot::{chunk_state, Progress, StateRebuilder};
|
|||||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||||
use super::helpers::{compare_dbs, StateProducer};
|
use super::helpers::{compare_dbs, StateProducer};
|
||||||
|
|
||||||
use rand;
|
use rand::{XorShiftRng, SeedableRng};
|
||||||
use util::hash::H256;
|
use util::hash::H256;
|
||||||
use util::journaldb::{self, Algorithm};
|
use util::journaldb::{self, Algorithm};
|
||||||
use util::kvdb::{Database, DatabaseConfig};
|
use util::kvdb::{Database, DatabaseConfig};
|
||||||
@ -33,7 +33,7 @@ use std::sync::Arc;
|
|||||||
#[test]
|
#[test]
|
||||||
fn snap_and_restore() {
|
fn snap_and_restore() {
|
||||||
let mut producer = StateProducer::new();
|
let mut producer = StateProducer::new();
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = XorShiftRng::from_seed([1, 2, 3, 4]);
|
||||||
let mut old_db = MemoryDB::new();
|
let mut old_db = MemoryDB::new();
|
||||||
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||||
|
|
||||||
|
@ -444,8 +444,7 @@ use env_info::*;
|
|||||||
use spec::*;
|
use spec::*;
|
||||||
use transaction::*;
|
use transaction::*;
|
||||||
use util::log::init_log;
|
use util::log::init_log;
|
||||||
use trace::trace;
|
use trace::{FlatTrace, TraceError, trace};
|
||||||
use trace::FlatTrace;
|
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -538,7 +537,7 @@ fn should_trace_failed_create_transaction() {
|
|||||||
gas: 78792.into(),
|
gas: 78792.into(),
|
||||||
init: vec![91, 96, 0, 86],
|
init: vec![91, 96, 0, 86],
|
||||||
}),
|
}),
|
||||||
result: trace::Res::FailedCreate,
|
result: trace::Res::FailedCreate(TraceError::OutOfGas),
|
||||||
subtraces: 0
|
subtraces: 0
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -869,7 +868,7 @@ fn should_trace_failed_call_transaction() {
|
|||||||
input: vec![],
|
input: vec![],
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::FailedCall,
|
result: trace::Res::FailedCall(TraceError::OutOfGas),
|
||||||
subtraces: 0,
|
subtraces: 0,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -1084,7 +1083,7 @@ fn should_trace_failed_subcall_transaction() {
|
|||||||
input: vec![],
|
input: vec![],
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::FailedCall,
|
result: trace::Res::FailedCall(TraceError::OutOfGas),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
assert_eq!(result.trace, expected_trace);
|
assert_eq!(result.trace, expected_trace);
|
||||||
@ -1217,7 +1216,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
|||||||
input: vec![],
|
input: vec![],
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: trace::Res::FailedCall,
|
result: trace::Res::FailedCall(TraceError::OutOfGas),
|
||||||
}, FlatTrace {
|
}, FlatTrace {
|
||||||
trace_address: vec![0, 0].into_iter().collect(),
|
trace_address: vec![0, 0].into_iter().collect(),
|
||||||
subtraces: 0,
|
subtraces: 0,
|
||||||
|
@ -420,7 +420,7 @@ mod tests {
|
|||||||
use devtools::RandomTempPath;
|
use devtools::RandomTempPath;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use trace::{Config, Switch, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest};
|
use trace::{Config, Switch, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest};
|
||||||
use trace::{Filter, LocalizedTrace, AddressesFilter};
|
use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError};
|
||||||
use trace::trace::{Call, Action, Res};
|
use trace::trace::{Call, Action, Res};
|
||||||
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
|
use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces};
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
@ -560,7 +560,7 @@ mod tests {
|
|||||||
input: vec![],
|
input: vec![],
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall(TraceError::OutOfGas),
|
||||||
}])]),
|
}])]),
|
||||||
block_hash: block_hash.clone(),
|
block_hash: block_hash.clone(),
|
||||||
block_number: block_number,
|
block_number: block_number,
|
||||||
@ -579,7 +579,7 @@ mod tests {
|
|||||||
input: vec![],
|
input: vec![],
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall(TraceError::OutOfGas),
|
||||||
trace_address: vec![],
|
trace_address: vec![],
|
||||||
subtraces: 0,
|
subtraces: 0,
|
||||||
transaction_number: 0,
|
transaction_number: 0,
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
use util::{Bytes, Address, U256};
|
use util::{Bytes, Address, U256};
|
||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
|
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide};
|
||||||
use trace::{Tracer, VMTracer, FlatTrace};
|
use trace::{Tracer, VMTracer, FlatTrace, TraceError};
|
||||||
|
|
||||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -112,23 +112,23 @@ impl Tracer for ExecutiveTracer {
|
|||||||
self.traces.extend(update_trace_address(subs));
|
self.traces.extend(update_trace_address(subs));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>) {
|
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError) {
|
||||||
let trace = FlatTrace {
|
let trace = FlatTrace {
|
||||||
trace_address: Default::default(),
|
trace_address: Default::default(),
|
||||||
subtraces: top_level_subtraces(&subs),
|
subtraces: top_level_subtraces(&subs),
|
||||||
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall(error),
|
||||||
};
|
};
|
||||||
debug!(target: "trace", "Traced failed call {:?}", trace);
|
debug!(target: "trace", "Traced failed call {:?}", trace);
|
||||||
self.traces.push(trace);
|
self.traces.push(trace);
|
||||||
self.traces.extend(update_trace_address(subs));
|
self.traces.extend(update_trace_address(subs));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>) {
|
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError) {
|
||||||
let trace = FlatTrace {
|
let trace = FlatTrace {
|
||||||
subtraces: top_level_subtraces(&subs),
|
subtraces: top_level_subtraces(&subs),
|
||||||
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
|
||||||
result: Res::FailedCreate,
|
result: Res::FailedCreate(error),
|
||||||
trace_address: Default::default(),
|
trace_address: Default::default(),
|
||||||
};
|
};
|
||||||
debug!(target: "trace", "Traced failed create {:?}", trace);
|
debug!(target: "trace", "Traced failed create {:?}", trace);
|
||||||
|
@ -24,7 +24,8 @@ mod executive_tracer;
|
|||||||
mod import;
|
mod import;
|
||||||
mod noop_tracer;
|
mod noop_tracer;
|
||||||
|
|
||||||
pub use types::trace_types::*;
|
pub use types::trace_types::{filter, flat, localized, trace};
|
||||||
|
pub use types::trace_types::error::Error as TraceError;
|
||||||
pub use self::config::{Config, Switch};
|
pub use self::config::{Config, Switch};
|
||||||
pub use self::db::TraceDB;
|
pub use self::db::TraceDB;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
@ -71,10 +72,10 @@ pub trait Tracer: Send {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Stores failed call trace.
|
/// Stores failed call trace.
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>);
|
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError);
|
||||||
|
|
||||||
/// Stores failed create trace.
|
/// Stores failed create trace.
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>);
|
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError);
|
||||||
|
|
||||||
/// Stores suicide info.
|
/// Stores suicide info.
|
||||||
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
use util::{Bytes, Address, U256};
|
use util::{Bytes, Address, U256};
|
||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use trace::{Tracer, VMTracer, FlatTrace};
|
use trace::{Tracer, VMTracer, FlatTrace, TraceError};
|
||||||
use trace::trace::{Call, Create, VMTrace};
|
use trace::trace::{Call, Create, VMTrace};
|
||||||
|
|
||||||
/// Nonoperative tracer. Does not trace anything.
|
/// Nonoperative tracer. Does not trace anything.
|
||||||
@ -47,11 +47,11 @@ impl Tracer for NoopTracer {
|
|||||||
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
assert!(code.is_none(), "self.prepare_trace_output().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>) {
|
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>, _: TraceError) {
|
||||||
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>) {
|
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>, _: TraceError) {
|
||||||
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
99
ethcore/src/types/trace_types/error.rs
Normal file
99
ethcore/src/types/trace_types/error.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Trace errors.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use rlp::{Encodable, RlpStream, Decodable, Decoder, DecoderError, Stream, View};
|
||||||
|
use evm::Error as EvmError;
|
||||||
|
|
||||||
|
/// Trace evm errors.
|
||||||
|
#[derive(Debug, PartialEq, Clone, Binary)]
|
||||||
|
pub enum Error {
|
||||||
|
/// `OutOfGas` is returned when transaction execution runs out of gas.
|
||||||
|
OutOfGas,
|
||||||
|
/// `BadJumpDestination` is returned when execution tried to move
|
||||||
|
/// to position that wasn't marked with JUMPDEST instruction
|
||||||
|
BadJumpDestination,
|
||||||
|
/// `BadInstructions` is returned when given instruction is not supported
|
||||||
|
BadInstruction,
|
||||||
|
/// `StackUnderflow` when there is not enough stack elements to execute instruction
|
||||||
|
StackUnderflow,
|
||||||
|
/// When execution would exceed defined Stack Limit
|
||||||
|
OutOfStack,
|
||||||
|
/// Returned on evm internal error. Should never be ignored during development.
|
||||||
|
/// Likely to cause consensus issues.
|
||||||
|
Internal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EvmError> for Error {
|
||||||
|
fn from(e: EvmError) -> Self {
|
||||||
|
match e {
|
||||||
|
EvmError::OutOfGas => Error::OutOfGas,
|
||||||
|
EvmError::BadJumpDestination { .. } => Error::BadJumpDestination,
|
||||||
|
EvmError::BadInstruction { .. } => Error::BadInstruction,
|
||||||
|
EvmError::StackUnderflow { .. } => Error::StackUnderflow,
|
||||||
|
EvmError::OutOfStack { .. } => Error::OutOfStack,
|
||||||
|
EvmError::Internal => Error::Internal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::Error::*;
|
||||||
|
let message = match *self {
|
||||||
|
OutOfGas => "Out of gas",
|
||||||
|
BadJumpDestination => "Bad jump destination",
|
||||||
|
BadInstruction => "Bad instruction",
|
||||||
|
StackUnderflow => "Stack underflow",
|
||||||
|
OutOfStack => "Out of stack",
|
||||||
|
Internal => "Internal error",
|
||||||
|
};
|
||||||
|
message.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for Error {
|
||||||
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
|
use self::Error::*;
|
||||||
|
let value = match *self {
|
||||||
|
OutOfGas => 0u8,
|
||||||
|
BadJumpDestination => 1,
|
||||||
|
BadInstruction => 2,
|
||||||
|
StackUnderflow => 3,
|
||||||
|
OutOfStack => 4,
|
||||||
|
Internal => 5,
|
||||||
|
};
|
||||||
|
s.append(&value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for Error {
|
||||||
|
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||||
|
use self::Error::*;
|
||||||
|
let value: u8 = try!(decoder.as_rlp().as_val());
|
||||||
|
match value {
|
||||||
|
0 => Ok(OutOfGas),
|
||||||
|
1 => Ok(BadJumpDestination),
|
||||||
|
2 => Ok(BadInstruction),
|
||||||
|
3 => Ok(StackUnderflow),
|
||||||
|
4 => Ok(OutOfStack),
|
||||||
|
5 => Ok(Internal),
|
||||||
|
_ => Err(DecoderError::Custom("Invalid error type")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -140,7 +140,7 @@ mod tests {
|
|||||||
use util::bloom::Bloomable;
|
use util::bloom::Bloomable;
|
||||||
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
|
use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide};
|
||||||
use trace::flat::FlatTrace;
|
use trace::flat::FlatTrace;
|
||||||
use trace::{Filter, AddressesFilter};
|
use trace::{Filter, AddressesFilter, TraceError};
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -286,7 +286,7 @@ mod tests {
|
|||||||
input: vec![0x5],
|
input: vec![0x5],
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::FailedCall,
|
result: Res::FailedCall(TraceError::OutOfGas),
|
||||||
trace_address: vec![0].into_iter().collect(),
|
trace_address: vec![0].into_iter().collect(),
|
||||||
subtraces: 0,
|
subtraces: 0,
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
//! Types used in the public api
|
//! Types used in the public api
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod flat;
|
pub mod flat;
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
|
@ -24,6 +24,7 @@ use rlp::*;
|
|||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use basic_types::LogBloom;
|
use basic_types::LogBloom;
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
|
use super::error::Error;
|
||||||
|
|
||||||
/// `Call` result.
|
/// `Call` result.
|
||||||
#[derive(Debug, Clone, PartialEq, Default, Binary)]
|
#[derive(Debug, Clone, PartialEq, Default, Binary)]
|
||||||
@ -322,9 +323,9 @@ pub enum Res {
|
|||||||
/// Successful create action result.
|
/// Successful create action result.
|
||||||
Create(CreateResult),
|
Create(CreateResult),
|
||||||
/// Failed call.
|
/// Failed call.
|
||||||
FailedCall,
|
FailedCall(Error),
|
||||||
/// Failed create.
|
/// Failed create.
|
||||||
FailedCreate,
|
FailedCreate(Error),
|
||||||
/// None
|
/// None
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
@ -342,13 +343,15 @@ impl Encodable for Res {
|
|||||||
s.append(&1u8);
|
s.append(&1u8);
|
||||||
s.append(create);
|
s.append(create);
|
||||||
},
|
},
|
||||||
Res::FailedCall => {
|
Res::FailedCall(ref err) => {
|
||||||
s.begin_list(1);
|
s.begin_list(2);
|
||||||
s.append(&2u8);
|
s.append(&2u8);
|
||||||
|
s.append(err);
|
||||||
},
|
},
|
||||||
Res::FailedCreate => {
|
Res::FailedCreate(ref err) => {
|
||||||
s.begin_list(1);
|
s.begin_list(2);
|
||||||
s.append(&3u8);
|
s.append(&3u8);
|
||||||
|
s.append(err);
|
||||||
},
|
},
|
||||||
Res::None => {
|
Res::None => {
|
||||||
s.begin_list(1);
|
s.begin_list(1);
|
||||||
@ -365,8 +368,8 @@ impl Decodable for Res {
|
|||||||
match action_type {
|
match action_type {
|
||||||
0 => d.val_at(1).map(Res::Call),
|
0 => d.val_at(1).map(Res::Call),
|
||||||
1 => d.val_at(1).map(Res::Create),
|
1 => d.val_at(1).map(Res::Create),
|
||||||
2 => Ok(Res::FailedCall),
|
2 => d.val_at(1).map(Res::FailedCall),
|
||||||
3 => Ok(Res::FailedCreate),
|
3 => d.val_at(1).map(Res::FailedCreate),
|
||||||
4 => Ok(Res::None),
|
4 => Ok(Res::None),
|
||||||
_ => Err(DecoderError::Custom("Invalid result type.")),
|
_ => Err(DecoderError::Custom("Invalid result type.")),
|
||||||
}
|
}
|
||||||
@ -378,7 +381,7 @@ impl Res {
|
|||||||
pub fn bloom(&self) -> LogBloom {
|
pub fn bloom(&self) -> LogBloom {
|
||||||
match *self {
|
match *self {
|
||||||
Res::Create(ref create) => create.bloom(),
|
Res::Create(ref create) => create.bloom(),
|
||||||
Res::Call(_) | Res::FailedCall | Res::FailedCreate | Res::None => Default::default(),
|
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,8 @@ pub fn setup_log(config: &Config) -> Result<Arc<RotatingLogger>, String> {
|
|||||||
let mut builder = LogBuilder::new();
|
let mut builder = LogBuilder::new();
|
||||||
// Disable ws info logging by default.
|
// Disable ws info logging by default.
|
||||||
builder.filter(Some("ws"), LogLevelFilter::Warn);
|
builder.filter(Some("ws"), LogLevelFilter::Warn);
|
||||||
|
// Disable rustls info logging by default.
|
||||||
|
builder.filter(Some("rustls"), LogLevelFilter::Warn);
|
||||||
builder.filter(None, LogLevelFilter::Info);
|
builder.filter(None, LogLevelFilter::Info);
|
||||||
|
|
||||||
if env::var("RUST_LOG").is_ok() {
|
if env::var("RUST_LOG").is_ok() {
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use ethcore::trace::trace;
|
use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError};
|
||||||
use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace};
|
|
||||||
use ethcore::trace as et;
|
use ethcore::trace as et;
|
||||||
use ethcore::state_diff;
|
use ethcore::state_diff;
|
||||||
use ethcore::account_diff;
|
use ethcore::account_diff;
|
||||||
@ -319,16 +318,13 @@ impl From<trace::Suicide> for Suicide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Action
|
/// Action
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Call
|
/// Call
|
||||||
#[serde(rename="call")]
|
|
||||||
Call(Call),
|
Call(Call),
|
||||||
/// Create
|
/// Create
|
||||||
#[serde(rename="create")]
|
|
||||||
Create(Create),
|
Create(Create),
|
||||||
/// Suicide
|
/// Suicide
|
||||||
#[serde(rename="suicide")]
|
|
||||||
Suicide(Suicide),
|
Suicide(Suicide),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,22 +380,17 @@ impl From<trace::CreateResult> for CreateResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Response
|
/// Response
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug)]
|
||||||
pub enum Res {
|
pub enum Res {
|
||||||
/// Call
|
/// Call
|
||||||
#[serde(rename="call")]
|
|
||||||
Call(CallResult),
|
Call(CallResult),
|
||||||
/// Create
|
/// Create
|
||||||
#[serde(rename="create")]
|
|
||||||
Create(CreateResult),
|
Create(CreateResult),
|
||||||
/// Call failure
|
/// Call failure
|
||||||
#[serde(rename="failedCall")]
|
FailedCall(TraceError),
|
||||||
FailedCall,
|
|
||||||
/// Creation failure
|
/// Creation failure
|
||||||
#[serde(rename="failedCreate")]
|
FailedCreate(TraceError),
|
||||||
FailedCreate,
|
|
||||||
/// None
|
/// None
|
||||||
#[serde(rename="none")]
|
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,39 +399,73 @@ impl From<trace::Res> for Res {
|
|||||||
match t {
|
match t {
|
||||||
trace::Res::Call(call) => Res::Call(CallResult::from(call)),
|
trace::Res::Call(call) => Res::Call(CallResult::from(call)),
|
||||||
trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
|
trace::Res::Create(create) => Res::Create(CreateResult::from(create)),
|
||||||
trace::Res::FailedCall => Res::FailedCall,
|
trace::Res::FailedCall(error) => Res::FailedCall(error),
|
||||||
trace::Res::FailedCreate => Res::FailedCreate,
|
trace::Res::FailedCreate(error) => Res::FailedCreate(error),
|
||||||
trace::Res::None => Res::None,
|
trace::Res::None => Res::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trace
|
/// Trace
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug)]
|
||||||
pub struct LocalizedTrace {
|
pub struct LocalizedTrace {
|
||||||
/// Action
|
/// Action
|
||||||
action: Action,
|
action: Action,
|
||||||
/// Result
|
/// Result
|
||||||
result: Res,
|
result: Res,
|
||||||
/// Trace address
|
/// Trace address
|
||||||
#[serde(rename="traceAddress")]
|
|
||||||
trace_address: Vec<U256>,
|
trace_address: Vec<U256>,
|
||||||
/// Subtraces
|
/// Subtraces
|
||||||
subtraces: U256,
|
subtraces: U256,
|
||||||
/// Transaction position
|
/// Transaction position
|
||||||
#[serde(rename="transactionPosition")]
|
|
||||||
transaction_position: U256,
|
transaction_position: U256,
|
||||||
/// Transaction hash
|
/// Transaction hash
|
||||||
#[serde(rename="transactionHash")]
|
|
||||||
transaction_hash: H256,
|
transaction_hash: H256,
|
||||||
/// Block Number
|
/// Block Number
|
||||||
#[serde(rename="blockNumber")]
|
|
||||||
block_number: U256,
|
block_number: U256,
|
||||||
/// Block Hash
|
/// Block Hash
|
||||||
#[serde(rename="blockHash")]
|
|
||||||
block_hash: H256,
|
block_hash: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for LocalizedTrace {
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
let mut state = try!(serializer.serialize_struct("LocalizedTrace", 9));
|
||||||
|
match self.action {
|
||||||
|
Action::Call(ref call) => {
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "type", "call"));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "action", call));
|
||||||
|
},
|
||||||
|
Action::Create(ref create) => {
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "type", "create"));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "action", create));
|
||||||
|
},
|
||||||
|
Action::Suicide(ref suicide) => {
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "type", "suicide"));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "action", suicide));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.result {
|
||||||
|
Res::Call(ref call) => try!(serializer.serialize_struct_elt(&mut state, "result", call)),
|
||||||
|
Res::Create(ref create) => try!(serializer.serialize_struct_elt(&mut state, "result", create)),
|
||||||
|
Res::FailedCall(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
|
||||||
|
Res::FailedCreate(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
|
||||||
|
Res::None => try!(serializer.serialize_struct_elt(&mut state, "result", None as Option<u8>)),
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "traceAddress", &self.trace_address));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "subtraces", &self.subtraces));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "transactionPosition", &self.transaction_position));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "transactionHash", &self.transaction_hash));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "blockNumber", &self.block_number));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "blockHash", &self.block_hash));
|
||||||
|
|
||||||
|
serializer.serialize_struct_end(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<EthLocalizedTrace> for LocalizedTrace {
|
impl From<EthLocalizedTrace> for LocalizedTrace {
|
||||||
fn from(t: EthLocalizedTrace) -> Self {
|
fn from(t: EthLocalizedTrace) -> Self {
|
||||||
LocalizedTrace {
|
LocalizedTrace {
|
||||||
@ -457,10 +482,9 @@ impl From<EthLocalizedTrace> for LocalizedTrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trace
|
/// Trace
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug)]
|
||||||
pub struct Trace {
|
pub struct Trace {
|
||||||
/// Trace address
|
/// Trace address
|
||||||
#[serde(rename="traceAddress")]
|
|
||||||
trace_address: Vec<U256>,
|
trace_address: Vec<U256>,
|
||||||
/// Subtraces
|
/// Subtraces
|
||||||
subtraces: U256,
|
subtraces: U256,
|
||||||
@ -470,6 +494,41 @@ pub struct Trace {
|
|||||||
result: Res,
|
result: Res,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for Trace {
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
let mut state = try!(serializer.serialize_struct("Trace", 4));
|
||||||
|
match self.action {
|
||||||
|
Action::Call(ref call) => {
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "type", "call"));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "action", call));
|
||||||
|
},
|
||||||
|
Action::Create(ref create) => {
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "type", "create"));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "action", create));
|
||||||
|
},
|
||||||
|
Action::Suicide(ref suicide) => {
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "type", "suicide"));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "action", suicide));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.result {
|
||||||
|
Res::Call(ref call) => try!(serializer.serialize_struct_elt(&mut state, "result", call)),
|
||||||
|
Res::Create(ref create) => try!(serializer.serialize_struct_elt(&mut state, "result", create)),
|
||||||
|
Res::FailedCall(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
|
||||||
|
Res::FailedCreate(ref error) => try!(serializer.serialize_struct_elt(&mut state, "error", error.to_string())),
|
||||||
|
Res::None => try!(serializer.serialize_struct_elt(&mut state, "result", None as Option<u8>)),
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "traceAddress", &self.trace_address));
|
||||||
|
try!(serializer.serialize_struct_elt(&mut state, "subtraces", &self.subtraces));
|
||||||
|
|
||||||
|
serializer.serialize_struct_end(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<FlatTrace> for Trace {
|
impl From<FlatTrace> for Trace {
|
||||||
fn from(t: FlatTrace) -> Self {
|
fn from(t: FlatTrace) -> Self {
|
||||||
Trace {
|
Trace {
|
||||||
@ -511,7 +570,8 @@ impl From<Executed> for TraceResults {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use v1::types::{Bytes, U256, H256, H160};
|
use v1::types::Bytes;
|
||||||
|
use ethcore::trace::TraceError;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -527,29 +587,118 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trace_serialize() {
|
fn test_trace_call_serialize() {
|
||||||
let t = LocalizedTrace {
|
let t = LocalizedTrace {
|
||||||
action: Action::Call(Call {
|
action: Action::Call(Call {
|
||||||
from: H160::from(4),
|
from: 4.into(),
|
||||||
to: H160::from(5),
|
to: 5.into(),
|
||||||
value: U256::from(6),
|
value: 6.into(),
|
||||||
gas: U256::from(7),
|
gas: 7.into(),
|
||||||
input: Bytes::new(vec![0x12, 0x34]),
|
input: Bytes::new(vec![0x12, 0x34]),
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
}),
|
}),
|
||||||
result: Res::Call(CallResult {
|
result: Res::Call(CallResult {
|
||||||
gas_used: U256::from(8),
|
gas_used: 8.into(),
|
||||||
output: vec![0x56, 0x78].into(),
|
output: vec![0x56, 0x78].into(),
|
||||||
}),
|
}),
|
||||||
trace_address: vec![U256::from(10)],
|
trace_address: vec![10.into()],
|
||||||
subtraces: U256::from(1),
|
subtraces: 1.into(),
|
||||||
transaction_position: U256::from(11),
|
transaction_position: 11.into(),
|
||||||
transaction_hash: H256::from(12),
|
transaction_hash: 12.into(),
|
||||||
block_number: U256::from(13),
|
block_number: 13.into(),
|
||||||
block_hash: H256::from(14),
|
block_hash: 14.into(),
|
||||||
};
|
};
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"}},"result":{"call":{"gasUsed":"0x8","output":"0x5678"}},"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"result":{"gasUsed":"0x8","output":"0x5678"},"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trace_failed_call_serialize() {
|
||||||
|
let t = LocalizedTrace {
|
||||||
|
action: Action::Call(Call {
|
||||||
|
from: 4.into(),
|
||||||
|
to: 5.into(),
|
||||||
|
value: 6.into(),
|
||||||
|
gas: 7.into(),
|
||||||
|
input: Bytes::new(vec![0x12, 0x34]),
|
||||||
|
call_type: CallType::Call,
|
||||||
|
}),
|
||||||
|
result: Res::FailedCall(TraceError::OutOfGas),
|
||||||
|
trace_address: vec![10.into()],
|
||||||
|
subtraces: 1.into(),
|
||||||
|
transaction_position: 11.into(),
|
||||||
|
transaction_hash: 12.into(),
|
||||||
|
block_number: 13.into(),
|
||||||
|
block_hash: 14.into(),
|
||||||
|
};
|
||||||
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"error":"Out of gas","traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trace_create_serialize() {
|
||||||
|
let t = LocalizedTrace {
|
||||||
|
action: Action::Create(Create {
|
||||||
|
from: 4.into(),
|
||||||
|
value: 6.into(),
|
||||||
|
gas: 7.into(),
|
||||||
|
init: Bytes::new(vec![0x12, 0x34]),
|
||||||
|
}),
|
||||||
|
result: Res::Create(CreateResult {
|
||||||
|
gas_used: 8.into(),
|
||||||
|
code: vec![0x56, 0x78].into(),
|
||||||
|
address: 0xff.into(),
|
||||||
|
}),
|
||||||
|
trace_address: vec![10.into()],
|
||||||
|
subtraces: 1.into(),
|
||||||
|
transaction_position: 11.into(),
|
||||||
|
transaction_hash: 12.into(),
|
||||||
|
block_number: 13.into(),
|
||||||
|
block_hash: 14.into(),
|
||||||
|
};
|
||||||
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"result":{"gasUsed":"0x8","code":"0x5678","address":"0x00000000000000000000000000000000000000ff"},"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trace_failed_create_serialize() {
|
||||||
|
let t = LocalizedTrace {
|
||||||
|
action: Action::Create(Create {
|
||||||
|
from: 4.into(),
|
||||||
|
value: 6.into(),
|
||||||
|
gas: 7.into(),
|
||||||
|
init: Bytes::new(vec![0x12, 0x34]),
|
||||||
|
}),
|
||||||
|
result: Res::FailedCreate(TraceError::OutOfGas),
|
||||||
|
trace_address: vec![10.into()],
|
||||||
|
subtraces: 1.into(),
|
||||||
|
transaction_position: 11.into(),
|
||||||
|
transaction_hash: 12.into(),
|
||||||
|
block_number: 13.into(),
|
||||||
|
block_hash: 14.into(),
|
||||||
|
};
|
||||||
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"error":"Out of gas","traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trace_suicide_serialize() {
|
||||||
|
let t = LocalizedTrace {
|
||||||
|
action: Action::Suicide(Suicide {
|
||||||
|
address: 4.into(),
|
||||||
|
refund_address: 6.into(),
|
||||||
|
balance: 7.into(),
|
||||||
|
}),
|
||||||
|
result: Res::None,
|
||||||
|
trace_address: vec![10.into()],
|
||||||
|
subtraces: 1.into(),
|
||||||
|
transaction_position: 11.into(),
|
||||||
|
transaction_hash: 12.into(),
|
||||||
|
block_number: 13.into(),
|
||||||
|
block_hash: 14.into(),
|
||||||
|
};
|
||||||
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
|
assert_eq!(serialized, r#"{"type":"suicide","action":{"address":"0x0000000000000000000000000000000000000004","refundAddress":"0x0000000000000000000000000000000000000006","balance":"0x7"},"result":null,"traceAddress":["0xa"],"subtraces":"0x1","transactionPosition":"0xb","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0xd","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -616,44 +765,4 @@ mod tests {
|
|||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
assert_eq!(serialized, r#"{"0x000000000000000000000000000000000000002a":{"balance":"=","nonce":{"+":"0x1"},"code":"=","storage":{"0x000000000000000000000000000000000000000000000000000000000000002a":"="}},"0x0000000000000000000000000000000000000045":{"balance":"=","nonce":{"*":{"from":"0x1","to":"0x0"}},"code":{"-":"0x60"},"storage":{}}}"#);
|
assert_eq!(serialized, r#"{"0x000000000000000000000000000000000000002a":{"balance":"=","nonce":{"+":"0x1"},"code":"=","storage":{"0x000000000000000000000000000000000000000000000000000000000000002a":"="}},"0x0000000000000000000000000000000000000045":{"balance":"=","nonce":{"*":{"from":"0x1","to":"0x0"}},"code":{"-":"0x60"},"storage":{}}}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_action_serialize() {
|
|
||||||
let actions = vec![Action::Call(Call {
|
|
||||||
from: H160::from(1),
|
|
||||||
to: H160::from(2),
|
|
||||||
value: U256::from(3),
|
|
||||||
gas: U256::from(4),
|
|
||||||
input: vec![0x12, 0x34].into(),
|
|
||||||
call_type: CallType::Call,
|
|
||||||
}), Action::Create(Create {
|
|
||||||
from: H160::from(5),
|
|
||||||
value: U256::from(6),
|
|
||||||
gas: U256::from(7),
|
|
||||||
init: vec![0x56, 0x78].into(),
|
|
||||||
})];
|
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&actions).unwrap();
|
|
||||||
assert_eq!(serialized, r#"[{"call":{"from":"0x0000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000002","value":"0x3","gas":"0x4","input":"0x1234","callType":"call"}},{"create":{"from":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","init":"0x5678"}}]"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_result_serialize() {
|
|
||||||
let results = vec![
|
|
||||||
Res::Call(CallResult {
|
|
||||||
gas_used: U256::from(1),
|
|
||||||
output: vec![0x12, 0x34].into(),
|
|
||||||
}),
|
|
||||||
Res::Create(CreateResult {
|
|
||||||
gas_used: U256::from(2),
|
|
||||||
code: vec![0x45, 0x56].into(),
|
|
||||||
address: H160::from(3),
|
|
||||||
}),
|
|
||||||
Res::FailedCall,
|
|
||||||
Res::FailedCreate,
|
|
||||||
];
|
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&results).unwrap();
|
|
||||||
assert_eq!(serialized, r#"[{"call":{"gasUsed":"0x1","output":"0x1234"}},{"create":{"gasUsed":"0x2","code":"0x4556","address":"0x0000000000000000000000000000000000000003"}},"failedCall","failedCreate"]"#);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1505,11 +1505,13 @@ impl ChainSync {
|
|||||||
|
|
||||||
// Send RLPs
|
// Send RLPs
|
||||||
let sent = lucky_peers.len();
|
let sent = lucky_peers.len();
|
||||||
|
if sent > 0 {
|
||||||
for (peer_id, rlp) in lucky_peers.into_iter() {
|
for (peer_id, rlp) in lucky_peers.into_iter() {
|
||||||
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(target: "sync", "Sent up to {} transactions to {} peers.", transactions.len(), sent);
|
trace!(target: "sync", "Sent up to {} transactions to {} peers.", transactions.len(), sent);
|
||||||
|
}
|
||||||
sent
|
sent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
util/https-fetch/Cargo.toml
Normal file
18
util/https-fetch/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
description = "HTTPS fetching library"
|
||||||
|
homepage = "http://ethcore.io"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
name = "https-fetch"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" }
|
||||||
|
rustls = { git = "https://github.com/ctz/rustls" }
|
||||||
|
clippy = { version = "0.0.85", optional = true}
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
ca-github-only = []
|
||||||
|
dev = ["clippy"]
|
15
util/https-fetch/examples/fetch.rs
Normal file
15
util/https-fetch/examples/fetch.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
extern crate https_fetch;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use https_fetch::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let client = Client::new().unwrap();
|
||||||
|
let aborted = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
client.fetch(Url::new("github.com", 443, "/").unwrap(), Box::new(io::stdout()), aborted, |result| {
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}).unwrap();
|
||||||
|
}
|
4496
util/https-fetch/src/ca-certificates.crt
Normal file
4496
util/https-fetch/src/ca-certificates.crt
Normal file
File diff suppressed because it is too large
Load Diff
93
util/https-fetch/src/ca-github.crt
Normal file
93
util/https-fetch/src/ca-github.crt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||||
|
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||||
|
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||||
|
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||||
|
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||||
|
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||||
|
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||||
|
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||||
|
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||||
|
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||||
|
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||||
|
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||||
|
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||||
|
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||||
|
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||||
|
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||||
|
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||||
|
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||||
|
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||||
|
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||||
|
+OkuE6N36B9K
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
|
||||||
|
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||||
|
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||||
|
ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
|
||||||
|
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||||
|
LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
|
||||||
|
YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||||
|
ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
|
||||||
|
uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
|
||||||
|
LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
|
||||||
|
/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
|
||||||
|
cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
|
||||||
|
8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
|
||||||
|
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
|
||||||
|
BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
|
||||||
|
Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
|
||||||
|
dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
|
||||||
|
MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
|
||||||
|
b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
|
||||||
|
gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
|
||||||
|
hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
|
||||||
|
4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
|
||||||
|
2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
|
||||||
|
1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
|
||||||
|
oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
|
||||||
|
8TUoE6smftX3eg==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIHeTCCBmGgAwIBAgIQC/20CQrXteZAwwsWyVKaJzANBgkqhkiG9w0BAQsFADB1
|
||||||
|
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||||
|
d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk
|
||||||
|
IFZhbGlkYXRpb24gU2VydmVyIENBMB4XDTE2MDMxMDAwMDAwMFoXDTE4MDUxNzEy
|
||||||
|
MDAwMFowgf0xHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
|
||||||
|
BAGCNzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMRAwDgYDVQQF
|
||||||
|
Ewc1MTU3NTUwMSQwIgYDVQQJExs4OCBDb2xpbiBQIEtlbGx5LCBKciBTdHJlZXQx
|
||||||
|
DjAMBgNVBBETBTk0MTA3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5p
|
||||||
|
YTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHViLCBJbmMu
|
||||||
|
MRMwEQYDVQQDEwpnaXRodWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||||
|
CgKCAQEA54hc8pZclxgcupjiA/F/OZGRwm/ZlucoQGTNTKmBEgNsrn/mxhngWmPw
|
||||||
|
bAvUaLP//T79Jc+1WXMpxMiz9PK6yZRRFuIo0d2bx423NA6hOL2RTtbnfs+y0PFS
|
||||||
|
/YTpQSelTuq+Fuwts5v6aAweNyMcYD0HBybkkdosFoDccBNzJ92Ac8I5EVDUc3Or
|
||||||
|
/4jSyZwzxu9kdmBlBzeHMvsqdH8SX9mNahXtXxRpwZnBiUjw36PgN+s9GLWGrafd
|
||||||
|
02T0ux9Yzd5ezkMxukqEAQ7AKIIijvaWPAJbK/52XLhIy2vpGNylyni/DQD18bBP
|
||||||
|
T+ZG1uv0QQP9LuY/joO+FKDOTler4wIDAQABo4IDejCCA3YwHwYDVR0jBBgwFoAU
|
||||||
|
PdNQpdagre7zSmAKZdMh1Pj41g8wHQYDVR0OBBYEFIhcSGcZzKB2WS0RecO+oqyH
|
||||||
|
IidbMCUGA1UdEQQeMByCCmdpdGh1Yi5jb22CDnd3dy5naXRodWIuY29tMA4GA1Ud
|
||||||
|
DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdQYDVR0f
|
||||||
|
BG4wbDA0oDKgMIYuaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItZXYtc2Vy
|
||||||
|
dmVyLWcxLmNybDA0oDKgMIYuaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTIt
|
||||||
|
ZXYtc2VydmVyLWcxLmNybDBLBgNVHSAERDBCMDcGCWCGSAGG/WwCATAqMCgGCCsG
|
||||||
|
AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAcGBWeBDAEBMIGI
|
||||||
|
BggrBgEFBQcBAQR8MHowJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
|
||||||
|
LmNvbTBSBggrBgEFBQcwAoZGaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp
|
||||||
|
Z2lDZXJ0U0hBMkV4dGVuZGVkVmFsaWRhdGlvblNlcnZlckNBLmNydDAMBgNVHRMB
|
||||||
|
Af8EAjAAMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkAdgCkuQmQtBhYFIe7E6LM
|
||||||
|
Z3AKPDWYBPkb37jjd80OyA3cEAAAAVNhieoeAAAEAwBHMEUCIQCHHSEY/ROK2/sO
|
||||||
|
ljbKaNEcKWz6BxHJNPOtjSyuVnSn4QIgJ6RqvYbSX1vKLeX7vpnOfCAfS2Y8lB5R
|
||||||
|
NMwk6us2QiAAdgBo9pj4H2SCvjqM7rkoHUz8cVFdZ5PURNEKZ6y7T0/7xAAAAVNh
|
||||||
|
iennAAAEAwBHMEUCIQDZpd5S+3to8k7lcDeWBhiJASiYTk2rNAT26lVaM3xhWwIg
|
||||||
|
NUqrkIODZpRg+khhp8ag65B8mu0p4JUAmkRDbiYnRvYAdwBWFAaaL9fC7NP14b1E
|
||||||
|
sj7HRna5vJkRXMDvlJhV1onQ3QAAAVNhieqZAAAEAwBIMEYCIQDnm3WStlvE99GC
|
||||||
|
izSx+UGtGmQk2WTokoPgo1hfiv8zIAIhAPrYeXrBgseA9jUWWoB4IvmcZtshjXso
|
||||||
|
nT8MIG1u1zF8MA0GCSqGSIb3DQEBCwUAA4IBAQCLbNtkxuspqycq8h1EpbmAX0wM
|
||||||
|
5DoW7hM/FVdz4LJ3Kmftyk1yd8j/PSxRrAQN2Mr/frKeK8NE1cMji32mJbBqpWtK
|
||||||
|
/+wC+avPplBUbNpzP53cuTMF/QssxItPGNP5/OT9Aj1BxA/NofWZKh4ufV7cz3pY
|
||||||
|
RDS4BF+EEFQ4l5GY+yp4WJA/xSvYsTHWeWxRD1/nl62/Rd9FN2NkacRVozCxRVle
|
||||||
|
FrBHTFxqIP6kDnxiLElBrZngtY07ietaYZVLQN/ETyqLQftsf8TecwTklbjvm8NT
|
||||||
|
JqbaIVifYwqwNN+4lRxS3F5lNlA/il12IOgbRioLI62o8G0DaEUQgHNf8vSG
|
||||||
|
-----END CERTIFICATE-----
|
210
util/https-fetch/src/client.rs
Normal file
210
util/https-fetch/src/client.rs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::{fs, str, thread};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use mio;
|
||||||
|
use tlsclient::{TlsClient, TlsClientError};
|
||||||
|
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FetchError {
|
||||||
|
InvalidAddress,
|
||||||
|
ReadingCaCertificates,
|
||||||
|
CaCertificates(io::Error),
|
||||||
|
Io(io::Error),
|
||||||
|
Notify(mio::NotifyError<ClientMessage>),
|
||||||
|
Client(TlsClientError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for FetchError {
|
||||||
|
fn from(e: io::Error) -> Self {
|
||||||
|
FetchError::Io(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mio::NotifyError<ClientMessage>> for FetchError {
|
||||||
|
fn from(e: mio::NotifyError<ClientMessage>) -> Self {
|
||||||
|
FetchError::Notify(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TlsClientError> for FetchError {
|
||||||
|
fn from(e: TlsClientError) -> Self {
|
||||||
|
FetchError::Client(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type FetchResult = Result<(), FetchError>;
|
||||||
|
|
||||||
|
pub enum ClientMessage {
|
||||||
|
Fetch(Url, Box<io::Write + Send>, Arc<AtomicBool>, Box<FnMut(FetchResult) + Send>),
|
||||||
|
Shutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
channel: mio::Sender<ClientMessage>,
|
||||||
|
thread: Option<thread::JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Client {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.close_internal();
|
||||||
|
if let Some(thread) = self.thread.take() {
|
||||||
|
thread.join().expect("Clean shutdown.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new() -> Result<Self, FetchError> {
|
||||||
|
let mut event_loop = try!(mio::EventLoop::new());
|
||||||
|
let channel = event_loop.channel();
|
||||||
|
|
||||||
|
let thread = thread::spawn(move || {
|
||||||
|
let mut client = ClientLoop {
|
||||||
|
next_token: 0,
|
||||||
|
sessions: HashMap::new(),
|
||||||
|
};
|
||||||
|
event_loop.run(&mut client).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Client {
|
||||||
|
channel: channel,
|
||||||
|
thread: Some(thread),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_to_file<F: FnOnce(FetchResult) + Send + 'static>(&self, url: Url, path: PathBuf, abort: Arc<AtomicBool>, callback: F) -> Result<(), FetchError> {
|
||||||
|
let file = try!(fs::File::create(&path));
|
||||||
|
self.fetch(url, Box::new(file), abort, move |result| {
|
||||||
|
if let Err(_) = result {
|
||||||
|
// remove temporary file
|
||||||
|
let _ = fs::remove_file(&path);
|
||||||
|
}
|
||||||
|
callback(result);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch<F: FnOnce(FetchResult) + Send + 'static>(&self, url: Url, writer: Box<io::Write + Send>, abort: Arc<AtomicBool>, callback: F) -> Result<(), FetchError> {
|
||||||
|
let cell = RefCell::new(Some(callback));
|
||||||
|
try!(self.channel.send(ClientMessage::Fetch(url, writer, abort, Box::new(move |res| {
|
||||||
|
cell.borrow_mut().take().expect("Called only once.")(res);
|
||||||
|
}))));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(mut self) {
|
||||||
|
self.close_internal()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_internal(&mut self) {
|
||||||
|
if let Err(e) = self.channel.send(ClientMessage::Shutdown) {
|
||||||
|
warn!("Error while closing client: {:?}. Already stopped?", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientLoop {
|
||||||
|
next_token: usize,
|
||||||
|
sessions: HashMap<usize, TlsClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl mio::Handler for ClientLoop {
|
||||||
|
type Timeout = ();
|
||||||
|
type Message = ClientMessage;
|
||||||
|
|
||||||
|
fn ready(&mut self, event_loop: &mut mio::EventLoop<ClientLoop>, token: mio::Token, events: mio::EventSet) {
|
||||||
|
let utoken = token.as_usize();
|
||||||
|
let remove = if let Some(mut tlsclient) = self.sessions.get_mut(&utoken) {
|
||||||
|
tlsclient.ready(event_loop, token, events)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if remove {
|
||||||
|
self.sessions.remove(&utoken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, event_loop: &mut mio::EventLoop<Self>, msg: Self::Message) {
|
||||||
|
match msg {
|
||||||
|
ClientMessage::Shutdown => event_loop.shutdown(),
|
||||||
|
ClientMessage::Fetch(url, writer, abort, callback) => {
|
||||||
|
let token = self.next_token;
|
||||||
|
self.next_token += 1;
|
||||||
|
|
||||||
|
if let Ok(mut tlsclient) = TlsClient::new(mio::Token(token), &url, writer, abort, callback) {
|
||||||
|
let httpreq = format!(
|
||||||
|
"GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept-Encoding: identity\r\n\r\n",
|
||||||
|
url.path(),
|
||||||
|
url.hostname()
|
||||||
|
);
|
||||||
|
let _ = tlsclient.write(httpreq.as_bytes());
|
||||||
|
tlsclient.register(event_loop);
|
||||||
|
|
||||||
|
self.sessions.insert(token, tlsclient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_successfuly_fetch_a_page() {
|
||||||
|
use std::io::{self, Cursor};
|
||||||
|
use std::sync::{mpsc, Arc};
|
||||||
|
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
|
||||||
|
|
||||||
|
struct Writer {
|
||||||
|
wrote: Arc<AtomicUsize>,
|
||||||
|
data: Cursor<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for Writer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let res = self.data.write(buf);
|
||||||
|
if let Ok(count) = res {
|
||||||
|
self.wrote.fetch_add(count, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = Client::new().unwrap();
|
||||||
|
|
||||||
|
let wrote = Arc::new(AtomicUsize::new(0));
|
||||||
|
let writer = Writer {
|
||||||
|
wrote: wrote.clone(),
|
||||||
|
data: Cursor::new(Vec::new()),
|
||||||
|
};
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
client.fetch(Url::new("github.com", 443, "/").unwrap(), Box::new(writer), Arc::new(AtomicBool::new(false)), move |result| {
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert!(wrote.load(Ordering::Relaxed) > 0);
|
||||||
|
tx.send(result).unwrap();
|
||||||
|
}).unwrap();
|
||||||
|
let _ = rx.recv().unwrap();
|
||||||
|
}
|
||||||
|
|
334
util/https-fetch/src/http.rs
Normal file
334
util/https-fetch/src/http.rs
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! HTTP format processor
|
||||||
|
|
||||||
|
use std::io::{self, Cursor, Write};
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum State {
|
||||||
|
WaitingForStatus,
|
||||||
|
WaitingForHeaders,
|
||||||
|
WaitingForChunk,
|
||||||
|
WritingBody,
|
||||||
|
WritingChunk(usize),
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HttpProcessor {
|
||||||
|
state: State,
|
||||||
|
buffer: Cursor<Vec<u8>>,
|
||||||
|
status: Option<String>,
|
||||||
|
headers: Vec<String>,
|
||||||
|
body_writer: io::BufWriter<Box<io::Write>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BREAK_LEN: usize = 2;
|
||||||
|
|
||||||
|
impl HttpProcessor {
|
||||||
|
pub fn new(body_writer: Box<io::Write>) -> Self {
|
||||||
|
HttpProcessor {
|
||||||
|
state: State::WaitingForStatus,
|
||||||
|
buffer: Cursor::new(Vec::new()),
|
||||||
|
status: None,
|
||||||
|
headers: Vec::new(),
|
||||||
|
body_writer: io::BufWriter::new(body_writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn status(&self) -> Option<&String> {
|
||||||
|
self.status.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn headers(&self) -> &[String] {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_break_index(&mut self) -> Option<usize> {
|
||||||
|
let data = self.buffer.get_ref();
|
||||||
|
let mut idx = 0;
|
||||||
|
let mut got_r = false;
|
||||||
|
// looks for \r\n in data
|
||||||
|
for b in data {
|
||||||
|
idx += 1;
|
||||||
|
if got_r && b == &10u8 {
|
||||||
|
return Some(idx);
|
||||||
|
} else if !got_r && b == &13u8 {
|
||||||
|
got_r = true;
|
||||||
|
} else {
|
||||||
|
got_r = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumes bytes from internal buffer
|
||||||
|
fn buffer_consume(&mut self, bytes: usize) {
|
||||||
|
let bytes = cmp::min(bytes, self.buffer.get_ref().len());
|
||||||
|
// Drain data
|
||||||
|
self.buffer.get_mut().drain(0..bytes);
|
||||||
|
let len = self.buffer.position();
|
||||||
|
self.buffer.set_position(len - bytes as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_to_string(&mut self, bytes: usize) -> String {
|
||||||
|
let val = String::from_utf8_lossy(&self.buffer.get_ref()[0..bytes-BREAK_LEN]).into_owned();
|
||||||
|
self.buffer_consume(bytes);
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_chunked(&self) -> bool {
|
||||||
|
self.headers
|
||||||
|
.iter()
|
||||||
|
.find(|item| item.to_lowercase().contains("transfer-encoding: chunked"))
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
fn set_state(&mut self, state: State) {
|
||||||
|
self.state = state;
|
||||||
|
trace!("Changing state to {:?}", state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_buffer(&mut self) -> io::Result<()> {
|
||||||
|
// consume data and perform state transitions
|
||||||
|
loop {
|
||||||
|
match self.state {
|
||||||
|
State::WaitingForStatus => {
|
||||||
|
if let Some(break_index) = self.find_break_index() {
|
||||||
|
let status = self.buffer_to_string(break_index);
|
||||||
|
debug!("Read status: {:?}", status);
|
||||||
|
self.status = Some(status);
|
||||||
|
self.set_state(State::WaitingForHeaders);
|
||||||
|
} else {
|
||||||
|
// wait for more data
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State::WaitingForHeaders => {
|
||||||
|
match self.find_break_index() {
|
||||||
|
// Last header - got empty line, body starts
|
||||||
|
Some(BREAK_LEN) => {
|
||||||
|
self.buffer_consume(BREAK_LEN);
|
||||||
|
let is_chunked = self.is_chunked();
|
||||||
|
self.set_state(match is_chunked {
|
||||||
|
true => State::WaitingForChunk,
|
||||||
|
false => State::WritingBody,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Some(break_index) => {
|
||||||
|
let header = self.buffer_to_string(break_index);
|
||||||
|
debug!("Found header: {:?}", header);
|
||||||
|
self.headers.push(header);
|
||||||
|
},
|
||||||
|
None => return Ok(()),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State::WritingBody => {
|
||||||
|
let len = self.buffer.get_ref().len();
|
||||||
|
try!(self.body_writer.write_all(self.buffer.get_ref()));
|
||||||
|
self.buffer_consume(len);
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
State::WaitingForChunk => {
|
||||||
|
match self.find_break_index() {
|
||||||
|
None => return Ok(()),
|
||||||
|
// last chunk - size 0
|
||||||
|
Some(BREAK_LEN) => {
|
||||||
|
self.state = State::Finished;
|
||||||
|
},
|
||||||
|
Some(break_index) => {
|
||||||
|
let chunk_size = self.buffer_to_string(break_index);
|
||||||
|
self.set_state(if let Ok(size) = usize::from_str_radix(&chunk_size, 16) {
|
||||||
|
State::WritingChunk(size)
|
||||||
|
} else {
|
||||||
|
warn!("Error parsing server chunked response. Invalid chunk size.");
|
||||||
|
State::Finished
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State::WritingChunk(0) => {
|
||||||
|
self.set_state(State::Finished);
|
||||||
|
},
|
||||||
|
// Buffers the data until we have a full chunk
|
||||||
|
State::WritingChunk(left) if self.buffer.get_ref().len() >= left => {
|
||||||
|
try!(self.body_writer.write_all(&self.buffer.get_ref()[0..left]));
|
||||||
|
self.buffer_consume(left + BREAK_LEN);
|
||||||
|
|
||||||
|
self.set_state(State::WaitingForChunk);
|
||||||
|
},
|
||||||
|
// Wait for more data
|
||||||
|
State::WritingChunk(_) => return Ok(()),
|
||||||
|
// Just consume buffer
|
||||||
|
State::Finished => {
|
||||||
|
let len = self.buffer.get_ref().len();
|
||||||
|
self.buffer_consume(len);
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn state(&self) -> State {
|
||||||
|
self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for HttpProcessor {
|
||||||
|
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||||
|
let result = self.buffer.write(bytes);
|
||||||
|
try!(self.process_buffer());
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.buffer.flush().and_then(|_| {
|
||||||
|
self.body_writer.flush()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::io::{self, Write, Cursor};
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Writer {
|
||||||
|
data: Rc<RefCell<Cursor<Vec<u8>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Writer {
|
||||||
|
fn new() -> (Self, Rc<RefCell<Cursor<Vec<u8>>>>) {
|
||||||
|
let data = Rc::new(RefCell::new(Cursor::new(Vec::new())));
|
||||||
|
(Writer { data: data.clone() }, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Writer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.data.borrow_mut().write(buf) }
|
||||||
|
fn flush(&mut self) -> io::Result<()> { self.data.borrow_mut().flush() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_be_able_to_process_status_line() {
|
||||||
|
// given
|
||||||
|
let mut http = HttpProcessor::new(Box::new(Cursor::new(Vec::new())));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let out =
|
||||||
|
"\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
Server: Pari
|
||||||
|
";
|
||||||
|
http.write_all(out.as_bytes()).unwrap();
|
||||||
|
http.flush().unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(http.status().unwrap(), "HTTP/1.1 200 OK");
|
||||||
|
assert_eq!(http.state(), State::WaitingForHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_be_able_to_process_headers() {
|
||||||
|
// given
|
||||||
|
let mut http = HttpProcessor::new(Box::new(Cursor::new(Vec::new())));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let out =
|
||||||
|
"\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
Server: Parity/1.1.1\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
Content-Length: 2\r\n\
|
||||||
|
Content-Type: application/json\r\n\
|
||||||
|
\r\n\
|
||||||
|
";
|
||||||
|
http.write_all(out.as_bytes()).unwrap();
|
||||||
|
http.flush().unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(http.status().unwrap(), "HTTP/1.1 200 OK");
|
||||||
|
assert_eq!(http.headers().len(), 4);
|
||||||
|
assert_eq!(http.state(), State::WritingBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_be_able_to_consume_body() {
|
||||||
|
// given
|
||||||
|
let (writer, data) = Writer::new();
|
||||||
|
let mut http = HttpProcessor::new(Box::new(writer));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let out =
|
||||||
|
"\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
Server: Parity/1.1.1\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
Content-Length: 2\r\n\
|
||||||
|
Content-Type: application/json\r\n\
|
||||||
|
\r\n\
|
||||||
|
Some data\
|
||||||
|
";
|
||||||
|
http.write_all(out.as_bytes()).unwrap();
|
||||||
|
http.flush().unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(http.status().unwrap(), "HTTP/1.1 200 OK");
|
||||||
|
assert_eq!(http.headers().len(), 4);
|
||||||
|
assert_eq!(http.state(), State::WritingBody);
|
||||||
|
assert_eq!(data.borrow().get_ref()[..], b"Some data"[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_correctly_handle_chunked_content() {
|
||||||
|
// given
|
||||||
|
let (writer, data) = Writer::new();
|
||||||
|
let mut http = HttpProcessor::new(Box::new(writer));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let out =
|
||||||
|
"\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
Host: 127.0.0.1:8080\r\n\
|
||||||
|
Transfer-Encoding: chunked\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
4\r\n\
|
||||||
|
Pari\r\n\
|
||||||
|
3\r\n\
|
||||||
|
ty \r\n\
|
||||||
|
D\r\n\
|
||||||
|
in\r\n\
|
||||||
|
\r\n\
|
||||||
|
chunks.\r\n\
|
||||||
|
0\r\n\
|
||||||
|
\r\n\
|
||||||
|
";
|
||||||
|
http.write_all(out.as_bytes()).unwrap();
|
||||||
|
http.flush().unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(http.status().unwrap(), "HTTP/1.1 200 OK");
|
||||||
|
assert_eq!(http.headers().len(), 3);
|
||||||
|
assert_eq!(data.borrow().get_ref()[..], b"Parity in\r\n\r\nchunks."[..]);
|
||||||
|
assert_eq!(http.state(), State::Finished);
|
||||||
|
}
|
||||||
|
}
|
28
util/https-fetch/src/lib.rs
Normal file
28
util/https-fetch/src/lib.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustls;
|
||||||
|
extern crate mio;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
|
mod tlsclient;
|
||||||
|
mod client;
|
||||||
|
mod url;
|
||||||
|
mod http;
|
||||||
|
|
||||||
|
pub use self::client::{Client, FetchError, FetchResult};
|
||||||
|
pub use self::url::{Url, UrlError};
|
||||||
|
|
247
util/https-fetch/src/tlsclient.rs
Normal file
247
util/https-fetch/src/tlsclient.rs
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::str;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::io::{self, Write, Read, Cursor, BufReader};
|
||||||
|
|
||||||
|
use mio;
|
||||||
|
use mio::tcp::TcpStream;
|
||||||
|
use rustls::{self, Session};
|
||||||
|
|
||||||
|
use url::Url;
|
||||||
|
use http::HttpProcessor;
|
||||||
|
use client::{FetchError, ClientLoop, FetchResult};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TlsClientError {
|
||||||
|
Aborted,
|
||||||
|
Initialization,
|
||||||
|
UnexpectedEof,
|
||||||
|
Connection(io::Error),
|
||||||
|
Writer(io::Error),
|
||||||
|
Tls(rustls::TLSError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This encapsulates the TCP-level connection, some connection
|
||||||
|
/// state, and the underlying TLS-level session.
|
||||||
|
pub struct TlsClient {
|
||||||
|
abort: Arc<AtomicBool>,
|
||||||
|
token: mio::Token,
|
||||||
|
socket: TcpStream,
|
||||||
|
tls_session: rustls::ClientSession,
|
||||||
|
writer: HttpProcessor,
|
||||||
|
error: Option<TlsClientError>,
|
||||||
|
closing: bool,
|
||||||
|
callback: Box<FnMut(FetchResult) + Send>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for TlsClient {
|
||||||
|
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||||
|
self.tls_session.write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.tls_session.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Read for TlsClient {
|
||||||
|
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.tls_session.read(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ca-github-only")]
|
||||||
|
static CA_CERTS: &'static [u8] = include_bytes!("./ca-github.crt");
|
||||||
|
#[cfg(not(feature = "ca-github-only"))]
|
||||||
|
static CA_CERTS: &'static [u8] = include_bytes!("./ca-certificates.crt");
|
||||||
|
|
||||||
|
impl TlsClient {
|
||||||
|
pub fn make_config() -> Result<Arc<rustls::ClientConfig>, FetchError> {
|
||||||
|
let mut config = rustls::ClientConfig::new();
|
||||||
|
let mut cursor = Cursor::new(CA_CERTS.to_vec());
|
||||||
|
let mut reader = BufReader::new(&mut cursor);
|
||||||
|
try!(config.root_store.add_pem_file(&mut reader).map_err(|_| FetchError::ReadingCaCertificates));
|
||||||
|
// TODO [ToDr] client certificate?
|
||||||
|
Ok(Arc::new(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
token: mio::Token,
|
||||||
|
url: &Url,
|
||||||
|
writer: Box<io::Write + Send>,
|
||||||
|
abort: Arc<AtomicBool>,
|
||||||
|
mut callback: Box<FnMut(FetchResult) + Send>,
|
||||||
|
) -> Result<Self, FetchError> {
|
||||||
|
let res = TlsClient::make_config().and_then(|cfg| {
|
||||||
|
TcpStream::connect(url.address()).map(|sock| {
|
||||||
|
(cfg, sock)
|
||||||
|
}).map_err(Into::into)
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok((cfg, sock)) => Ok(TlsClient {
|
||||||
|
abort: abort,
|
||||||
|
token: token,
|
||||||
|
writer: HttpProcessor::new(writer),
|
||||||
|
socket: sock,
|
||||||
|
closing: false,
|
||||||
|
error: None,
|
||||||
|
tls_session: rustls::ClientSession::new(&cfg, url.hostname()),
|
||||||
|
callback: callback,
|
||||||
|
}),
|
||||||
|
Err(e) => {
|
||||||
|
callback(Err(e));
|
||||||
|
Err(FetchError::Client(TlsClientError::Initialization))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by mio each time events we register() for happen.
|
||||||
|
/// Return false if reregistering again.
|
||||||
|
pub fn ready(&mut self, event_loop: &mut mio::EventLoop<ClientLoop>, token: mio::Token, events: mio::EventSet) -> bool {
|
||||||
|
assert_eq!(token, self.token);
|
||||||
|
|
||||||
|
let aborted = self.is_aborted();
|
||||||
|
if aborted {
|
||||||
|
// do_write needs to be invoked after that
|
||||||
|
self.tls_session.send_close_notify();
|
||||||
|
self.error = Some(TlsClientError::Aborted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if events.is_readable() {
|
||||||
|
self.do_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if events.is_writable() {
|
||||||
|
self.do_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_closed() || aborted {
|
||||||
|
trace!("Connection closed");
|
||||||
|
let callback = &mut self.callback;
|
||||||
|
callback(match self.error.take() {
|
||||||
|
Some(err) => Err(err.into()),
|
||||||
|
None => Ok(()),
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.reregister(event_loop);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(&mut self, event_loop: &mut mio::EventLoop<ClientLoop>) {
|
||||||
|
event_loop.register(
|
||||||
|
&self.socket,
|
||||||
|
self.token,
|
||||||
|
self.event_set(),
|
||||||
|
mio::PollOpt::level() | mio::PollOpt::oneshot()
|
||||||
|
).unwrap_or_else(|e| self.error = Some(TlsClientError::Connection(e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reregister(&mut self, event_loop: &mut mio::EventLoop<ClientLoop>) {
|
||||||
|
event_loop.reregister(
|
||||||
|
&self.socket,
|
||||||
|
self.token,
|
||||||
|
self.event_set(),
|
||||||
|
mio::PollOpt::level() | mio::PollOpt::oneshot()
|
||||||
|
).unwrap_or_else(|e| self.error = Some(TlsClientError::Connection(e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We're ready to do a read.
|
||||||
|
fn do_read(&mut self) {
|
||||||
|
// Read TLS data. This fails if the underlying TCP connection is broken.
|
||||||
|
let rc = self.tls_session.read_tls(&mut self.socket);
|
||||||
|
if let Err(e) = rc {
|
||||||
|
trace!("TLS read error: {:?}", e);
|
||||||
|
self.closing = true;
|
||||||
|
self.error = Some(TlsClientError::Connection(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're ready but there's no data: EOF.
|
||||||
|
if rc.unwrap() == 0 {
|
||||||
|
trace!("Unexpected EOF");
|
||||||
|
self.error = Some(TlsClientError::UnexpectedEof);
|
||||||
|
self.closing = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading some TLS data might have yielded new TLS messages to process.
|
||||||
|
// Errors from this indicate TLS protocol problems and are fatal.
|
||||||
|
let processed = self.tls_session.process_new_packets();
|
||||||
|
if let Err(e) = processed {
|
||||||
|
trace!("TLS error: {:?}", e);
|
||||||
|
self.error = Some(TlsClientError::Tls(e));
|
||||||
|
self.closing = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having read some TLS data, and processed any new messages, we might have new plaintext as a result.
|
||||||
|
// Read it and then write it to stdout.
|
||||||
|
let mut plaintext = Vec::new();
|
||||||
|
let rc = self.tls_session.read_to_end(&mut plaintext);
|
||||||
|
if !plaintext.is_empty() {
|
||||||
|
self.writer.write(&plaintext).unwrap_or_else(|e| {
|
||||||
|
trace!("Write error: {:?}", e);
|
||||||
|
self.error = Some(TlsClientError::Writer(e));
|
||||||
|
0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If that fails, the peer might have started a clean TLS-level session closure.
|
||||||
|
if let Err(err) = rc {
|
||||||
|
if err.kind() != io::ErrorKind::ConnectionAborted {
|
||||||
|
self.error = Some(TlsClientError::Connection(err));
|
||||||
|
}
|
||||||
|
self.closing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_write(&mut self) {
|
||||||
|
self.tls_session.write_tls(&mut self.socket).unwrap_or_else(|e| {
|
||||||
|
warn!("TLS write error: {:?}", e);
|
||||||
|
0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use wants_read/wants_write to register for different mio-level IO readiness events.
|
||||||
|
fn event_set(&self) -> mio::EventSet {
|
||||||
|
let rd = self.tls_session.wants_read();
|
||||||
|
let wr = self.tls_session.wants_write();
|
||||||
|
|
||||||
|
if rd && wr {
|
||||||
|
mio::EventSet::readable() | mio::EventSet::writable()
|
||||||
|
} else if wr {
|
||||||
|
mio::EventSet::writable()
|
||||||
|
} else {
|
||||||
|
mio::EventSet::readable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_closed(&self) -> bool {
|
||||||
|
self.closing
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_aborted(&self) -> bool {
|
||||||
|
self.abort.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
81
util/https-fetch/src/url.rs
Normal file
81
util/https-fetch/src/url.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UrlError {
|
||||||
|
InvalidAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a ClientConfig from our arguments
|
||||||
|
pub struct Url {
|
||||||
|
address: SocketAddr,
|
||||||
|
hostname: String,
|
||||||
|
port: u16,
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Url {
|
||||||
|
pub fn new(hostname: &str, port: u16, path: &str) -> Result<Self, UrlError> {
|
||||||
|
let addr = try!(Self::lookup_ipv4(hostname, port));
|
||||||
|
Ok(Url {
|
||||||
|
address: addr,
|
||||||
|
hostname: hostname.into(),
|
||||||
|
port: port,
|
||||||
|
path: path.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_ipv4(host: &str, port: u16) -> Result<SocketAddr, UrlError> {
|
||||||
|
use std::net::ToSocketAddrs;
|
||||||
|
|
||||||
|
let addrs = try!((host, port).to_socket_addrs().map_err(|_| UrlError::InvalidAddress));
|
||||||
|
for addr in addrs {
|
||||||
|
if let SocketAddr::V4(_) = addr {
|
||||||
|
return Ok(addr.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(UrlError::InvalidAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address(&self) -> &SocketAddr {
|
||||||
|
&self.address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hostname(&self) -> &str {
|
||||||
|
&self.hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> &str {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[test]
|
||||||
|
fn should_parse_url() {
|
||||||
|
// given
|
||||||
|
let url = Url::new("github.com", 443, "/").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(url.hostname(), "github.com");
|
||||||
|
assert_eq!(url.port(), 443);
|
||||||
|
assert_eq!(url.path(), "/");
|
||||||
|
}
|
@ -69,7 +69,7 @@ impl<T> Hashable for T where T: AsRef<[u8]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate SHA3 of given stream.
|
/// Calculate SHA3 of given stream.
|
||||||
pub fn sha3<R: io::Read>(r: &mut R) -> Result<H256, io::Error> {
|
pub fn sha3(r: &mut io::BufRead) -> Result<H256, io::Error> {
|
||||||
let mut output = [0u8; 32];
|
let mut output = [0u8; 32];
|
||||||
let mut input = [0u8; 1024];
|
let mut input = [0u8; 1024];
|
||||||
let mut sha3 = Keccak::new_keccak256();
|
let mut sha3 = Keccak::new_keccak256();
|
||||||
@ -90,7 +90,7 @@ pub fn sha3<R: io::Read>(r: &mut R) -> Result<H256, io::Error> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::{Write, BufReader};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -113,7 +113,7 @@ mod tests {
|
|||||||
file.write_all(b"something").unwrap();
|
file.write_all(b"something").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file = fs::File::open(&path).unwrap();
|
let mut file = BufReader::new(fs::File::open(&path).unwrap());
|
||||||
// when
|
// when
|
||||||
let hash = sha3(&mut file).unwrap();
|
let hash = sha3(&mut file).unwrap();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user