diff --git a/Cargo.lock b/Cargo.lock index 35203084c0ce..d5d517df1882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,32 +224,18 @@ dependencies = [ "futures-core", ] -[[package]] -name = "async-executor" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f47c78ea98277cb1f5e6f60ba4fc762f5eafe9f6511bc2f7dfd8b75c225650" -dependencies = [ - "async-io 0.1.5", - "futures-lite 0.1.10", - "multitask", - "parking 1.0.5", - "scoped-tls", - "waker-fn", -] - [[package]] name = "async-executor" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801" dependencies = [ - "async-task 4.0.3", + "async-task", "concurrent-queue", "fastrand", - "futures-lite 1.11.2", + "futures-lite", "once_cell 1.4.1", - "vec-arena 1.0.0", + "vec-arena", ] [[package]] @@ -258,31 +244,13 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "124ac8c265e407641c3362b8f4d39cdb4e243885b71eef087be27199790f5a3a" dependencies = [ - "async-executor 1.3.0", - "async-io 1.1.0", - "futures-lite 1.11.2", + "async-executor", + "async-io", + "futures-lite", "num_cpus", "once_cell 1.4.1", ] -[[package]] -name = "async-io" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8126ef9fb99355c6fd27575d691be4887b884137a5b6f48c2d961f13590c51" -dependencies = [ - "cfg-if", - "concurrent-queue", - "futures-lite 0.1.10", - "libc", - "once_cell 1.4.1", - "parking 1.0.5", - "socket2", - "vec-arena 0.5.0", - "wepoll-sys-stjepang", - "winapi 0.3.9", -] - [[package]] name = "async-io" version = "1.1.0" @@ -292,14 +260,14 @@ dependencies = [ "cfg-if", "concurrent-queue", "fastrand", - "futures-lite 1.11.2", + "futures-lite", "libc", "log 0.4.11", "once_cell 1.4.1", - "parking 2.0.0", + "parking", "polling", "socket2", - "vec-arena 1.0.0", + "vec-arena", "waker-fn", "wepoll-sys-stjepang", "winapi 0.3.9", @@ -321,14 +289,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9fa76751505e8df1c7a77762f60486f60c71bbd9b8557f4da6ad47d083732ed" dependencies = [ "async-global-executor", - "async-io 1.1.0", + "async-io", "async-mutex", - "blocking 1.0.2", + "blocking", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 1.11.2", + "futures-lite", "gloo-timers", "kv-log-macro", "log 0.4.11", @@ -341,12 +309,6 @@ dependencies = [ "wasm-bindgen-futures", ] -[[package]] -name = "async-task" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3" - [[package]] name = "async-task" version = "4.0.3" @@ -598,33 +560,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "blocking" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2468ff7bf85066b4a3678fede6fe66db31846d753ff0adfbfab2c6a6e81612b" -dependencies = [ - "async-channel", - "atomic-waker", - "futures-lite 0.1.10", - "once_cell 1.4.1", - "parking 1.0.5", - "waker-fn", -] - -[[package]] -name = "blocking" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e94bf99b692f54c9d05f97454d3faf11134523fe5b180564a3fb6ed63bcc0a" -dependencies = [ - "async-channel", - "atomic-waker", - "futures-lite 0.1.10", - "once_cell 1.4.1", - "waker-fn", -] - [[package]] name = "blocking" version = "1.0.2" @@ -632,10 +567,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" dependencies = [ "async-channel", - "async-task 4.0.3", + "async-task", "atomic-waker", "fastrand", - "futures-lite 1.11.2", + "futures-lite", "once_cell 1.4.1", ] @@ -1107,32 +1042,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" -[[package]] -name = "derive_more" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "rustc_version", - "syn 0.15.44", -] - -[[package]] -name = "derive_more" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" -dependencies = [ - "lazy_static", - "proc-macro2 0.4.30", - "quote 0.6.13", - "regex", - "rustc_version", - "syn 0.15.44", -] - [[package]] name = "derive_more" version = "0.99.11" @@ -1241,12 +1150,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c53dc3a653e0f64081026e4bf048d48fec9fce90c66e8326ca7292df0ff2d82" -[[package]] -name = "easy-parallel" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd4afd79212583ff429b913ad6605242ed7eec277e950b1438f300748f948f4" - [[package]] name = "ed25519" version = "1.0.1" @@ -1448,15 +1351,16 @@ dependencies = [ [[package]] name = "femme" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b6b21baebbed15551f2170010ca4101b9ed3fdc05822791c8bd4631840eab81" +checksum = "2af1a24f391a5a94d756db5092c6576aad494b88a71a5a36b20c67b63e0df034" dependencies = [ "cfg-if", "js-sys", "log 0.4.11", "serde", "serde_derive", + "serde_json", "wasm-bindgen", "web-sys", ] @@ -1829,21 +1733,6 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" -[[package]] -name = "futures-lite" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe71459749b2e8e66fb95df721b22fa08661ad384a0c5b519e11d3893b4692a" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking 1.0.5", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "1.11.2" @@ -1854,7 +1743,7 @@ dependencies = [ "futures-core", "futures-io", "memchr", - "parking 2.0.0", + "parking", "pin-project-lite", "waker-fn", ] @@ -3641,17 +3530,6 @@ dependencies = [ "unsigned-varint 0.4.0", ] -[[package]] -name = "multitask" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c09c35271e7dcdb5f709779111f2c8e8ab8e06c1b587c1c6a9e179d865aaa5b4" -dependencies = [ - "async-task 3.0.0", - "concurrent-queue", - "fastrand", -] - [[package]] name = "nalgebra" version = "0.18.1" @@ -4544,12 +4422,6 @@ dependencies = [ "url 2.1.1", ] -[[package]] -name = "parking" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d4a6da31f8144a32532fe38fe8fb439a6842e0ec633f0037f0144c14e7f907" - [[package]] name = "parking" version = "2.0.0" @@ -4784,25 +4656,18 @@ dependencies = [ "bitvec", "env_logger 0.7.1", "futures 0.3.5", - "futures-timer 3.0.2", "log 0.4.11", "maplit", "parity-scale-codec", - "parking_lot 0.11.0", - "polkadot-network-bridge", "polkadot-node-network-protocol", - "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-util", "polkadot-primitives", "sc-keystore", - "sc-network", - "smol 0.3.3", "sp-application-crypto", "sp-core", "sp-keystore", - "streamunordered", "tempfile", ] @@ -4811,16 +4676,12 @@ name = "polkadot-availability-distribution" version = "0.1.0" dependencies = [ "assert_matches", - "bitvec", - "derive_more 0.99.11", "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", "parity-scale-codec", - "parking_lot 0.11.0", "polkadot-erasure-coding", - "polkadot-network-bridge", "polkadot-node-network-protocol", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", @@ -4832,7 +4693,7 @@ dependencies = [ "sp-core", "sp-keyring", "sp-keystore", - "streamunordered", + "thiserror", ] [[package]] @@ -4840,23 +4701,16 @@ name = "polkadot-cli" version = "0.8.25" dependencies = [ "frame-benchmarking-cli", - "futures 0.3.5", "log 0.4.11", "polkadot-service", "sc-cli", - "sc-client-api", - "sc-client-db", - "sc-executor", "sc-service", "sc-tracing", - "sp-api", "sp-core", - "sp-runtime", "sp-trie", "structopt", "substrate-browser-utils", "substrate-build-script-utils", - "tokio 0.2.21", "wasm-bindgen", "wasm-bindgen-futures", ] @@ -4866,22 +4720,19 @@ name = "polkadot-collator-protocol" version = "0.1.0" dependencies = [ "assert_matches", - "derive_more 0.99.11", "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", - "parity-scale-codec", - "polkadot-network-bridge", "polkadot-node-network-protocol", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-util", "polkadot-primitives", "smallvec 1.4.2", - "smol-timeout", "sp-core", "sp-keyring", + "thiserror", ] [[package]] @@ -4898,12 +4749,12 @@ dependencies = [ name = "polkadot-erasure-coding" version = "0.8.25" dependencies = [ - "derive_more 0.15.0", "parity-scale-codec", "polkadot-primitives", "reed-solomon-erasure", "sp-core", "sp-trie", + "thiserror", ] [[package]] @@ -4913,7 +4764,6 @@ dependencies = [ "assert_matches", "async-trait", "futures 0.3.5", - "futures-timer 3.0.2", "log 0.4.11", "parity-scale-codec", "parking_lot 0.10.2", @@ -4926,14 +4776,12 @@ dependencies = [ "sp-core", "sp-keyring", "sp-runtime", - "streamunordered", ] [[package]] name = "polkadot-node-collation-generation" version = "0.1.0" dependencies = [ - "derive_more 0.99.11", "futures 0.3.5", "log 0.4.11", "polkadot-erasure-coding", @@ -4943,6 +4791,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "sp-core", + "thiserror", ] [[package]] @@ -4950,7 +4799,6 @@ name = "polkadot-node-core-av-store" version = "0.1.0" dependencies = [ "assert_matches", - "derive_more 0.99.11", "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", @@ -4967,6 +4815,7 @@ dependencies = [ "polkadot-primitives", "smallvec 1.4.2", "sp-core", + "thiserror", ] [[package]] @@ -4975,7 +4824,6 @@ version = "0.1.0" dependencies = [ "assert_matches", "bitvec", - "derive_more 0.99.11", "futures 0.3.5", "log 0.4.11", "polkadot-erasure-coding", @@ -4985,14 +4833,12 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-statement-table", - "sc-client-api", "sc-keystore", - "sp-api", "sp-application-crypto", - "sp-blockchain", "sp-core", "sp-keyring", "sp-keystore", + "thiserror", ] [[package]] @@ -5000,13 +4846,13 @@ name = "polkadot-node-core-bitfield-signing" version = "0.1.0" dependencies = [ "bitvec", - "derive_more 0.99.11", "futures 0.3.5", "log 0.4.11", "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", "sp-keystore", + "thiserror", "wasm-timer", ] @@ -5014,7 +4860,6 @@ dependencies = [ name = "polkadot-node-core-candidate-selection" version = "0.1.0" dependencies = [ - "derive_more 0.99.11", "futures 0.3.5", "log 0.4.11", "polkadot-node-primitives", @@ -5022,6 +4867,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "sp-core", + "thiserror", ] [[package]] @@ -5029,7 +4875,6 @@ name = "polkadot-node-core-candidate-validation" version = "0.1.0" dependencies = [ "assert_matches", - "derive_more 0.99.11", "futures 0.3.5", "log 0.4.11", "parity-scale-codec", @@ -5039,7 +4884,6 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-parachain", "polkadot-primitives", - "sp-blockchain", "sp-core", "sp-keyring", ] @@ -5063,16 +4907,13 @@ name = "polkadot-node-core-proposer" version = "0.1.0" dependencies = [ "futures 0.3.5", - "futures-timer 3.0.2", "log 0.4.11", - "parity-scale-codec", "polkadot-node-subsystem", "polkadot-overseer", "polkadot-primitives", "sc-basic-authorship", "sc-block-builder", "sc-client-api", - "sc-telemetry", "sp-api", "sp-blockchain", "sp-consensus", @@ -5080,7 +4921,6 @@ dependencies = [ "sp-inherents", "sp-runtime", "sp-transaction-pool", - "tokio-executor 0.2.0-alpha.6", "wasm-timer", ] @@ -5089,19 +4929,17 @@ name = "polkadot-node-core-provisioner" version = "0.1.0" dependencies = [ "bitvec", - "derive_more 0.99.11", "futures 0.3.5", "futures-timer 3.0.2", - "lazy_static", "log 0.4.11", "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", "sc-keystore", "sp-application-crypto", - "sp-core", "sp-keystore", "tempfile", + "thiserror", ] [[package]] @@ -5109,13 +4947,11 @@ name = "polkadot-node-core-runtime-api" version = "0.1.0" dependencies = [ "futures 0.3.5", - "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-util", "polkadot-primitives", "sp-api", - "sp-blockchain", "sp-core", ] @@ -5127,8 +4963,6 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "sc-network", - "sp-core", - "sp-runtime", ] [[package]] @@ -5149,7 +4983,7 @@ version = "0.1.0" dependencies = [ "assert_matches", "async-trait", - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", @@ -5164,6 +4998,7 @@ dependencies = [ "sc-network", "smallvec 1.4.2", "sp-core", + "thiserror", ] [[package]] @@ -5171,7 +5006,6 @@ name = "polkadot-node-subsystem-test-helpers" version = "0.1.0" dependencies = [ "async-trait", - "derive_more 0.99.11", "futures 0.3.5", "futures-timer 3.0.2", "log 0.4.11", @@ -5195,7 +5029,6 @@ version = "0.1.0" dependencies = [ "assert_matches", "async-trait", - "derive_more 0.99.11", "env_logger 0.7.1", "futures 0.3.5", "futures-timer 3.0.2", @@ -5207,14 +5040,13 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", "polkadot-primitives", - "polkadot-statement-table", "sc-network", - "smallvec 1.4.2", "sp-application-crypto", "sp-core", "sp-keystore", "streamunordered", "substrate-prometheus-endpoint", + "thiserror", ] [[package]] @@ -5241,7 +5073,7 @@ dependencies = [ name = "polkadot-parachain" version = "0.8.25" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "log 0.4.11", "parity-scale-codec", @@ -5264,19 +5096,13 @@ version = "0.1.0" dependencies = [ "assert_matches", "futures 0.3.5", - "futures-timer 3.0.2", "log 0.4.11", - "parity-scale-codec", - "parking_lot 0.10.2", "polkadot-node-network-protocol", - "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", "polkadot-node-subsystem-util", "polkadot-primitives", "sp-core", - "sp-runtime", - "streamunordered", ] [[package]] @@ -5570,11 +5396,8 @@ dependencies = [ "arrayvec 0.5.1", "assert_matches", "futures 0.3.5", - "futures-timer 3.0.2", "indexmap", "log 0.4.11", - "parity-scale-codec", - "parking_lot 0.10.2", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -5586,9 +5409,7 @@ dependencies = [ "sp-core", "sp-keyring", "sp-keystore", - "sp-runtime", "sp-staking", - "streamunordered", ] [[package]] @@ -5736,7 +5557,6 @@ dependencies = [ name = "polkadot-validation" version = "0.8.25" dependencies = [ - "derive_more 0.14.1", "futures 0.3.5", "log 0.4.11", "parity-scale-codec", @@ -5758,6 +5578,7 @@ dependencies = [ "sp-transaction-pool", "sp-trie", "substrate-prometheus-endpoint", + "thiserror", ] [[package]] @@ -6590,7 +6411,7 @@ source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e2 dependencies = [ "async-trait", "bytes 0.5.6", - "derive_more 0.99.11", + "derive_more", "either", "futures 0.3.5", "futures-timer 3.0.2", @@ -6695,7 +6516,7 @@ dependencies = [ "atty", "bip39", "chrono", - "derive_more 0.99.11", + "derive_more", "fdlimit", "futures 0.3.5", "hex", @@ -6756,7 +6577,7 @@ name = "sc-client-api" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "fnv", "futures 0.3.5", "hash-db", @@ -6834,7 +6655,7 @@ name = "sc-consensus-babe" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "fork-tree", "futures 0.3.5", "futures-timer 3.0.2", @@ -6879,7 +6700,7 @@ name = "sc-consensus-babe-rpc" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", @@ -6953,7 +6774,7 @@ name = "sc-executor" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "lazy_static", "libsecp256k1", "log 0.4.11", @@ -6982,7 +6803,7 @@ name = "sc-executor-common" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "log 0.4.11", "parity-scale-codec", "parity-wasm", @@ -7032,7 +6853,7 @@ name = "sc-finality-grandpa" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "finality-grandpa", "fork-tree", "futures 0.3.5", @@ -7069,7 +6890,7 @@ name = "sc-finality-grandpa-rpc" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "finality-grandpa", "futures 0.3.5", "jsonrpc-core", @@ -7112,7 +6933,7 @@ version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ "async-trait", - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "futures-util", "hex", @@ -7155,7 +6976,7 @@ dependencies = [ "bitflags", "bs58", "bytes 0.5.6", - "derive_more 0.99.11", + "derive_more", "either", "erased-serde", "fnv", @@ -7301,7 +7122,7 @@ name = "sc-rpc-api" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", @@ -7343,7 +7164,7 @@ name = "sc-service" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "directories", "exit-future", "futures 0.1.29", @@ -7480,7 +7301,7 @@ name = "sc-transaction-graph" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "linked-hash-map", "log 0.4.11", @@ -7501,7 +7322,7 @@ name = "sc-transaction-pool" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "futures-diagnose", "intervalier", @@ -7870,53 +7691,6 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" -[[package]] -name = "smol" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5" -dependencies = [ - "async-task 3.0.0", - "blocking 0.4.7", - "concurrent-queue", - "fastrand", - "futures-io", - "futures-util", - "libc", - "once_cell 1.4.1", - "scoped-tls", - "slab", - "socket2", - "wepoll-sys-stjepang", - "winapi 0.3.9", -] - -[[package]] -name = "smol" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67583f4ccc13bbb105a0752058d8ad66c47753d85445952809bcaca891954f83" -dependencies = [ - "async-channel", - "async-executor 0.1.2", - "async-io 0.1.5", - "blocking 0.5.0", - "cfg-if", - "easy-parallel", - "futures-lite 0.1.10", - "num_cpus", -] - -[[package]] -name = "smol-timeout" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024818c1f00b80e8171ddcfcee33860134293aa3aced60c9cbd7a5a2d41db392" -dependencies = [ - "pin-project", - "smol 0.1.18", -] - [[package]] name = "snow" version = "0.7.1" @@ -7968,7 +7742,7 @@ name = "sp-allocator" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "log 0.4.11", "sp-core", "sp-std", @@ -8067,7 +7841,7 @@ name = "sp-blockchain" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "log 0.4.11", "lru 0.4.3", "parity-scale-codec", @@ -8093,7 +7867,7 @@ name = "sp-consensus" version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "futures-timer 3.0.2", "libp2p", @@ -8250,7 +8024,7 @@ name = "sp-inherents" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "parity-scale-codec", "parking_lot 0.10.2", "sp-core", @@ -8298,7 +8072,7 @@ version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ "async-trait", - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "merlin", "parity-scale-codec", @@ -8525,7 +8299,7 @@ name = "sp-transaction-pool" version = "2.0.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ - "derive_more 0.99.11", + "derive_more", "futures 0.3.5", "log 0.4.11", "parity-scale-codec", @@ -8777,7 +8551,7 @@ version = "0.8.0" source = "git+https://github.com/paritytech/substrate#d766e229466d63afadd19097e277d85146fee3c9" dependencies = [ "async-std", - "derive_more 0.99.11", + "derive_more", "futures-util", "hyper 0.13.6", "log 0.4.11", @@ -8984,18 +8758,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", @@ -9651,12 +9425,6 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" -[[package]] -name = "vec-arena" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17dfb54bf57c9043f4616cb03dab30eff012cc26631b797d8354b916708db919" - [[package]] name = "vec-arena" version = "1.0.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 1060a48ce07a..a51ea2ae86d4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -14,19 +14,12 @@ wasm-opt = false crate-type = ["cdylib", "rlib"] [dependencies] -log = "0.4.8" -futures = { version = "0.3.4", features = ["compat"] } -structopt = "0.3.8" -sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +log = "0.4.11" +structopt = { version = "0.3.8", optional = true } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-client-db = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" } service = { package = "polkadot-service", path = "../node/service", default-features = false, optional = true } -tokio = { version = "0.2.13", features = ["rt-threaded"], optional = true } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } sc-service = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } @@ -46,7 +39,7 @@ default = [ "wasmtime", "db", "cli", "full-node", "trie-memory-tracker" ] wasmtime = [ "sc-cli/wasmtime" ] db = [ "service/db" ] cli = [ - "tokio", + "structopt", "sc-cli", "sc-service", "frame-benchmarking-cli", diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 76c67488a116..43b8da81f969 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -17,7 +17,6 @@ //! Polkadot CLI library. #![warn(missing_docs)] -#![warn(unused_extern_crates)] #[cfg(feature = "browser")] mod browser; diff --git a/erasure-coding/Cargo.toml b/erasure-coding/Cargo.toml index dc7bdf05dfb6..6a8988eb0aeb 100644 --- a/erasure-coding/Cargo.toml +++ b/erasure-coding/Cargo.toml @@ -10,4 +10,4 @@ reed_solomon = { package = "reed-solomon-erasure", version = "4.0.2"} codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } trie = { package = "sp-trie", git = "https://github.com/paritytech/substrate", branch = "master" } -derive_more = "0.15.0" +thiserror = "1.0.21" diff --git a/erasure-coding/src/lib.rs b/erasure-coding/src/lib.rs index 708a167d6276..e20e36c25905 100644 --- a/erasure-coding/src/lib.rs +++ b/erasure-coding/src/lib.rs @@ -30,6 +30,7 @@ use primitives::v0::{self, Hash as H256, BlakeTwo256, HashT}; use primitives::v1; use sp_core::Blake2Hasher; use trie::{EMPTY_PREFIX, MemoryDB, Trie, TrieMut, trie_types::{TrieDBMut, TrieDB}}; +use thiserror::Error; use self::wrapped_shard::WrappedShard; @@ -39,35 +40,43 @@ mod wrapped_shard; const MAX_VALIDATORS: usize = ::ORDER; /// Errors in erasure coding. -#[derive(Debug, Clone, PartialEq, derive_more::Display)] +#[derive(Debug, Clone, PartialEq, Error)] pub enum Error { /// Returned when there are too many validators. + #[error("There are too many validators")] TooManyValidators, /// Cannot encode something for no validators + #[error("Validator set is empty")] EmptyValidators, /// Cannot reconstruct: wrong number of validators. + #[error("Validator count mismatches between encoding and decoding")] WrongValidatorCount, /// Not enough chunks present. + #[error("Not enough chunks to reconstruct message")] NotEnoughChunks, /// Too many chunks present. + #[error("Too many chunks present")] TooManyChunks, /// Chunks not of uniform length or the chunks are empty. + #[error("Chunks are not unform, mismatch in length or are zero sized")] NonUniformChunks, /// An uneven byte-length of a shard is not valid for GF(2^16) encoding. + #[error("Uneven length is not valid for field GF(2^16)")] UnevenLength, /// Chunk index out of bounds. - #[display(fmt = "Chunk is out of bounds: {} {}", _0, _1)] - ChunkIndexOutOfBounds(usize, usize), + #[error("Chunk is out of bounds: {chunk_index} not included in 0..{n_validators}")] + ChunkIndexOutOfBounds{ chunk_index: usize, n_validators: usize }, /// Bad payload in reconstructed bytes. + #[error("Reconstructed payload invalid")] BadPayload, /// Invalid branch proof. + #[error("Invalid branch proof")] InvalidBranchProof, /// Branch out of bounds. + #[error("Branch is out of bounds")] BranchOutOfBounds, } -impl std::error::Error for Error { } - #[derive(Debug, PartialEq)] struct CodeParams { data_shards: usize, @@ -206,7 +215,7 @@ fn reconstruct<'a, I: 'a, T: Decode>(n_validators: usize, chunks: I) -> Result= n_validators { - return Err(Error::ChunkIndexOutOfBounds(chunk_idx, n_validators)); + return Err(Error::ChunkIndexOutOfBounds{ chunk_index: chunk_idx, n_validators }); } let shard_len = shard_len.get_or_insert_with(|| chunk_data.len()); diff --git a/node/collation-generation/Cargo.toml b/node/collation-generation/Cargo.toml index f7d5e7f162ef..0e302ae6884f 100644 --- a/node/collation-generation/Cargo.toml +++ b/node/collation-generation/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -derive_more = "0.99.9" futures = "0.3.5" log = "0.4.8" polkadot-erasure-coding = { path = "../../erasure-coding" } @@ -14,6 +13,7 @@ polkadot-node-subsystem = { path = "../subsystem" } polkadot-node-subsystem-util = { path = "../subsystem-util" } polkadot-primitives = { path = "../../primitives" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +thiserror = "1.0.21" [dev-dependencies] polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } diff --git a/node/collation-generation/src/error.rs b/node/collation-generation/src/error.rs index b256df751e4b..44b08c473f83 100644 --- a/node/collation-generation/src/error.rs +++ b/node/collation-generation/src/error.rs @@ -14,19 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use thiserror::Error; -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] pub enum Error { - #[from] - Subsystem(polkadot_node_subsystem::SubsystemError), - #[from] - OneshotRecv(futures::channel::oneshot::Canceled), - #[from] - Runtime(polkadot_node_subsystem::errors::RuntimeApiError), - #[from] - Util(polkadot_node_subsystem_util::Error), - #[from] - Erasure(polkadot_erasure_coding::Error), + #[error(transparent)] + Subsystem(#[from] polkadot_node_subsystem::SubsystemError), + #[error(transparent)] + OneshotRecv(#[from] futures::channel::oneshot::Canceled), + #[error(transparent)] + Runtime(#[from] polkadot_node_subsystem::errors::RuntimeApiError), + #[error(transparent)] + Util(#[from] polkadot_node_subsystem_util::Error), + #[error(transparent)] + Erasure(#[from] polkadot_erasure_coding::Error), } pub type Result = std::result::Result; diff --git a/node/core/av-store/Cargo.toml b/node/core/av-store/Cargo.toml index 27dd7bd7337b..f13ebae2b62a 100644 --- a/node/core/av-store/Cargo.toml +++ b/node/core/av-store/Cargo.toml @@ -5,12 +5,12 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -derive_more = "0.99.9" futures = "0.3.5" futures-timer = "3.0.2" kvdb = "0.7.0" kvdb-rocksdb = "0.9.1" -log = "0.4.8" +log = "0.4.11" +thiserror = "1.0.21" codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } erasure = { package = "polkadot-erasure-coding", path = "../../../erasure-coding" } diff --git a/node/core/av-store/src/lib.rs b/node/core/av-store/src/lib.rs index e4e26d294035..ce73e02df1f5 100644 --- a/node/core/av-store/src/lib.rs +++ b/node/core/av-store/src/lib.rs @@ -44,6 +44,7 @@ use polkadot_node_subsystem_util::metrics::{self, prometheus}; use polkadot_subsystem::messages::{ AllMessages, AvailabilityStoreMessage, ChainApiMessage, RuntimeApiMessage, RuntimeApiRequest, }; +use thiserror::Error; const LOG_TARGET: &str = "availability"; @@ -53,22 +54,22 @@ mod columns { pub const NUM_COLUMNS: u32 = 2; } -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] enum Error { - #[from] - Chain(ChainApiError), - #[from] - Erasure(erasure::Error), - #[from] - Io(io::Error), - #[from] - Oneshot(oneshot::Canceled), - #[from] - Runtime(RuntimeApiError), - #[from] - Subsystem(SubsystemError), - #[from] - Time(SystemTimeError), + #[error(transparent)] + RuntimeAPI(#[from] RuntimeApiError), + #[error(transparent)] + ChainAPI(#[from] ChainApiError), + #[error(transparent)] + Erasure(#[from] erasure::Error), + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] + Oneshot(#[from] oneshot::Canceled), + #[error(transparent)] + Subsystem(#[from] SubsystemError), + #[error(transparent)] + Time(#[from] SystemTimeError), } /// A wrapper type for delays. diff --git a/node/core/backing/Cargo.toml b/node/core/backing/Cargo.toml index 722f3ce6ae67..8892e201503c 100644 --- a/node/core/backing/Cargo.toml +++ b/node/core/backing/Cargo.toml @@ -6,19 +6,16 @@ edition = "2018" [dependencies] futures = "0.3.5" -sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-primitives = { path = "../../../primitives" } polkadot-node-primitives = { path = "../../primitives" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } erasure-coding = { package = "polkadot-erasure-coding", path = "../../../erasure-coding" } statement-table = { package = "polkadot-statement-table", path = "../../../statement-table" } -derive_more = "0.99.9" bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] } -log = "0.4.8" +log = "0.4.11" +thiserror = "1.0.21" [dev-dependencies] sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/core/backing/src/lib.rs b/node/core/backing/src/lib.rs index 69eb84efe4c6..6c0979618160 100644 --- a/node/core/backing/src/lib.rs +++ b/node/core/backing/src/lib.rs @@ -16,6 +16,8 @@ //! Implements a `CandidateBackingSubsystem`. +#![deny(unused_crate_dependencies)] + use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::pin::Pin; @@ -64,22 +66,26 @@ use statement_table::{ SignedStatement as TableSignedStatement, Summary as TableSummary, }, }; +use thiserror::Error; -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] enum Error { + #[error("Candidate is not found")] CandidateNotFound, + #[error("Signature is invalid")] InvalidSignature, - StoreFailed, - #[from] - Erasure(erasure_coding::Error), - #[from] - ValidationFailed(ValidationFailed), - #[from] - Oneshot(oneshot::Canceled), - #[from] - Mpsc(mpsc::SendError), - #[from] - UtilError(util::Error), + #[error("Failed to send candidates {0:?}")] + Send(Vec), + #[error("Oneshot never resolved")] + Oneshot(#[from] #[source] oneshot::Canceled), + #[error("Obtaining erasure chunks failed")] + ObtainErasureChunks(#[from] #[source] erasure_coding::Error), + #[error(transparent)] + ValidationFailed(#[from] ValidationFailed), + #[error(transparent)] + Mpsc(#[from] mpsc::SendError), + #[error(transparent)] + UtilError(#[from] util::Error), } /// Holds all data needed for candidate backing job operation. @@ -468,7 +474,7 @@ impl CandidateBackingJob { CandidateBackingMessage::GetBackedCandidates(_, tx) => { let backed = self.get_backed(); - tx.send(backed).map_err(|_| oneshot::Canceled)?; + tx.send(backed).map_err(|data| Error::Send(data))?; } } @@ -640,7 +646,7 @@ impl CandidateBackingJob { ) ).await?; - rx.await?.map_err(|_| Error::StoreFailed)?; + let _ = rx.await?; Ok(()) } diff --git a/node/core/bitfield-signing/Cargo.toml b/node/core/bitfield-signing/Cargo.toml index 634f8fd8f37e..1ad319617ad7 100644 --- a/node/core/bitfield-signing/Cargo.toml +++ b/node/core/bitfield-signing/Cargo.toml @@ -6,11 +6,11 @@ edition = "2018" [dependencies] bitvec = "0.17.4" -derive_more = "0.99.9" futures = "0.3.5" -log = "0.4.8" +log = "0.4.11" polkadot-primitives = { path = "../../../primitives" } polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } wasm-timer = "0.2.4" +thiserror = "1.0.21" diff --git a/node/core/bitfield-signing/src/lib.rs b/node/core/bitfield-signing/src/lib.rs index a518469c9dfc..48bc8df70a5a 100644 --- a/node/core/bitfield-signing/src/lib.rs +++ b/node/core/bitfield-signing/src/lib.rs @@ -16,6 +16,9 @@ //! The bitfield signing subsystem produces `SignedAvailabilityBitfield`s once per block. +#![deny(unused_crate_dependencies, unused_results)] +#![warn(missing_docs)] + use bitvec::bitvec; use futures::{ channel::{mpsc, oneshot}, @@ -37,6 +40,7 @@ use polkadot_node_subsystem_util::{ use polkadot_primitives::v1::{AvailabilityBitfield, CoreState, Hash, ValidatorIndex}; use std::{convert::TryFrom, pin::Pin, time::Duration}; use wasm_timer::{Delay, Instant}; +use thiserror::Error; /// Delay between starting a bitfield signing job and its attempting to create a bitfield. const JOB_DELAY: Duration = Duration::from_millis(1500); @@ -45,6 +49,7 @@ const JOB_DELAY: Duration = Duration::from_millis(1500); pub struct BitfieldSigningJob; /// Messages which a `BitfieldSigningJob` is prepared to receive. +#[allow(missing_docs)] pub enum ToJob { BitfieldSigning(BitfieldSigningMessage), Stop, @@ -79,6 +84,7 @@ impl From for ToJob { } /// Messages which may be sent from a `BitfieldSigningJob`. +#[allow(missing_docs)] pub enum FromJob { AvailabilityStore(AvailabilityStoreMessage), BitfieldDistribution(BitfieldDistributionMessage), @@ -112,28 +118,28 @@ impl TryFrom for FromJob { } /// Errors we may encounter in the course of executing the `BitfieldSigningSubsystem`. -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] pub enum Error { /// error propagated from the utility subsystem - #[from] - Util(util::Error), + #[error(transparent)] + Util(#[from] util::Error), /// io error - #[from] - Io(std::io::Error), + #[error(transparent)] + Io(#[from] std::io::Error), /// a one shot channel was canceled - #[from] - Oneshot(oneshot::Canceled), + #[error(transparent)] + Oneshot(#[from] oneshot::Canceled), /// a mspc channel failed to send - #[from] - MpscSend(mpsc::SendError), + #[error(transparent)] + MpscSend(#[from] mpsc::SendError), /// several errors collected into one - #[from] + #[error("Multiple errours occured: {0:?}")] Multiple(Vec), /// the runtime API failed to return what we wanted - #[from] - Runtime(RuntimeApiError), + #[error(transparent)] + Runtime(#[from] RuntimeApiError), /// the keystore failed to process signing request - #[from] + #[error("Keystore failed: {0:?}")] Keystore(KeystoreError), } @@ -252,7 +258,7 @@ async fn construct_availability_bitfield( if errs.is_empty() { Ok(out.into_inner().into()) } else { - Err(errs.into()) + Err(Error::Multiple(errs.into())) } } diff --git a/node/core/candidate-selection/Cargo.toml b/node/core/candidate-selection/Cargo.toml index 171e84723eb1..071f83d53c4c 100644 --- a/node/core/candidate-selection/Cargo.toml +++ b/node/core/candidate-selection/Cargo.toml @@ -5,9 +5,9 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -derive_more = "0.99.9" futures = "0.3.5" -log = "0.4.8" +log = "0.4.11" +thiserror = "1.0.21" polkadot-primitives = { path = "../../../primitives" } polkadot-node-primitives = { path = "../../primitives" } polkadot-node-subsystem = { path = "../../subsystem" } diff --git a/node/core/candidate-selection/src/lib.rs b/node/core/candidate-selection/src/lib.rs index f4ed605ef3bf..1e8cf4900221 100644 --- a/node/core/candidate-selection/src/lib.rs +++ b/node/core/candidate-selection/src/lib.rs @@ -17,7 +17,7 @@ //! The provisioner is responsible for assembling a relay chain block //! from a set of available parachain candidates of its choice. -#![deny(missing_docs)] +#![deny(missing_docs, unused_crate_dependencies, unused_results)] use futures::{ channel::{mpsc, oneshot}, @@ -39,6 +39,7 @@ use polkadot_primitives::v1::{ CandidateDescriptor, CandidateReceipt, CollatorId, Hash, Id as ParaId, PoV, }; use std::{convert::TryFrom, pin::Pin, sync::Arc}; +use thiserror::Error; const TARGET: &'static str = "candidate_selection"; @@ -116,18 +117,18 @@ impl TryFrom for FromJob { } } -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] enum Error { - #[from] - Sending(mpsc::SendError), - #[from] - Util(util::Error), - #[from] - OneshotRecv(oneshot::Canceled), - #[from] - ChainApi(ChainApiError), - #[from] - Runtime(RuntimeApiError), + #[error(transparent)] + Sending(#[from] mpsc::SendError), + #[error(transparent)] + Util(#[from] util::Error), + #[error(transparent)] + OneshotRecv(#[from] oneshot::Canceled), + #[error(transparent)] + ChainApi(#[from] ChainApiError), + #[error(transparent)] + Runtime(#[from] RuntimeApiError), } impl JobTrait for CandidateSelectionJob { @@ -149,14 +150,13 @@ impl JobTrait for CandidateSelectionJob { receiver: mpsc::Receiver, sender: mpsc::Sender, ) -> Pin> + Send>> { - async move { + Box::pin(async move { let job = CandidateSelectionJob::new(metrics, sender, receiver); // it isn't necessary to break run_loop into its own function, // but it's convenient to separate the concerns in this way job.run_loop().await - } - .boxed() + }) } } diff --git a/node/core/candidate-validation/Cargo.toml b/node/core/candidate-validation/Cargo.toml index 77f9712cb8c9..87cb639a3601 100644 --- a/node/core/candidate-validation/Cargo.toml +++ b/node/core/candidate-validation/Cargo.toml @@ -5,11 +5,9 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -derive_more = "0.99.9" futures = "0.3.5" -log = "0.4.8" +log = "0.4.11" -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "master" } parity-scale-codec = { version = "1.3.0", default-features = false, features = ["bit-vec", "derive"] } diff --git a/node/core/candidate-validation/src/lib.rs b/node/core/candidate-validation/src/lib.rs index 33c696d2db6d..632e9b55774d 100644 --- a/node/core/candidate-validation/src/lib.rs +++ b/node/core/candidate-validation/src/lib.rs @@ -20,8 +20,11 @@ //! according to a validation function. This delegates validation to an underlying //! pool of processes used for execution of the Wasm. +#![deny(unused_crate_dependencies, unused_results)] +#![warn(missing_docs)] + use polkadot_subsystem::{ - Subsystem, SubsystemContext, SpawnedSubsystem, SubsystemResult, + Subsystem, SubsystemContext, SpawnedSubsystem, SubsystemResult, SubsystemError, FromOverseer, OverseerSignal, messages::{ AllMessages, CandidateValidationMessage, RuntimeApiMessage, @@ -116,9 +119,13 @@ impl Subsystem for CandidateValidationSubsystem where S: SpawnNamed + Clone + 'static, { fn start(self, ctx: C) -> SpawnedSubsystem { + let future = run(ctx, self.spawn, self.metrics) + .map_err(|e| SubsystemError::with_origin("candidate-validation", e)) + .map(|_| ()) + .boxed(); SpawnedSubsystem { name: "candidate-validation-subsystem", - future: run(ctx, self.spawn, self.metrics).map(|_| ()).boxed(), + future, } } } diff --git a/node/core/chain-api/src/lib.rs b/node/core/chain-api/src/lib.rs index b41c017cda10..7c7f2d98b2d2 100644 --- a/node/core/chain-api/src/lib.rs +++ b/node/core/chain-api/src/lib.rs @@ -27,9 +27,12 @@ //! * Last finalized block number //! * Ancestors +#![deny(unused_crate_dependencies, unused_results)] +#![warn(missing_docs)] + use polkadot_subsystem::{ FromOverseer, OverseerSignal, - SpawnedSubsystem, Subsystem, SubsystemResult, SubsystemContext, + SpawnedSubsystem, Subsystem, SubsystemResult, SubsystemError, SubsystemContext, messages::ChainApiMessage, }; use polkadot_node_subsystem_util::{ @@ -61,8 +64,12 @@ impl Subsystem for ChainApiSubsystem where Context: SubsystemContext { fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = run(ctx, self) + .map_err(|e| SubsystemError::with_origin("chain-api", e)) + .map(|_| ()) + .boxed(); SpawnedSubsystem { - future: run(ctx, self).map(|_| ()).boxed(), + future, name: "chain-api-subsystem", } } @@ -112,7 +119,10 @@ where let maybe_header = subsystem.client.header(BlockId::Hash(hash)); match maybe_header { // propagate the error - Err(e) => Some(Err(e.to_string().into())), + Err(e) => { + let e = e.to_string().into(); + Some(Err(e)) + }, // fewer than `k` ancestors are available Ok(None) => None, Ok(Some(header)) => { diff --git a/node/core/proposer/Cargo.toml b/node/core/proposer/Cargo.toml index 7cf7fc28af22..3205b50b5bb0 100644 --- a/node/core/proposer/Cargo.toml +++ b/node/core/proposer/Cargo.toml @@ -6,16 +6,13 @@ edition = "2018" [dependencies] futures = "0.3.4" -futures-timer = "3.0.1" log = "0.4.8" -parity-scale-codec = "1.3.4" polkadot-node-subsystem = { path = "../../subsystem" } polkadot-overseer = { path = "../../overseer" } polkadot-primitives = { path = "../../../primitives" } sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -23,5 +20,4 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } -tokio-executor = { version = "0.2.0-alpha.6", features = ["blocking"] } wasm-timer = "0.2.4" diff --git a/node/core/proposer/src/lib.rs b/node/core/proposer/src/lib.rs index ed1a52230989..89a4060621f5 100644 --- a/node/core/proposer/src/lib.rs +++ b/node/core/proposer/src/lib.rs @@ -1,3 +1,23 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! The proposer proposes new blocks to include + +#![deny(unused_crate_dependencies, unused_results)] + use futures::prelude::*; use futures::select; use polkadot_node_subsystem::{messages::{AllMessages, ProvisionerInherentData, ProvisionerMessage}, SubsystemError}; @@ -123,7 +143,7 @@ where let (sender, receiver) = futures::channel::oneshot::channel(); overseer.wait_for_activation(parent_header_hash, sender).await?; - receiver.await.map_err(Error::ClosedChannelFromProvisioner)?; + receiver.await.map_err(Error::ClosedChannelFromProvisioner)??; let (sender, receiver) = futures::channel::oneshot::channel(); // strictly speaking, we don't _have_ to .await this send_msg before opening the @@ -206,7 +226,7 @@ where // It would have been more ergonomic to use thiserror to derive the // From implementations, Display, and std::error::Error, but unfortunately -// two of the wrapped errors (sp_inherents::Error, SubsystemError) also +// one of the wrapped errors (sp_inherents::Error) also // don't impl std::error::Error, which breaks the thiserror derive. #[derive(Debug)] pub enum Error { @@ -261,6 +281,7 @@ impl std::error::Error for Error { Self::Consensus(err) => Some(err), Self::Blockchain(err) => Some(err), Self::ClosedChannelFromProvisioner(err) => Some(err), + Self::Subsystem(err) => Some(err), _ => None } } diff --git a/node/core/provisioner/Cargo.toml b/node/core/provisioner/Cargo.toml index 3b7e1f5add4c..a44fae43f52a 100644 --- a/node/core/provisioner/Cargo.toml +++ b/node/core/provisioner/Cargo.toml @@ -6,16 +6,14 @@ edition = "2018" [dependencies] bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] } -derive_more = "0.99.9" futures = "0.3.5" -log = "0.4.8" +log = "0.4.11" +thiserror = "1.0.21" polkadot-primitives = { path = "../../../primitives" } polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } [dev-dependencies] -lazy_static = "1.4" -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/core/provisioner/src/lib.rs b/node/core/provisioner/src/lib.rs index b8af4852a0dc..37c082bc47d5 100644 --- a/node/core/provisioner/src/lib.rs +++ b/node/core/provisioner/src/lib.rs @@ -17,7 +17,7 @@ //! The provisioner is responsible for assembling a relay chain block //! from a set of available parachain candidates of its choice. -#![deny(missing_docs)] +#![deny(missing_docs, unused_crate_dependencies, unused_results)] use bitvec::vec::BitVec; use futures::{ @@ -42,6 +42,7 @@ use polkadot_primitives::v1::{ SignedAvailabilityBitfield, }; use std::{collections::HashMap, convert::TryFrom, pin::Pin}; +use thiserror::Error; struct ProvisioningJob { relay_parent: Hash, @@ -115,19 +116,25 @@ impl TryFrom for FromJob { } } -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] enum Error { - #[from] - Sending(mpsc::SendError), - #[from] - Util(util::Error), - #[from] - OneshotRecv(oneshot::Canceled), - #[from] - ChainApi(ChainApiError), - #[from] - Runtime(RuntimeApiError), - OneshotSend, + #[error(transparent)] + Util(#[from] util::Error), + + #[error(transparent)] + OneshotRecv(#[from] oneshot::Canceled), + + #[error(transparent)] + ChainApi(#[from] ChainApiError), + + #[error(transparent)] + Runtime(#[from] RuntimeApiError), + + #[error("Failed to send message to ChainAPI")] + ChainApiMessageSend(#[source] mpsc::SendError), + + #[error("Failed to send return message with Inherents")] + InherentDataReturnChannel, } impl JobTrait for ProvisioningJob { @@ -230,7 +237,7 @@ impl ProvisioningJob { let tail = bad_indices[bad_indices.len() - 1]; let retain = *idx != tail; if *idx >= tail { - bad_indices.pop(); + let _ = bad_indices.pop(); } retain }) @@ -299,7 +306,7 @@ async fn send_inherent_data( return_sender .send((bitfields, candidates)) - .map_err(|_| Error::OneshotSend)?; + .map_err(|_data| Error::InherentDataReturnChannel)?; Ok(()) } @@ -423,7 +430,7 @@ async fn get_block_number_under_construction( tx, ))) .await - .map_err(|_| Error::OneshotSend)?; + .map_err(|e| Error::ChainApiMessageSend(e))?; match rx.await? { Ok(Some(n)) => Ok(n + 1), Ok(None) => Ok(0), @@ -504,382 +511,4 @@ impl metrics::Metrics for Metrics { delegated_subsystem!(ProvisioningJob((), Metrics) <- ToJob as ProvisioningSubsystem); #[cfg(test)] -mod tests { - use super::*; - use bitvec::bitvec; - use polkadot_primitives::v1::{OccupiedCore, ScheduledCore}; - - pub fn occupied_core(para_id: u32) -> CoreState { - CoreState::Occupied(OccupiedCore { - para_id: para_id.into(), - group_responsible: para_id.into(), - next_up_on_available: None, - occupied_since: 100_u32, - time_out_at: 200_u32, - next_up_on_time_out: None, - availability: default_bitvec(), - }) - } - - pub fn build_occupied_core(para_id: u32, builder: Builder) -> CoreState - where - Builder: FnOnce(&mut OccupiedCore), - { - let mut core = match occupied_core(para_id) { - CoreState::Occupied(core) => core, - _ => unreachable!(), - }; - - builder(&mut core); - - CoreState::Occupied(core) - } - - pub fn default_bitvec() -> CoreAvailability { - bitvec![bitvec::order::Lsb0, u8; 0; 32] - } - - pub fn scheduled_core(id: u32) -> ScheduledCore { - ScheduledCore { - para_id: id.into(), - ..Default::default() - } - } - - mod select_availability_bitfields { - use super::super::*; - use super::{default_bitvec, occupied_core}; - use futures::executor::block_on; - use std::sync::Arc; - use polkadot_primitives::v1::{SigningContext, ValidatorIndex, ValidatorId}; - use sp_application_crypto::AppKey; - use sp_keystore::{CryptoStore, SyncCryptoStorePtr}; - use sc_keystore::LocalKeystore; - - async fn signed_bitfield( - keystore: &SyncCryptoStorePtr, - field: CoreAvailability, - validator_idx: ValidatorIndex, - ) -> SignedAvailabilityBitfield { - let public = CryptoStore::sr25519_generate_new(&**keystore, ValidatorId::ID, None) - .await - .expect("generated sr25519 key"); - SignedAvailabilityBitfield::sign( - &keystore, - field.into(), - &>::default(), - validator_idx, - &public.into(), - ).await.expect("Should be signed") - } - - #[test] - fn not_more_than_one_per_validator() { - // Configure filesystem-based keystore as generating keys without seed - // would trigger the key to be generated on the filesystem. - let keystore_path = tempfile::tempdir().expect("Creates keystore path"); - let keystore : SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None) - .expect("Creates keystore")); - let bitvec = default_bitvec(); - - let cores = vec![occupied_core(0), occupied_core(1)]; - - // we pass in three bitfields with two validators - // this helps us check the postcondition that we get two bitfields back, for which the validators differ - let bitfields = vec![ - block_on(signed_bitfield(&keystore, bitvec.clone(), 0)), - block_on(signed_bitfield(&keystore, bitvec.clone(), 1)), - block_on(signed_bitfield(&keystore, bitvec, 1)), - ]; - - let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields); - selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index()); - - assert_eq!(selected_bitfields.len(), 2); - assert_eq!(selected_bitfields[0], bitfields[0]); - // we don't know which of the (otherwise equal) bitfields will be selected - assert!(selected_bitfields[1] == bitfields[1] || selected_bitfields[1] == bitfields[2]); - } - - #[test] - fn each_corresponds_to_an_occupied_core() { - // Configure filesystem-based keystore as generating keys without seed - // would trigger the key to be generated on the filesystem. - let keystore_path = tempfile::tempdir().expect("Creates keystore path"); - let keystore : SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None) - .expect("Creates keystore")); - let bitvec = default_bitvec(); - - let cores = vec![CoreState::Free, CoreState::Scheduled(Default::default())]; - - let bitfields = vec![ - block_on(signed_bitfield(&keystore, bitvec.clone(), 0)), - block_on(signed_bitfield(&keystore, bitvec.clone(), 1)), - block_on(signed_bitfield(&keystore, bitvec, 1)), - ]; - - let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields); - selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index()); - - // bitfields not corresponding to occupied cores are not selected - assert!(selected_bitfields.is_empty()); - } - - #[test] - fn more_set_bits_win_conflicts() { - // Configure filesystem-based keystore as generating keys without seed - // would trigger the key to be generated on the filesystem. - let keystore_path = tempfile::tempdir().expect("Creates keystore path"); - let keystore : SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None) - .expect("Creates keystore")); - let bitvec_zero = default_bitvec(); - let bitvec_one = { - let mut bitvec = bitvec_zero.clone(); - bitvec.set(0, true); - bitvec - }; - - let cores = vec![occupied_core(0)]; - - let bitfields = vec![ - block_on(signed_bitfield(&keystore, bitvec_zero, 0)), - block_on(signed_bitfield(&keystore, bitvec_one.clone(), 0)), - ]; - - // this test is probablistic: chances are excellent that it does what it claims to. - // it cannot fail unless things are broken. - // however, there is a (very small) chance that it passes when things are broken. - for _ in 0..64 { - let selected_bitfields = select_availability_bitfields(&cores, &bitfields); - assert_eq!(selected_bitfields.len(), 1); - assert_eq!(selected_bitfields[0].payload().0, bitvec_one); - } - } - } - - mod select_candidates { - use futures_timer::Delay; - use super::super::*; - use super::{build_occupied_core, default_bitvec, occupied_core, scheduled_core}; - use polkadot_node_subsystem::messages::RuntimeApiRequest::{ - AvailabilityCores, PersistedValidationData as PersistedValidationDataReq, - }; - use polkadot_primitives::v1::{ - BlockNumber, CandidateDescriptor, CommittedCandidateReceipt, PersistedValidationData, - }; - use FromJob::{ChainApi, Runtime}; - - const BLOCK_UNDER_PRODUCTION: BlockNumber = 128; - - fn test_harness( - overseer_factory: OverseerFactory, - test_factory: TestFactory, - ) where - OverseerFactory: FnOnce(mpsc::Receiver) -> Overseer, - Overseer: Future, - TestFactory: FnOnce(mpsc::Sender) -> Test, - Test: Future, - { - let (tx, rx) = mpsc::channel(64); - let overseer = overseer_factory(rx); - let test = test_factory(tx); - - futures::pin_mut!(overseer, test); - - futures::executor::block_on(future::select(overseer, test)); - } - - // For test purposes, we always return this set of availability cores: - // - // [ - // 0: Free, - // 1: Scheduled(default), - // 2: Occupied(no next_up set), - // 3: Occupied(next_up_on_available set but not available), - // 4: Occupied(next_up_on_available set and available), - // 5: Occupied(next_up_on_time_out set but not timeout), - // 6: Occupied(next_up_on_time_out set and timeout but available), - // 7: Occupied(next_up_on_time_out set and timeout and not available), - // 8: Occupied(both next_up set, available), - // 9: Occupied(both next_up set, not available, no timeout), - // 10: Occupied(both next_up set, not available, timeout), - // 11: Occupied(next_up_on_available and available, but different successor para_id) - // ] - fn mock_availability_cores() -> Vec { - use std::ops::Not; - use CoreState::{Free, Scheduled}; - - vec![ - // 0: Free, - Free, - // 1: Scheduled(default), - Scheduled(scheduled_core(1)), - // 2: Occupied(no next_up set), - occupied_core(2), - // 3: Occupied(next_up_on_available set but not available), - build_occupied_core(3, |core| { - core.next_up_on_available = Some(scheduled_core(3)); - }), - // 4: Occupied(next_up_on_available set and available), - build_occupied_core(4, |core| { - core.next_up_on_available = Some(scheduled_core(4)); - core.availability = core.availability.clone().not(); - }), - // 5: Occupied(next_up_on_time_out set but not timeout), - build_occupied_core(5, |core| { - core.next_up_on_time_out = Some(scheduled_core(5)); - }), - // 6: Occupied(next_up_on_time_out set and timeout but available), - build_occupied_core(6, |core| { - core.next_up_on_time_out = Some(scheduled_core(6)); - core.time_out_at = BLOCK_UNDER_PRODUCTION; - core.availability = core.availability.clone().not(); - }), - // 7: Occupied(next_up_on_time_out set and timeout and not available), - build_occupied_core(7, |core| { - core.next_up_on_time_out = Some(scheduled_core(7)); - core.time_out_at = BLOCK_UNDER_PRODUCTION; - }), - // 8: Occupied(both next_up set, available), - build_occupied_core(8, |core| { - core.next_up_on_available = Some(scheduled_core(8)); - core.next_up_on_time_out = Some(scheduled_core(8)); - core.availability = core.availability.clone().not(); - }), - // 9: Occupied(both next_up set, not available, no timeout), - build_occupied_core(9, |core| { - core.next_up_on_available = Some(scheduled_core(9)); - core.next_up_on_time_out = Some(scheduled_core(9)); - }), - // 10: Occupied(both next_up set, not available, timeout), - build_occupied_core(10, |core| { - core.next_up_on_available = Some(scheduled_core(10)); - core.next_up_on_time_out = Some(scheduled_core(10)); - core.time_out_at = BLOCK_UNDER_PRODUCTION; - }), - // 11: Occupied(next_up_on_available and available, but different successor para_id) - build_occupied_core(11, |core| { - core.next_up_on_available = Some(scheduled_core(12)); - core.availability = core.availability.clone().not(); - }), - ] - } - - async fn mock_overseer(mut receiver: mpsc::Receiver) { - use ChainApiMessage::BlockNumber; - use RuntimeApiMessage::Request; - - while let Some(from_job) = receiver.next().await { - match from_job { - ChainApi(BlockNumber(_relay_parent, tx)) => { - tx.send(Ok(Some(BLOCK_UNDER_PRODUCTION - 1))).unwrap() - } - Runtime(Request( - _parent_hash, - PersistedValidationDataReq(_para_id, _assumption, tx), - )) => tx.send(Ok(Some(Default::default()))).unwrap(), - Runtime(Request(_parent_hash, AvailabilityCores(tx))) => { - tx.send(Ok(mock_availability_cores())).unwrap() - } - // non-exhaustive matches are fine for testing - _ => unimplemented!(), - } - } - } - - #[test] - fn handles_overseer_failure() { - let overseer = |rx: mpsc::Receiver| async move { - // drop the receiver so it closes and the sender can't send, then just sleep long enough that - // this is almost certainly not the first of the two futures to complete - std::mem::drop(rx); - Delay::new(std::time::Duration::from_secs(1)).await; - }; - - let test = |mut tx: mpsc::Sender| async move { - // wait so that the overseer can drop the rx before we attempt to send - Delay::new(std::time::Duration::from_millis(50)).await; - let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await; - println!("{:?}", result); - assert!(std::matches!(result, Err(Error::OneshotSend))); - }; - - test_harness(overseer, test); - } - - #[test] - fn can_succeed() { - test_harness(mock_overseer, |mut tx: mpsc::Sender| async move { - let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await; - println!("{:?}", result); - assert!(result.is_ok()); - }) - } - - // this tests that only the appropriate candidates get selected. - // To accomplish this, we supply a candidate list containing one candidate per possible core; - // the candidate selection algorithm must filter them to the appropriate set - #[test] - fn selects_correct_candidates() { - let mock_cores = mock_availability_cores(); - - let empty_hash = PersistedValidationData::::default().hash(); - - let candidate_template = BackedCandidate { - candidate: CommittedCandidateReceipt { - descriptor: CandidateDescriptor { - persisted_validation_data_hash: empty_hash, - ..Default::default() - }, - ..Default::default() - }, - validity_votes: Vec::new(), - validator_indices: default_bitvec(), - }; - - let candidates: Vec<_> = std::iter::repeat(candidate_template) - .take(mock_cores.len()) - .enumerate() - .map(|(idx, mut candidate)| { - candidate.candidate.descriptor.para_id = idx.into(); - candidate - }) - .cycle() - .take(mock_cores.len() * 3) - .enumerate() - .map(|(idx, mut candidate)| { - if idx < mock_cores.len() { - // first go-around: use candidates which should work - candidate - } else if idx < mock_cores.len() * 2 { - // for the second repetition of the candidates, give them the wrong hash - candidate.candidate.descriptor.persisted_validation_data_hash - = Default::default(); - candidate - } else { - // third go-around: right hash, wrong para_id - candidate.candidate.descriptor.para_id = idx.into(); - candidate - } - }) - .collect(); - - // why those particular indices? see the comments on mock_availability_cores() - let expected_candidates: Vec<_> = [1, 4, 7, 8, 10] - .iter() - .map(|&idx| candidates[idx].clone()) - .collect(); - - test_harness(mock_overseer, |mut tx: mpsc::Sender| async move { - let result = - select_candidates(&mock_cores, &[], &candidates, Default::default(), &mut tx) - .await; - - if result.is_err() { - println!("{:?}", result); - } - assert_eq!(result.unwrap(), expected_candidates); - }) - } - } -} +mod tests; \ No newline at end of file diff --git a/node/core/provisioner/src/tests.rs b/node/core/provisioner/src/tests.rs new file mode 100644 index 000000000000..d0df7f5329f0 --- /dev/null +++ b/node/core/provisioner/src/tests.rs @@ -0,0 +1,377 @@ +use super::*; +use bitvec::bitvec; +use polkadot_primitives::v1::{OccupiedCore, ScheduledCore}; + +pub fn occupied_core(para_id: u32) -> CoreState { + CoreState::Occupied(OccupiedCore { + para_id: para_id.into(), + group_responsible: para_id.into(), + next_up_on_available: None, + occupied_since: 100_u32, + time_out_at: 200_u32, + next_up_on_time_out: None, + availability: default_bitvec(), + }) +} + +pub fn build_occupied_core(para_id: u32, builder: Builder) -> CoreState +where + Builder: FnOnce(&mut OccupiedCore), +{ + let mut core = match occupied_core(para_id) { + CoreState::Occupied(core) => core, + _ => unreachable!(), + }; + + builder(&mut core); + + CoreState::Occupied(core) +} + +pub fn default_bitvec() -> CoreAvailability { + bitvec![bitvec::order::Lsb0, u8; 0; 32] +} + +pub fn scheduled_core(id: u32) -> ScheduledCore { + ScheduledCore { + para_id: id.into(), + ..Default::default() + } +} + +mod select_availability_bitfields { + use super::super::*; + use super::{default_bitvec, occupied_core}; + use futures::executor::block_on; + use std::sync::Arc; + use polkadot_primitives::v1::{SigningContext, ValidatorIndex, ValidatorId}; + use sp_application_crypto::AppKey; + use sp_keystore::{CryptoStore, SyncCryptoStorePtr}; + use sc_keystore::LocalKeystore; + + async fn signed_bitfield( + keystore: &SyncCryptoStorePtr, + field: CoreAvailability, + validator_idx: ValidatorIndex, + ) -> SignedAvailabilityBitfield { + let public = CryptoStore::sr25519_generate_new(&**keystore, ValidatorId::ID, None) + .await + .expect("generated sr25519 key"); + SignedAvailabilityBitfield::sign( + &keystore, + field.into(), + &>::default(), + validator_idx, + &public.into(), + ).await.expect("Should be signed") + } + + #[test] + fn not_more_than_one_per_validator() { + // Configure filesystem-based keystore as generating keys without seed + // would trigger the key to be generated on the filesystem. + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore : SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None) + .expect("Creates keystore")); + let bitvec = default_bitvec(); + + let cores = vec![occupied_core(0), occupied_core(1)]; + + // we pass in three bitfields with two validators + // this helps us check the postcondition that we get two bitfields back, for which the validators differ + let bitfields = vec![ + block_on(signed_bitfield(&keystore, bitvec.clone(), 0)), + block_on(signed_bitfield(&keystore, bitvec.clone(), 1)), + block_on(signed_bitfield(&keystore, bitvec, 1)), + ]; + + let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields); + selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index()); + + assert_eq!(selected_bitfields.len(), 2); + assert_eq!(selected_bitfields[0], bitfields[0]); + // we don't know which of the (otherwise equal) bitfields will be selected + assert!(selected_bitfields[1] == bitfields[1] || selected_bitfields[1] == bitfields[2]); + } + + #[test] + fn each_corresponds_to_an_occupied_core() { + // Configure filesystem-based keystore as generating keys without seed + // would trigger the key to be generated on the filesystem. + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore : SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None) + .expect("Creates keystore")); + let bitvec = default_bitvec(); + + let cores = vec![CoreState::Free, CoreState::Scheduled(Default::default())]; + + let bitfields = vec![ + block_on(signed_bitfield(&keystore, bitvec.clone(), 0)), + block_on(signed_bitfield(&keystore, bitvec.clone(), 1)), + block_on(signed_bitfield(&keystore, bitvec, 1)), + ]; + + let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields); + selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index()); + + // bitfields not corresponding to occupied cores are not selected + assert!(selected_bitfields.is_empty()); + } + + #[test] + fn more_set_bits_win_conflicts() { + // Configure filesystem-based keystore as generating keys without seed + // would trigger the key to be generated on the filesystem. + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore : SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None) + .expect("Creates keystore")); + let bitvec_zero = default_bitvec(); + let bitvec_one = { + let mut bitvec = bitvec_zero.clone(); + bitvec.set(0, true); + bitvec + }; + + let cores = vec![occupied_core(0)]; + + let bitfields = vec![ + block_on(signed_bitfield(&keystore, bitvec_zero, 0)), + block_on(signed_bitfield(&keystore, bitvec_one.clone(), 0)), + ]; + + // this test is probablistic: chances are excellent that it does what it claims to. + // it cannot fail unless things are broken. + // however, there is a (very small) chance that it passes when things are broken. + for _ in 0..64 { + let selected_bitfields = select_availability_bitfields(&cores, &bitfields); + assert_eq!(selected_bitfields.len(), 1); + assert_eq!(selected_bitfields[0].payload().0, bitvec_one); + } + } +} + +mod select_candidates { + use futures_timer::Delay; + use super::super::*; + use super::{build_occupied_core, default_bitvec, occupied_core, scheduled_core}; + use polkadot_node_subsystem::messages::RuntimeApiRequest::{ + AvailabilityCores, PersistedValidationData as PersistedValidationDataReq, + }; + use polkadot_primitives::v1::{ + BlockNumber, CandidateDescriptor, CommittedCandidateReceipt, PersistedValidationData, + }; + use FromJob::{ChainApi, Runtime}; + + const BLOCK_UNDER_PRODUCTION: BlockNumber = 128; + + fn test_harness( + overseer_factory: OverseerFactory, + test_factory: TestFactory, + ) where + OverseerFactory: FnOnce(mpsc::Receiver) -> Overseer, + Overseer: Future, + TestFactory: FnOnce(mpsc::Sender) -> Test, + Test: Future, + { + let (tx, rx) = mpsc::channel(64); + let overseer = overseer_factory(rx); + let test = test_factory(tx); + + futures::pin_mut!(overseer, test); + + let _ = futures::executor::block_on(future::select(overseer, test)); + } + + // For test purposes, we always return this set of availability cores: + // + // [ + // 0: Free, + // 1: Scheduled(default), + // 2: Occupied(no next_up set), + // 3: Occupied(next_up_on_available set but not available), + // 4: Occupied(next_up_on_available set and available), + // 5: Occupied(next_up_on_time_out set but not timeout), + // 6: Occupied(next_up_on_time_out set and timeout but available), + // 7: Occupied(next_up_on_time_out set and timeout and not available), + // 8: Occupied(both next_up set, available), + // 9: Occupied(both next_up set, not available, no timeout), + // 10: Occupied(both next_up set, not available, timeout), + // 11: Occupied(next_up_on_available and available, but different successor para_id) + // ] + fn mock_availability_cores() -> Vec { + use std::ops::Not; + use CoreState::{Free, Scheduled}; + + vec![ + // 0: Free, + Free, + // 1: Scheduled(default), + Scheduled(scheduled_core(1)), + // 2: Occupied(no next_up set), + occupied_core(2), + // 3: Occupied(next_up_on_available set but not available), + build_occupied_core(3, |core| { + core.next_up_on_available = Some(scheduled_core(3)); + }), + // 4: Occupied(next_up_on_available set and available), + build_occupied_core(4, |core| { + core.next_up_on_available = Some(scheduled_core(4)); + core.availability = core.availability.clone().not(); + }), + // 5: Occupied(next_up_on_time_out set but not timeout), + build_occupied_core(5, |core| { + core.next_up_on_time_out = Some(scheduled_core(5)); + }), + // 6: Occupied(next_up_on_time_out set and timeout but available), + build_occupied_core(6, |core| { + core.next_up_on_time_out = Some(scheduled_core(6)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + core.availability = core.availability.clone().not(); + }), + // 7: Occupied(next_up_on_time_out set and timeout and not available), + build_occupied_core(7, |core| { + core.next_up_on_time_out = Some(scheduled_core(7)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + }), + // 8: Occupied(both next_up set, available), + build_occupied_core(8, |core| { + core.next_up_on_available = Some(scheduled_core(8)); + core.next_up_on_time_out = Some(scheduled_core(8)); + core.availability = core.availability.clone().not(); + }), + // 9: Occupied(both next_up set, not available, no timeout), + build_occupied_core(9, |core| { + core.next_up_on_available = Some(scheduled_core(9)); + core.next_up_on_time_out = Some(scheduled_core(9)); + }), + // 10: Occupied(both next_up set, not available, timeout), + build_occupied_core(10, |core| { + core.next_up_on_available = Some(scheduled_core(10)); + core.next_up_on_time_out = Some(scheduled_core(10)); + core.time_out_at = BLOCK_UNDER_PRODUCTION; + }), + // 11: Occupied(next_up_on_available and available, but different successor para_id) + build_occupied_core(11, |core| { + core.next_up_on_available = Some(scheduled_core(12)); + core.availability = core.availability.clone().not(); + }), + ] + } + + async fn mock_overseer(mut receiver: mpsc::Receiver) { + use ChainApiMessage::BlockNumber; + use RuntimeApiMessage::Request; + + while let Some(from_job) = receiver.next().await { + match from_job { + ChainApi(BlockNumber(_relay_parent, tx)) => { + tx.send(Ok(Some(BLOCK_UNDER_PRODUCTION - 1))).unwrap() + } + Runtime(Request( + _parent_hash, + PersistedValidationDataReq(_para_id, _assumption, tx), + )) => tx.send(Ok(Some(Default::default()))).unwrap(), + Runtime(Request(_parent_hash, AvailabilityCores(tx))) => { + tx.send(Ok(mock_availability_cores())).unwrap() + } + // non-exhaustive matches are fine for testing + _ => unimplemented!(), + } + } + } + + #[test] + fn handles_overseer_failure() { + let overseer = |rx: mpsc::Receiver| async move { + // drop the receiver so it closes and the sender can't send, then just sleep long enough that + // this is almost certainly not the first of the two futures to complete + std::mem::drop(rx); + Delay::new(std::time::Duration::from_secs(1)).await; + }; + + let test = |mut tx: mpsc::Sender| async move { + // wait so that the overseer can drop the rx before we attempt to send + Delay::new(std::time::Duration::from_millis(50)).await; + let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await; + println!("{:?}", result); + assert!(std::matches!(result, Err(Error::ChainApiMessageSend(_)))); + }; + + test_harness(overseer, test); + } + + #[test] + fn can_succeed() { + test_harness(mock_overseer, |mut tx: mpsc::Sender| async move { + let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await; + println!("{:?}", result); + assert!(result.is_ok()); + }) + } + + // this tests that only the appropriate candidates get selected. + // To accomplish this, we supply a candidate list containing one candidate per possible core; + // the candidate selection algorithm must filter them to the appropriate set + #[test] + fn selects_correct_candidates() { + let mock_cores = mock_availability_cores(); + + let empty_hash = PersistedValidationData::::default().hash(); + + let candidate_template = BackedCandidate { + candidate: CommittedCandidateReceipt { + descriptor: CandidateDescriptor { + persisted_validation_data_hash: empty_hash, + ..Default::default() + }, + ..Default::default() + }, + validity_votes: Vec::new(), + validator_indices: default_bitvec(), + }; + + let candidates: Vec<_> = std::iter::repeat(candidate_template) + .take(mock_cores.len()) + .enumerate() + .map(|(idx, mut candidate)| { + candidate.candidate.descriptor.para_id = idx.into(); + candidate + }) + .cycle() + .take(mock_cores.len() * 3) + .enumerate() + .map(|(idx, mut candidate)| { + if idx < mock_cores.len() { + // first go-around: use candidates which should work + candidate + } else if idx < mock_cores.len() * 2 { + // for the second repetition of the candidates, give them the wrong hash + candidate.candidate.descriptor.persisted_validation_data_hash + = Default::default(); + candidate + } else { + // third go-around: right hash, wrong para_id + candidate.candidate.descriptor.para_id = idx.into(); + candidate + } + }) + .collect(); + + // why those particular indices? see the comments on mock_availability_cores() + let expected_candidates: Vec<_> = [1, 4, 7, 8, 10] + .iter() + .map(|&idx| candidates[idx].clone()) + .collect(); + + test_harness(mock_overseer, |mut tx: mpsc::Sender| async move { + let result = + select_candidates(&mock_cores, &[], &candidates, Default::default(), &mut tx) + .await; + + if result.is_err() { + println!("{:?}", result); + } + assert_eq!(result.unwrap(), expected_candidates); + }) + } +} diff --git a/node/core/runtime-api/Cargo.toml b/node/core/runtime-api/Cargo.toml index 0a3a5015a4be..81cf95410976 100644 --- a/node/core/runtime-api/Cargo.toml +++ b/node/core/runtime-api/Cargo.toml @@ -7,10 +7,8 @@ edition = "2018" [dependencies] futures = "0.3.5" sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-primitives = { path = "../../../primitives" } -polkadot-node-primitives = { path = "../../primitives" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } diff --git a/node/core/runtime-api/src/lib.rs b/node/core/runtime-api/src/lib.rs index 4378903b3bff..2f568673cd15 100644 --- a/node/core/runtime-api/src/lib.rs +++ b/node/core/runtime-api/src/lib.rs @@ -19,6 +19,9 @@ //! This provides a clean, ownerless wrapper around the parachain-related runtime APIs. This crate //! can also be used to cache responses from heavy runtime APIs. +#![deny(unused_crate_dependencies)] +#![warn(missing_docs)] + use polkadot_subsystem::{ Subsystem, SpawnedSubsystem, SubsystemResult, SubsystemContext, FromOverseer, OverseerSignal, diff --git a/node/network/availability-distribution/Cargo.toml b/node/network/availability-distribution/Cargo.toml index 0e8413d8aab2..1f032b7f054b 100644 --- a/node/network/availability-distribution/Cargo.toml +++ b/node/network/availability-distribution/Cargo.toml @@ -7,26 +7,22 @@ edition = "2018" [dependencies] futures = "0.3.5" log = "0.4.11" -streamunordered = "0.5.1" codec = { package="parity-scale-codec", version = "1.3.4", features = ["std"] } -derive_more = "0.99.9" polkadot-primitives = { path = "../../../primitives" } polkadot-erasure-coding = { path = "../../../erasure-coding" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } -polkadot-network-bridge = { path = "../../network/bridge" } polkadot-node-network-protocol = { path = "../../network/protocol" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["std"] } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } +thiserror = "1.0.21" [dev-dependencies] polkadot-subsystem-testhelpers = { package = "polkadot-node-subsystem-test-helpers", path = "../../subsystem-test-helpers" } -bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["std"] } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } -parking_lot = "0.11.0" futures-timer = "3.0.2" env_logger = "0.7.1" assert_matches = "1.3.0" diff --git a/node/network/availability-distribution/src/lib.rs b/node/network/availability-distribution/src/lib.rs index b2e20ac8eb39..7a53452ef118 100644 --- a/node/network/availability-distribution/src/lib.rs +++ b/node/network/availability-distribution/src/lib.rs @@ -22,56 +22,99 @@ //! peers. Verified in this context means, the erasure chunks contained merkle proof //! is checked. +#![deny(unused_crate_dependencies, unused_qualifications)] + use codec::{Decode, Encode}; -use futures::{channel::oneshot, FutureExt}; +use futures::{channel::oneshot, FutureExt, TryFutureExt}; use sp_core::crypto::Public; use sp_keystore::{CryptoStore, SyncCryptoStorePtr}; use log::{trace, warn}; use polkadot_erasure_coding::branch_hash; +use polkadot_node_network_protocol::{ + v1 as protocol_v1, NetworkBridgeEvent, PeerId, ReputationChange as Rep, View, +}; +use polkadot_node_subsystem_util::metrics::{self, prometheus}; use polkadot_primitives::v1::{ - PARACHAIN_KEY_TYPE_ID, - BlakeTwo256, CommittedCandidateReceipt, CoreState, ErasureChunk, - Hash as Hash, HashT, Id as ParaId, - ValidatorId, ValidatorIndex, SessionIndex, + BlakeTwo256, CommittedCandidateReceipt, CoreState, ErasureChunk, Hash, HashT, Id as ParaId, + SessionIndex, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; use polkadot_subsystem::messages::{ - AllMessages, AvailabilityDistributionMessage, NetworkBridgeMessage, RuntimeApiMessage, - RuntimeApiRequest, AvailabilityStoreMessage, ChainApiMessage, + AllMessages, AvailabilityDistributionMessage, AvailabilityStoreMessage, ChainApiMessage, + NetworkBridgeMessage, RuntimeApiMessage, RuntimeApiRequest, }; use polkadot_subsystem::{ errors::{ChainApiError, RuntimeApiError}, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, Subsystem, SubsystemContext, SubsystemError, }; -use polkadot_node_subsystem_util::{ - metrics::{self, prometheus}, -}; -use polkadot_node_network_protocol::{ - v1 as protocol_v1, View, ReputationChange as Rep, PeerId, - NetworkBridgeEvent, -}; use std::collections::{HashMap, HashSet}; -use std::io; use std::iter; +use thiserror::Error; const TARGET: &'static str = "avad"; -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] enum Error { - #[from] - Erasure(polkadot_erasure_coding::Error), - #[from] - Io(io::Error), - #[from] - Oneshot(oneshot::Canceled), - #[from] - Subsystem(SubsystemError), - #[from] - RuntimeApi(RuntimeApiError), - #[from] - ChainApi(ChainApiError), + #[error("Sending PendingAvailability query failed")] + QueryPendingAvailabilitySendQuery(#[source] SubsystemError), + #[error("Response channel to obtain PendingAvailability failed")] + QueryPendingAvailabilityResponseChannel(#[source] oneshot::Canceled), + #[error("RuntimeAPI to obtain PendingAvailability failed")] + QueryPendingAvailability(#[source] RuntimeApiError), + + #[error("Sending StoreChunk query failed")] + StoreChunkSendQuery(#[source] SubsystemError), + #[error("Response channel to obtain StoreChunk failed")] + StoreChunkResponseChannel(#[source] oneshot::Canceled), + + #[error("Sending QueryChunk query failed")] + QueryChunkSendQuery(#[source] SubsystemError), + #[error("Response channel to obtain QueryChunk failed")] + QueryChunkResponseChannel(#[source] oneshot::Canceled), + + #[error("Sending QueryAncestors query failed")] + QueryAncestorsSendQuery(#[source] SubsystemError), + #[error("Response channel to obtain QueryAncestors failed")] + QueryAncestorsResponseChannel(#[source] oneshot::Canceled), + #[error("RuntimeAPI to obtain QueryAncestors failed")] + QueryAncestors(#[source] ChainApiError), + + #[error("Sending QuerySession query failed")] + QuerySessionSendQuery(#[source] SubsystemError), + #[error("Response channel to obtain QuerySession failed")] + QuerySessionResponseChannel(#[source] oneshot::Canceled), + #[error("RuntimeAPI to obtain QuerySession failed")] + QuerySession(#[source] RuntimeApiError), + + #[error("Sending QueryValidators query failed")] + QueryValidatorsSendQuery(#[source] SubsystemError), + #[error("Response channel to obtain QueryValidators failed")] + QueryValidatorsResponseChannel(#[source] oneshot::Canceled), + #[error("RuntimeAPI to obtain QueryValidators failed")] + QueryValidators(#[source] RuntimeApiError), + + #[error("Sending AvailabilityCores query failed")] + AvailabilityCoresSendQuery(#[source] SubsystemError), + #[error("Response channel to obtain AvailabilityCores failed")] + AvailabilityCoresResponseChannel(#[source] oneshot::Canceled), + #[error("RuntimeAPI to obtain AvailabilityCores failed")] + AvailabilityCores(#[source] RuntimeApiError), + + #[error("Sending AvailabilityCores query failed")] + QueryAvailabilitySendQuery(#[source] SubsystemError), + #[error("Response channel to obtain AvailabilityCores failed")] + QueryAvailabilityResponseChannel(#[source] oneshot::Canceled), + + #[error("Sending out a peer report message")] + ReportPeerMessageSend(#[source] SubsystemError), + + #[error("Sending a gossip message")] + TrackedGossipMessage(#[source] SubsystemError), + + #[error("Receive channel closed")] + IncomingMessageChannel(#[source] SubsystemError), } type Result = std::result::Result; @@ -199,22 +242,18 @@ impl ProtocolState { where Context: SubsystemContext, { - let candidates = - query_live_candidates(ctx, self, std::iter::once(relay_parent)).await?; + let candidates = query_live_candidates(ctx, self, std::iter::once(relay_parent)).await?; // register the relation of relay_parent to candidate.. // ..and the reverse association. for (relay_parent_or_ancestor, (receipt_hash, receipt)) in candidates.clone() { - self - .reverse + self.reverse .insert(receipt_hash.clone(), relay_parent_or_ancestor.clone()); - let per_candidate = self.per_candidate.entry(receipt_hash.clone()) - .or_default(); + let per_candidate = self.per_candidate.entry(receipt_hash.clone()).or_default(); per_candidate.validator_index = validator_index.clone(); per_candidate.validators = validators.clone(); - self - .receipts + self.receipts .entry(relay_parent_or_ancestor) .or_default() .insert((receipt_hash, receipt)); @@ -240,8 +279,7 @@ impl ProtocolState { .insert(relay_parent); } - self - .per_relay_parent + self.per_relay_parent .entry(relay_parent) .or_default() .ancestors = ancestors; @@ -314,17 +352,21 @@ where } NetworkBridgeEvent::PeerMessage(remote, msg) => { let gossiped_availability = match msg { - protocol_v1::AvailabilityDistributionMessage::Chunk(candidate_hash, chunk) => - AvailabilityGossipMessage { candidate_hash, erasure_chunk: chunk } + protocol_v1::AvailabilityDistributionMessage::Chunk(candidate_hash, chunk) => { + AvailabilityGossipMessage { + candidate_hash, + erasure_chunk: chunk, + } + } }; - process_incoming_peer_message(ctx, state, remote, gossiped_availability, metrics).await?; + process_incoming_peer_message(ctx, state, remote, gossiped_availability, metrics) + .await?; } } Ok(()) } - /// Handle the changes necessary when our view changes. async fn handle_our_view_change( ctx: &mut Context, @@ -346,19 +388,15 @@ where for added in added.iter() { let added = **added; let validators = query_validators(ctx, added).await?; - let validator_index = obtain_our_validator_index( - &validators, - keystore.clone(), - ).await; - state.add_relay_parent(ctx, added, validators, validator_index).await?; + let validator_index = obtain_our_validator_index(&validators, keystore.clone()).await; + state + .add_relay_parent(ctx, added, validators, validator_index) + .await?; } // handle all candidates for (candidate_hash, _receipt) in state.cached_live_candidates_unioned(added) { - let per_candidate = state - .per_candidate - .entry(candidate_hash) - .or_default(); + let per_candidate = state.per_candidate.entry(candidate_hash).or_default(); // assure the node has the validator role if per_candidate.validator_index.is_none() { @@ -388,19 +426,18 @@ where // distribute all erasure messages to interested peers for chunk_index in 0u32..(validator_count as u32) { - // only the peers which did not receive this particular erasure chunk - let per_candidate = state - .per_candidate - .entry(candidate_hash) - .or_default(); + let per_candidate = state.per_candidate.entry(candidate_hash).or_default(); // obtain the chunks from the cache, if not fallback // and query the availability store let message_id = (candidate_hash, chunk_index); - let erasure_chunk = if let Some(message) = per_candidate.message_vault.get(&chunk_index) { + let erasure_chunk = if let Some(message) = per_candidate.message_vault.get(&chunk_index) + { message.erasure_chunk.clone() - } else if let Some(erasure_chunk) = query_chunk(ctx, candidate_hash, chunk_index as ValidatorIndex).await? { + } else if let Some(erasure_chunk) = + query_chunk(ctx, candidate_hash, chunk_index as ValidatorIndex).await? + { erasure_chunk } else { continue; @@ -415,9 +452,7 @@ where !per_candidate .sent_messages .get(*peer) - .filter(|set| { - set.contains(&message_id) - }) + .filter(|set| set.contains(&message_id)) .is_some() }) .map(|peer| peer.clone()) @@ -427,7 +462,8 @@ where erasure_chunk, }; - send_tracked_gossip_message_to_peers(ctx, per_candidate, metrics, peers, message).await?; + send_tracked_gossip_message_to_peers(ctx, per_candidate, metrics, peers, message) + .await?; } } @@ -450,7 +486,8 @@ async fn send_tracked_gossip_message_to_peers( where Context: SubsystemContext, { - send_tracked_gossip_messages_to_peers(ctx, per_candidate, metrics, peers, iter::once(message)).await + send_tracked_gossip_messages_to_peers(ctx, per_candidate, metrics, peers, iter::once(message)) + .await } #[inline(always)] @@ -464,7 +501,8 @@ async fn send_tracked_gossip_messages_to_peer( where Context: SubsystemContext, { - send_tracked_gossip_messages_to_peers(ctx, per_candidate, metrics, vec![peer], message_iter).await + send_tracked_gossip_messages_to_peers(ctx, per_candidate, metrics, vec![peer], message_iter) + .await } async fn send_tracked_gossip_messages_to_peers( @@ -478,7 +516,7 @@ where Context: SubsystemContext, { if peers.is_empty() { - return Ok(()) + return Ok(()); } for message in message_iter { for peer in peers.iter() { @@ -506,7 +544,7 @@ where ), )) .await - .map_err::(Into::into)?; + .map_err(|e| Error::TrackedGossipMessage(e))?; metrics.on_chunk_distributed(); } @@ -543,8 +581,7 @@ where let per_candidate = state.per_candidate.entry(candidate_hash).or_default(); // obtain the relevant chunk indices not sent yet - let messages = ((0 as ValidatorIndex) - ..(per_candidate.validators.len() as ValidatorIndex)) + let messages = ((0 as ValidatorIndex)..(per_candidate.validators.len() as ValidatorIndex)) .into_iter() .filter_map(|erasure_chunk_index: ValidatorIndex| { let message_id = (candidate_hash, erasure_chunk_index); @@ -567,7 +604,8 @@ where .cloned() .collect::>(); - send_tracked_gossip_messages_to_peer(ctx, per_candidate, metrics, origin.clone(), messages).await?; + send_tracked_gossip_messages_to_peer(ctx, per_candidate, metrics, origin.clone(), messages) + .await?; } Ok(()) } @@ -580,8 +618,13 @@ async fn obtain_our_validator_index( keystore: SyncCryptoStorePtr, ) -> Option { for (idx, validator) in validators.iter().enumerate() { - if CryptoStore::has_keys(&*keystore, &[(validator.to_raw_vec(), PARACHAIN_KEY_TYPE_ID)]).await { - return Some(idx as ValidatorIndex) + if CryptoStore::has_keys( + &*keystore, + &[(validator.to_raw_vec(), PARACHAIN_KEY_TYPE_ID)], + ) + .await + { + return Some(idx as ValidatorIndex); } } None @@ -664,8 +707,13 @@ where live_candidate.descriptor.relay_parent.clone(), message.erasure_chunk.index, message.erasure_chunk.clone(), - ).await? { - warn!(target: TARGET, "Failed to store erasure chunk to availability store"); + ) + .await? + { + warn!( + target: TARGET, + "Failed to store erasure chunk to availability store" + ); } } } @@ -729,7 +777,10 @@ impl AvailabilityDistributionSubsystem { // work: process incoming messages from the overseer. let mut state = ProtocolState::default(); loop { - let message = ctx.recv().await.map_err::(Into::into)?; + let message = ctx + .recv() + .await + .map_err(|e| Error::IncomingMessageChannel(e))?; match message { FromOverseer::Communication { msg: AvailabilityDistributionMessage::NetworkBridgeUpdateV1(event), @@ -740,7 +791,9 @@ impl AvailabilityDistributionSubsystem { &mut state, &self.metrics, event, - ).await { + ) + .await + { warn!( target: TARGET, "Failed to handle incomming network messages: {:?}", e @@ -767,9 +820,15 @@ where Context: SubsystemContext + Sync + Send, { fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self + .run(ctx) + .map_err(|e| SubsystemError::with_origin("availability-distribution", e)) + .map(|_| ()) + .boxed(); + SpawnedSubsystem { name: "availability-distribution-subsystem", - future: Box::pin(async move { self.run(ctx) }.map(|_| ())), + future, } } } @@ -816,7 +875,6 @@ where HashMap::::with_capacity(capacity); for relay_parent in iter { - // register one of relay parents (not the ancestors) let mut ancestors = query_up_to_k_ancestors_in_same_session( ctx, @@ -827,7 +885,6 @@ where ancestors.push(relay_parent); - // ancestors might overlap, so check the cache too let unknown = ancestors .into_iter() @@ -841,10 +898,7 @@ where // directly extend the live_candidates with the cached value live_candidates.extend(receipts.into_iter().map( |(receipt_hash, receipt)| { - ( - relay_parent, - (receipt_hash.clone(), receipt.clone()), - ) + (relay_parent, (receipt_hash.clone(), receipt.clone())) }, )); Some(()) @@ -877,10 +931,12 @@ where RuntimeApiRequest::AvailabilityCores(tx), ))) .await - .map_err::(Into::into)?; + .map_err(|e| Error::AvailabilityCoresSendQuery(e))?; let all_para_ids: Vec<_> = rx - .await??; + .await + .map_err(|e| Error::AvailabilityCoresResponseChannel(e))? + .map_err(|e| Error::AvailabilityCores(e))?; let occupied_para_ids = all_para_ids .into_iter() @@ -910,14 +966,11 @@ where NetworkBridgeMessage::ReportPeer(peer, rep), )) .await - .map_err::(Into::into) + .map_err(|e| Error::ReportPeerMessageSend(e)) } /// Query the proof of validity for a particular candidate hash. -async fn query_data_availability( - ctx: &mut Context, - candidate_hash: Hash, -) -> Result +async fn query_data_availability(ctx: &mut Context, candidate_hash: Hash) -> Result where Context: SubsystemContext, { @@ -925,11 +978,12 @@ where ctx.send_message(AllMessages::AvailabilityStore( AvailabilityStoreMessage::QueryDataAvailability(candidate_hash, tx), )) - .await?; - rx.await.map_err::(Into::into) + .await + .map_err(|e| Error::QueryAvailabilitySendQuery(e))?; + rx.await + .map_err(|e| Error::QueryAvailabilityResponseChannel(e)) } - async fn query_chunk( ctx: &mut Context, candidate_hash: Hash, @@ -940,13 +994,13 @@ where { let (tx, rx) = oneshot::channel(); ctx.send_message(AllMessages::AvailabilityStore( - AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx), - )) - .await?; - rx.await.map_err::(Into::into) + AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx), + )) + .await + .map_err(|e| Error::QueryChunkSendQuery(e))?; + rx.await.map_err(|e| Error::QueryChunkResponseChannel(e)) } - async fn store_chunk( ctx: &mut Context, candidate_hash: Hash, @@ -958,16 +1012,19 @@ where Context: SubsystemContext, { let (tx, rx) = oneshot::channel(); - ctx.send_message(AllMessages::AvailabilityStore( - AvailabilityStoreMessage::StoreChunk { - candidate_hash, - relay_parent, - validator_index, - chunk: erasure_chunk, - tx, - } - )).await?; - rx.await.map_err::(Into::into) + ctx.send_message( + AllMessages::AvailabilityStore( + AvailabilityStoreMessage::StoreChunk { + candidate_hash, + relay_parent, + validator_index, + chunk: erasure_chunk, + tx, + } + )).await + .map_err(|e| Error::StoreChunkSendQuery(e))?; + + rx.await.map_err(|e| Error::StoreChunkResponseChannel(e)) } /// Request the head data for a particular para. @@ -981,12 +1038,15 @@ where { let (tx, rx) = oneshot::channel(); ctx.send_message(AllMessages::RuntimeApi(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::CandidatePendingAvailability(para, tx), - ))) - .await?; - rx.await? - .map_err::(Into::into) + relay_parent, + RuntimeApiRequest::CandidatePendingAvailability(para, tx), + ))) + .await + .map_err(|e| Error::QueryPendingAvailabilitySendQuery(e))?; + + rx.await + .map_err(|e| Error::QueryPendingAvailabilityResponseChannel(e))? + .map_err(|e| Error::QueryPendingAvailability(e)) } /// Query the validator set. @@ -1004,9 +1064,11 @@ where )); ctx.send_message(query_validators) - .await?; - rx.await? - .map_err::(Into::into) + .await + .map_err(|e| Error::QueryValidatorsSendQuery(e))?; + rx.await + .map_err(|e| Error::QueryValidatorsResponseChannel(e))? + .map_err(|e| Error::QueryValidators(e)) } /// Query the hash of the `K` ancestors @@ -1026,9 +1088,11 @@ where }); ctx.send_message(query_ancestors) - .await?; - rx.await? - .map_err::(Into::into) + .await + .map_err(|e| Error::QueryAncestorsSendQuery(e))?; + rx.await + .map_err(|e| Error::QueryAncestorsResponseChannel(e))? + .map_err(|e| Error::QueryAncestors(e)) } /// Query the session index of a relay parent @@ -1046,9 +1110,11 @@ where )); ctx.send_message(query_session_idx_for_child) - .await?; - rx.await? - .map_err::(Into::into) + .await + .map_err(|e| Error::QuerySessionSendQuery(e))?; + rx.await + .map_err(|e| Error::QuerySessionResponseChannel(e))? + .map_err(|e| Error::QuerySession(e)) } /// Queries up to k ancestors with the constraints of equiv session @@ -1089,7 +1155,6 @@ where Ok(acc) } - #[derive(Clone)] struct MetricsInner { gossipped_availability_chunks: prometheus::Counter, @@ -1108,12 +1173,14 @@ impl Metrics { } impl metrics::Metrics for Metrics { - fn try_register(registry: &prometheus::Registry) -> std::result::Result { + fn try_register( + registry: &prometheus::Registry, + ) -> std::result::Result { let metrics = MetricsInner { gossipped_availability_chunks: prometheus::register( prometheus::Counter::new( "parachain_gossipped_availability_chunks_total", - "Number of availability chunks gossipped to other peers." + "Number of availability chunks gossipped to other peers.", )?, registry, )?, diff --git a/node/network/availability-distribution/src/tests.rs b/node/network/availability-distribution/src/tests.rs index c04716639905..2387c943cfa8 100644 --- a/node/network/availability-distribution/src/tests.rs +++ b/node/network/availability-distribution/src/tests.rs @@ -17,22 +17,21 @@ use super::*; use assert_matches::assert_matches; use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks}; +use polkadot_node_network_protocol::ObservedRole; +use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::v1::{ AvailableData, BlockData, CandidateCommitments, CandidateDescriptor, GroupIndex, - GroupRotationInfo, HeadData, PersistedValidationData, OccupiedCore, - PoV, ScheduledCore, + GroupRotationInfo, HeadData, OccupiedCore, PersistedValidationData, PoV, ScheduledCore, }; use polkadot_subsystem_testhelpers::{self as test_helpers}; -use polkadot_node_subsystem_util::TimeoutExt; -use polkadot_node_network_protocol::ObservedRole; use futures::{executor, future, Future}; use futures_timer::Delay; -use smallvec::smallvec; -use std::{sync::Arc, time::Duration}; use sc_keystore::LocalKeystore; -use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore}; +use smallvec::smallvec; use sp_application_crypto::AppKey; +use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use std::{sync::Arc, time::Duration}; macro_rules! view { ( $( $hash:expr ),* $(,)? ) => [ @@ -46,9 +45,9 @@ macro_rules! delay { }; } -fn chunk_protocol_message(message: AvailabilityGossipMessage) - -> protocol_v1::AvailabilityDistributionMessage -{ +fn chunk_protocol_message( + message: AvailabilityGossipMessage, +) -> protocol_v1::AvailabilityDistributionMessage { protocol_v1::AvailabilityDistributionMessage::Chunk( message.candidate_hash, message.erasure_chunk, @@ -175,8 +174,12 @@ impl Default for TestState { let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::in_memory()); - SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, Some(&validators[0].to_seed())) - .expect("Insert key into keystore"); + SyncCryptoStore::sr25519_generate_new( + &*keystore, + ValidatorId::ID, + Some(&validators[0].to_seed()), + ) + .expect("Insert key into keystore"); let validator_public = validator_pubkeys(&validators); @@ -867,10 +870,7 @@ fn reputation_verification() { overseer_send( &mut virtual_overseer, AvailabilityDistributionMessage::NetworkBridgeUpdateV1( - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - chunk_protocol_message(valid2), - ), + NetworkBridgeEvent::PeerMessage(peer_a.clone(), chunk_protocol_message(valid2)), ), ) .await; diff --git a/node/network/bitfield-distribution/Cargo.toml b/node/network/bitfield-distribution/Cargo.toml index 7ffeacabb4d5..41e34cb2f7cf 100644 --- a/node/network/bitfield-distribution/Cargo.toml +++ b/node/network/bitfield-distribution/Cargo.toml @@ -6,17 +6,12 @@ edition = "2018" [dependencies] futures = "0.3.5" -futures-timer = "3.0.2" -log = "0.4.8" -streamunordered = "0.5.1" +log = "0.4.11" codec = { package="parity-scale-codec", version = "1.3.4" } -node-primitives = { package = "polkadot-node-primitives", path = "../../primitives" } polkadot-primitives = { path = "../../../primitives" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } -polkadot-network-bridge = { path = "../../network/bridge" } polkadot-node-network-protocol = { path = "../../network/protocol" } -sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } @@ -25,9 +20,7 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } -parking_lot = "0.11.0" maplit = "1.0.2" -smol = "0.3.3" env_logger = "0.7.1" assert_matches = "1.3.0" tempfile = "3.1.0" diff --git a/node/network/bitfield-distribution/src/lib.rs b/node/network/bitfield-distribution/src/lib.rs index 3abe1e821487..3f3db43c0ba9 100644 --- a/node/network/bitfield-distribution/src/lib.rs +++ b/node/network/bitfield-distribution/src/lib.rs @@ -20,8 +20,10 @@ //! for a particular relay parent. //! Independently of that, gossips on received messages from peers to other interested peers. +#![deny(unused_crate_dependencies)] + use codec::{Decode, Encode}; -use futures::{channel::oneshot, FutureExt}; +use futures::{channel::oneshot, FutureExt, TryFutureExt}; use log::{trace, warn}; use polkadot_subsystem::messages::*; @@ -33,6 +35,7 @@ use polkadot_node_subsystem_util::{ }; use polkadot_primitives::v1::{Hash, SignedAvailabilityBitfield, SigningContext, ValidatorId}; use polkadot_node_network_protocol::{v1 as protocol_v1, PeerId, NetworkBridgeEvent, View, ReputationChange}; +use polkadot_subsystem::SubsystemError; use std::collections::{HashMap, HashSet}; const COST_SIGNATURE_INVALID: ReputationChange = @@ -578,9 +581,15 @@ where C: SubsystemContext + Sync + Send, { fn start(self, ctx: C) -> SpawnedSubsystem { + let future = self.run(ctx) + .map_err(|e| { + SubsystemError::with_origin("bitfield-distribution", e) + }) + .map(|_| ()).boxed(); + SpawnedSubsystem { name: "bitfield-distribution-subsystem", - future: Box::pin(async move { Self::run(self, ctx) }.map(|_| ())), + future, } } } diff --git a/node/network/bridge/Cargo.toml b/node/network/bridge/Cargo.toml index fbf93fd37e55..32b41133574c 100644 --- a/node/network/bridge/Cargo.toml +++ b/node/network/bridge/Cargo.toml @@ -7,9 +7,7 @@ edition = "2018" [dependencies] async-trait = "0.1" futures = "0.3.5" -log = "0.4.8" -futures-timer = "3.0.2" -streamunordered = "0.5.1" +log = "0.4.11" polkadot-primitives = { path = "../../../primitives" } parity-scale-codec = "1.3.4" sc-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/network/bridge/src/lib.rs b/node/network/bridge/src/lib.rs index a09b61e62407..86098b4477e6 100644 --- a/node/network/bridge/src/lib.rs +++ b/node/network/bridge/src/lib.rs @@ -16,6 +16,10 @@ //! The Network Bridge Subsystem - protocol multiplexer for Polkadot. +#![deny(unused_crate_dependencies, unused_results)] +#![warn(missing_docs)] + + use parity_scale_codec::{Encode, Decode}; use futures::prelude::*; use futures::future::BoxFuture; @@ -219,13 +223,19 @@ impl Subsystem for NetworkBridge // Swallow error because failure is fatal to the node and we log with more precision // within `run_network`. let Self { network_service, authority_discovery_service } = self; - SpawnedSubsystem { - name: "network-bridge-subsystem", - future: run_network( + let future = run_network( network_service, authority_discovery_service, ctx, - ).map(|_| ()).boxed(), + ) + .map_err(|e| { + SubsystemError::with_origin("network-bridge", e) + }) + .map(|_| ()) + .boxed(); + SpawnedSubsystem { + name: "network-bridge-subsystem", + future, } } } @@ -654,7 +664,7 @@ where match peer_map.entry(peer.clone()) { hash_map::Entry::Occupied(_) => continue, hash_map::Entry::Vacant(vacant) => { - vacant.insert(PeerData { + let _ = vacant.insert(PeerData { view: View(Vec::new()), }); @@ -937,7 +947,7 @@ mod tests { futures::pin_mut!(test_fut); futures::pin_mut!(network_bridge); - executor::block_on(future::select(test_fut, network_bridge)); + let _ = executor::block_on(future::select(test_fut, network_bridge)); } async fn assert_sends_validation_event_to_all( diff --git a/node/network/bridge/src/validator_discovery.rs b/node/network/bridge/src/validator_discovery.rs index 542045f8c300..531e41cd629d 100644 --- a/node/network/bridge/src/validator_discovery.rs +++ b/node/network/bridge/src/validator_discovery.rs @@ -192,7 +192,7 @@ impl Service { Err(e) if e.is_disconnected() => { // the request is already revoked for peer_id in validator_ids { - on_revoke(&mut self.requested_validators, peer_id); + let _ = on_revoke(&mut self.requested_validators, peer_id); } return (network_service, authority_discovery_service); } @@ -217,7 +217,7 @@ impl Service { // They are going to be removed soon though: // https://github.com/paritytech/substrate/issues/6845 for addr in addresses.into_iter().take(MAX_ADDR_PER_PEER) { - multiaddr_to_add.insert(addr); + let _ = multiaddr_to_add.insert(addr); } } } @@ -247,7 +247,7 @@ impl Service { let result = authority_discovery_service.get_addresses_by_authority_id(id).await; if let Some(addresses) = result { for addr in addresses.into_iter().take(MAX_ADDR_PER_PEER) { - multiaddr_to_remove.insert(addr); + let _ = multiaddr_to_remove.insert(addr); } } } @@ -283,16 +283,16 @@ impl Service { let maybe_authority = authority_discovery_service.get_authority_id_by_peer_id(peer_id.clone()).await; if let Some(authority) = maybe_authority { for request in self.non_revoked_discovery_requests.iter_mut() { - request.on_authority_connected(&authority, peer_id); + let _ = request.on_authority_connected(&authority, peer_id); } - self.connected_validators.insert(authority, peer_id.clone()); + let _ = self.connected_validators.insert(authority, peer_id.clone()); } } pub async fn on_peer_disconnected(&mut self, peer_id: &PeerId, authority_discovery_service: &mut AD) { let maybe_authority = authority_discovery_service.get_authority_id_by_peer_id(peer_id.clone()).await; if let Some(authority) = maybe_authority { - self.connected_validators.remove(&authority); + let _ = self.connected_validators.remove(&authority); } } } diff --git a/node/network/collator-protocol/Cargo.toml b/node/network/collator-protocol/Cargo.toml index 8469bc5b9450..3b10015e1545 100644 --- a/node/network/collator-protocol/Cargo.toml +++ b/node/network/collator-protocol/Cargo.toml @@ -7,12 +7,10 @@ edition = "2018" [dependencies] futures = "0.3.5" log = "0.4.11" -derive_more = "0.99.9" +thiserror = "1.0.21" -codec = { package="parity-scale-codec", version = "1.3.4", features = ["std"] } polkadot-primitives = { path = "../../../primitives" } -polkadot-network-bridge = { path = "../../network/bridge" } polkadot-node-network-protocol = { path = "../../network/protocol" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } @@ -20,7 +18,6 @@ polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsys [dev-dependencies] env_logger = "0.7.1" assert_matches = "1.3.0" -smol-timeout = "0.1.0" smallvec = "1.4.2" futures-timer = "3.0.2" diff --git a/node/network/collator-protocol/src/lib.rs b/node/network/collator-protocol/src/lib.rs index ef659051e621..01212ea4ea2d 100644 --- a/node/network/collator-protocol/src/lib.rs +++ b/node/network/collator-protocol/src/lib.rs @@ -17,11 +17,12 @@ //! The Collator Protocol allows collators and validators talk to each other. //! This subsystem implements both sides of the collator protocol. -#![deny(missing_docs)] +#![deny(missing_docs, unused_crate_dependencies)] use std::time::Duration; use futures::{channel::oneshot, FutureExt}; use log::trace; +use thiserror::Error; use polkadot_subsystem::{ Subsystem, SubsystemContext, SubsystemError, SpawnedSubsystem, @@ -45,18 +46,18 @@ mod validator_side; const TARGET: &'static str = "colp"; const REQUEST_TIMEOUT: Duration = Duration::from_secs(1); -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] enum Error { - #[from] - Subsystem(SubsystemError), - #[from] - Oneshot(oneshot::Canceled), - #[from] - RuntimeApi(RuntimeApiError), - #[from] - UtilError(util::Error), - #[from] - Prometheus(prometheus::PrometheusError), + #[error(transparent)] + Subsystem(#[from] SubsystemError), + #[error(transparent)] + Oneshot(#[from] oneshot::Canceled), + #[error(transparent)] + RuntimeApi(#[from] RuntimeApiError), + #[error(transparent)] + UtilError(#[from] util::Error), + #[error(transparent)] + Prometheus(#[from] prometheus::PrometheusError), } impl From for Error { @@ -113,7 +114,9 @@ impl CollatorProtocolSubsystem { id, metrics, ).await, - } + }.map_err(|e| { + SubsystemError::with_origin("collator-protocol", e).into() + }) } } diff --git a/node/network/pov-distribution/Cargo.toml b/node/network/pov-distribution/Cargo.toml index 24f2107ca18f..2449a63016f7 100644 --- a/node/network/pov-distribution/Cargo.toml +++ b/node/network/pov-distribution/Cargo.toml @@ -6,19 +6,13 @@ edition = "2018" [dependencies] futures = "0.3.5" -log = "0.4.8" -futures-timer = "3.0.2" -streamunordered = "0.5.1" +log = "0.4.11" polkadot-primitives = { path = "../../../primitives" } -node-primitives = { package = "polkadot-node-primitives", path = "../../primitives" } -parity-scale-codec = "1.3.4" -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } polkadot-node-network-protocol = { path = "../../network/protocol" } [dev-dependencies] -parking_lot = "0.10.0" assert_matches = "1.3.0" sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } diff --git a/node/network/pov-distribution/src/lib.rs b/node/network/pov-distribution/src/lib.rs index ccce091c1af2..2a6f81361a60 100644 --- a/node/network/pov-distribution/src/lib.rs +++ b/node/network/pov-distribution/src/lib.rs @@ -19,9 +19,13 @@ //! This is a gossip implementation of code that is responsible for distributing PoVs //! among validators. +#![deny(unused_crate_dependencies)] +#![warn(missing_docs)] + use polkadot_primitives::v1::{Hash, PoV, CandidateDescriptor}; use polkadot_subsystem::{ - ActiveLeavesUpdate, OverseerSignal, SubsystemContext, Subsystem, SubsystemResult, FromOverseer, SpawnedSubsystem, + ActiveLeavesUpdate, OverseerSignal, SubsystemContext, Subsystem, SubsystemResult, SubsystemError, + FromOverseer, SpawnedSubsystem, messages::{ PoVDistributionMessage, RuntimeApiMessage, RuntimeApiRequest, AllMessages, NetworkBridgeMessage, }, @@ -60,9 +64,13 @@ impl Subsystem for PoVDistribution fn start(self, ctx: C) -> SpawnedSubsystem { // Swallow error because failure is fatal to the node and we log with more precision // within `run`. + let future = self.run(ctx) + .map_err(|e| SubsystemError::with_origin("pov-distribution", e)) + .map(|_| ()) + .boxed(); SpawnedSubsystem { name: "pov-distribution-subsystem", - future: self.run(ctx).map(|_| ()).boxed(), + future, } } } @@ -608,918 +616,4 @@ impl metrics::Metrics for Metrics { } #[cfg(test)] -mod tests { - use super::*; - use futures::executor; - use polkadot_primitives::v1::BlockData; - use assert_matches::assert_matches; - - fn make_pov(data: Vec) -> PoV { - PoV { block_data: BlockData(data) } - } - - fn make_peer_state(awaited: Vec<(Hash, Vec)>) - -> PeerState - { - PeerState { - awaited: awaited.into_iter().map(|(rp, h)| (rp, h.into_iter().collect())).collect() - } - } - - #[test] - fn distributes_to_those_awaiting_and_completes_local() { - let hash_a: Hash = [0; 32].into(); - let hash_b: Hash = [1; 32].into(); - - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - let peer_c = PeerId::random(); - - let (pov_send, pov_recv) = oneshot::channel(); - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let mut b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - b.fetching.insert(pov_hash, vec![pov_send]); - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - // peer A has hash_a in its view and is awaiting the PoV. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![pov_hash])]), - ); - - // peer B has hash_a in its view but is not awaiting. - s.insert( - peer_b.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - // peer C doesn't have hash_a in its view but is awaiting the PoV under hash_b. - s.insert( - peer_c.clone(), - make_peer_state(vec![(hash_b, vec![pov_hash])]), - ); - - s - }, - our_view: View(vec![hash_a, hash_b]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - let mut descriptor = CandidateDescriptor::default(); - descriptor.pov_hash = pov_hash; - - executor::block_on(async move { - handle_distribute( - &mut state, - &mut ctx, - hash_a, - descriptor, - Arc::new(pov.clone()), - ).await.unwrap(); - - assert!(!state.peer_state[&peer_a].awaited[&hash_a].contains(&pov_hash)); - assert!(state.peer_state[&peer_c].awaited[&hash_b].contains(&pov_hash)); - - // our local sender also completed - assert_eq!(&*pov_recv.await.unwrap(), &pov); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::SendValidationMessage(peers, message) - ) => { - assert_eq!(peers, vec![peer_a.clone()]); - assert_eq!( - message, - send_pov_message(hash_a, pov_hash, pov.clone()), - ); - } - ) - }); - } - - #[test] - fn we_inform_peers_with_same_view_we_are_awaiting() { - let hash_a: Hash = [0; 32].into(); - let hash_b: Hash = [1; 32].into(); - - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - - let (pov_send, _) = oneshot::channel(); - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - // peer A has hash_a in its view. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - // peer B doesn't have hash_a in its view. - s.insert( - peer_b.clone(), - make_peer_state(vec![(hash_b, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - let mut descriptor = CandidateDescriptor::default(); - descriptor.pov_hash = pov_hash; - - executor::block_on(async move { - handle_fetch( - &mut state, - &mut ctx, - hash_a, - descriptor, - pov_send, - ).await.unwrap(); - - assert_eq!(state.relay_parent_state[&hash_a].fetching[&pov_hash].len(), 1); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::SendValidationMessage(peers, message) - ) => { - assert_eq!(peers, vec![peer_a.clone()]); - assert_eq!( - message, - awaiting_message(hash_a, vec![pov_hash]), - ); - } - ) - }); - } - - #[test] - fn peer_view_change_leads_to_us_informing() { - let hash_a: Hash = [0; 32].into(); - let hash_b: Hash = [1; 32].into(); - - let peer_a = PeerId::random(); - - let (pov_a_send, _) = oneshot::channel(); - - let pov_a = make_pov(vec![1, 2, 3]); - let pov_a_hash = pov_a.hash(); - - let pov_b = make_pov(vec![4, 5, 6]); - let pov_b_hash = pov_b.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let mut b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - // pov_a is still being fetched, whereas the fetch of pov_b has already - // completed, as implied by the empty vector. - b.fetching.insert(pov_a_hash, vec![pov_a_send]); - b.fetching.insert(pov_b_hash, vec![]); - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - // peer A doesn't yet have hash_a in its view. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_b, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerViewChange(peer_a.clone(), View(vec![hash_a, hash_b])), - ).await.unwrap(); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::SendValidationMessage(peers, message) - ) => { - assert_eq!(peers, vec![peer_a.clone()]); - assert_eq!( - message, - awaiting_message(hash_a, vec![pov_a_hash]), - ); - } - ) - }); - } - - #[test] - fn peer_complete_fetch_and_is_rewarded() { - let hash_a: Hash = [0; 32].into(); - - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - - let (pov_send, pov_recv) = oneshot::channel(); - - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let mut b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - // pov is being fetched. - b.fetching.insert(pov_hash, vec![pov_send]); - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - // peers A and B are functionally the same. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s.insert( - peer_b.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - // Peer A answers our request before peer B. - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - send_pov_message(hash_a, pov_hash, pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_b.clone(), - send_pov_message(hash_a, pov_hash, pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - assert_eq!(&*pov_recv.await.unwrap(), &pov); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, BENEFIT_FRESH_POV); - } - ); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_b); - assert_eq!(rep, BENEFIT_LATE_POV); - } - ); - }); - } - - #[test] - fn peer_punished_for_sending_bad_pov() { - let hash_a: Hash = [0; 32].into(); - - let peer_a = PeerId::random(); - - let (pov_send, _) = oneshot::channel(); - - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let bad_pov = make_pov(vec![6, 6, 6]); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let mut b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - // pov is being fetched. - b.fetching.insert(pov_hash, vec![pov_send]); - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - // Peer A answers our request: right relay parent, awaited hash, wrong PoV. - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - send_pov_message(hash_a, pov_hash, bad_pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - // didn't complete our sender. - assert_eq!(state.relay_parent_state[&hash_a].fetching[&pov_hash].len(), 1); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, COST_UNEXPECTED_POV); - } - ); - }); - } - - #[test] - fn peer_punished_for_sending_unexpected_pov() { - let hash_a: Hash = [0; 32].into(); - - let peer_a = PeerId::random(); - - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - // Peer A answers our request: right relay parent, awaited hash, wrong PoV. - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - send_pov_message(hash_a, pov_hash, pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, COST_UNEXPECTED_POV); - } - ); - }); - } - - #[test] - fn peer_punished_for_sending_pov_out_of_our_view() { - let hash_a: Hash = [0; 32].into(); - let hash_b: Hash = [1; 32].into(); - - let peer_a = PeerId::random(); - - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - // Peer A answers our request: right relay parent, awaited hash, wrong PoV. - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - send_pov_message(hash_b, pov_hash, pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, COST_UNEXPECTED_POV); - } - ); - }); - } - - #[test] - fn peer_reported_for_awaiting_too_much() { - let hash_a: Hash = [0; 32].into(); - - let peer_a = PeerId::random(); - let n_validators = 10; - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators, - }; - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - let max_plausibly_awaited = n_validators * 2; - - // The peer awaits a plausible (albeit unlikely) amount of PoVs. - for i in 0..max_plausibly_awaited { - let pov_hash = make_pov(vec![i as u8; 32]).hash(); - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - awaiting_message(hash_a, vec![pov_hash]), - ).focus().unwrap(), - ).await.unwrap(); - } - - assert_eq!(state.peer_state[&peer_a].awaited[&hash_a].len(), max_plausibly_awaited); - - // The last straw: - let last_pov_hash = make_pov(vec![max_plausibly_awaited as u8; 32]).hash(); - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - awaiting_message(hash_a, vec![last_pov_hash]), - ).focus().unwrap(), - ).await.unwrap(); - - // No more bookkeeping for you! - assert_eq!(state.peer_state[&peer_a].awaited[&hash_a].len(), max_plausibly_awaited); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, COST_APPARENT_FLOOD); - } - ); - }); - } - - #[test] - fn peer_reported_for_awaiting_outside_their_view() { - let hash_a: Hash = [0; 32].into(); - let hash_b: Hash = [1; 32].into(); - - let peer_a = PeerId::random(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - s.insert(hash_a, BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }); - - s.insert(hash_b, BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }); - - s - }, - peer_state: { - let mut s = HashMap::new(); - - // Peer has only hash A in its view. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a, hash_b]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - let pov_hash = make_pov(vec![1, 2, 3]).hash(); - - // Hash B is in our view but not the peer's - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - awaiting_message(hash_b, vec![pov_hash]), - ).focus().unwrap(), - ).await.unwrap(); - - assert!(state.peer_state[&peer_a].awaited.get(&hash_b).is_none()); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, COST_AWAITED_NOT_IN_VIEW); - } - ); - }); - } - - #[test] - fn peer_reported_for_awaiting_outside_our_view() { - let hash_a: Hash = [0; 32].into(); - let hash_b: Hash = [1; 32].into(); - - let peer_a = PeerId::random(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - s.insert(hash_a, BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }); - - s - }, - peer_state: { - let mut s = HashMap::new(); - - // Peer has hashes A and B in their view. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![]), (hash_b, vec![])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - let pov_hash = make_pov(vec![1, 2, 3]).hash(); - - // Hash B is in peer's view but not ours. - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - awaiting_message(hash_b, vec![pov_hash]), - ).focus().unwrap(), - ).await.unwrap(); - - // Illegal `awaited` is ignored. - assert!(state.peer_state[&peer_a].awaited[&hash_b].is_empty()); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, COST_AWAITED_NOT_IN_VIEW); - } - ); - }); - } - - #[test] - fn peer_complete_fetch_leads_to_us_completing_others() { - let hash_a: Hash = [0; 32].into(); - - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - - let (pov_send, pov_recv) = oneshot::channel(); - - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let mut b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - // pov is being fetched. - b.fetching.insert(pov_hash, vec![pov_send]); - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![])]), - ); - - // peer B is awaiting peer A's request. - s.insert( - peer_b.clone(), - make_peer_state(vec![(hash_a, vec![pov_hash])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - send_pov_message(hash_a, pov_hash, pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - assert_eq!(&*pov_recv.await.unwrap(), &pov); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, BENEFIT_FRESH_POV); - } - ); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::SendValidationMessage(peers, message) - ) => { - assert_eq!(peers, vec![peer_b.clone()]); - assert_eq!( - message, - send_pov_message(hash_a, pov_hash, pov.clone()), - ); - } - ); - - assert!(!state.peer_state[&peer_b].awaited[&hash_a].contains(&pov_hash)); - }); - } - - #[test] - fn peer_completing_request_no_longer_awaiting() { - let hash_a: Hash = [0; 32].into(); - - let peer_a = PeerId::random(); - - let (pov_send, pov_recv) = oneshot::channel(); - - let pov = make_pov(vec![1, 2, 3]); - let pov_hash = pov.hash(); - - let mut state = State { - relay_parent_state: { - let mut s = HashMap::new(); - let mut b = BlockBasedState { - known: HashMap::new(), - fetching: HashMap::new(), - n_validators: 10, - }; - - // pov is being fetched. - b.fetching.insert(pov_hash, vec![pov_send]); - - s.insert(hash_a, b); - s - }, - peer_state: { - let mut s = HashMap::new(); - - // peer A is registered as awaiting. - s.insert( - peer_a.clone(), - make_peer_state(vec![(hash_a, vec![pov_hash])]), - ); - - s - }, - our_view: View(vec![hash_a]), - metrics: Default::default(), - }; - - let pool = sp_core::testing::TaskExecutor::new(); - let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); - - executor::block_on(async move { - handle_network_update( - &mut state, - &mut ctx, - NetworkBridgeEvent::PeerMessage( - peer_a.clone(), - send_pov_message(hash_a, pov_hash, pov.clone()), - ).focus().unwrap(), - ).await.unwrap(); - - assert_eq!(&*pov_recv.await.unwrap(), &pov); - - assert_matches!( - handle.recv().await, - AllMessages::NetworkBridge( - NetworkBridgeMessage::ReportPeer(peer, rep) - ) => { - assert_eq!(peer, peer_a); - assert_eq!(rep, BENEFIT_FRESH_POV); - } - ); - - // We received the PoV from peer A, so we do not consider it awaited by peer A anymore. - assert!(!state.peer_state[&peer_a].awaited[&hash_a].contains(&pov_hash)); - }); - } -} +mod tests; \ No newline at end of file diff --git a/node/network/pov-distribution/src/tests.rs b/node/network/pov-distribution/src/tests.rs new file mode 100644 index 000000000000..65f32ffe8810 --- /dev/null +++ b/node/network/pov-distribution/src/tests.rs @@ -0,0 +1,913 @@ +use super::*; +use futures::executor; +use polkadot_primitives::v1::BlockData; +use assert_matches::assert_matches; + +fn make_pov(data: Vec) -> PoV { + PoV { block_data: BlockData(data) } +} + +fn make_peer_state(awaited: Vec<(Hash, Vec)>) + -> PeerState +{ + PeerState { + awaited: awaited.into_iter().map(|(rp, h)| (rp, h.into_iter().collect())).collect() + } +} + +#[test] +fn distributes_to_those_awaiting_and_completes_local() { + let hash_a: Hash = [0; 32].into(); + let hash_b: Hash = [1; 32].into(); + + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + let peer_c = PeerId::random(); + + let (pov_send, pov_recv) = oneshot::channel(); + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let mut b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + b.fetching.insert(pov_hash, vec![pov_send]); + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + // peer A has hash_a in its view and is awaiting the PoV. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![pov_hash])]), + ); + + // peer B has hash_a in its view but is not awaiting. + s.insert( + peer_b.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + // peer C doesn't have hash_a in its view but is awaiting the PoV under hash_b. + s.insert( + peer_c.clone(), + make_peer_state(vec![(hash_b, vec![pov_hash])]), + ); + + s + }, + our_view: View(vec![hash_a, hash_b]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + let mut descriptor = CandidateDescriptor::default(); + descriptor.pov_hash = pov_hash; + + executor::block_on(async move { + handle_distribute( + &mut state, + &mut ctx, + hash_a, + descriptor, + Arc::new(pov.clone()), + ).await.unwrap(); + + assert!(!state.peer_state[&peer_a].awaited[&hash_a].contains(&pov_hash)); + assert!(state.peer_state[&peer_c].awaited[&hash_b].contains(&pov_hash)); + + // our local sender also completed + assert_eq!(&*pov_recv.await.unwrap(), &pov); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::SendValidationMessage(peers, message) + ) => { + assert_eq!(peers, vec![peer_a.clone()]); + assert_eq!( + message, + send_pov_message(hash_a, pov_hash, pov.clone()), + ); + } + ) + }); +} + +#[test] +fn we_inform_peers_with_same_view_we_are_awaiting() { + let hash_a: Hash = [0; 32].into(); + let hash_b: Hash = [1; 32].into(); + + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + + let (pov_send, _) = oneshot::channel(); + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + // peer A has hash_a in its view. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + // peer B doesn't have hash_a in its view. + s.insert( + peer_b.clone(), + make_peer_state(vec![(hash_b, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + let mut descriptor = CandidateDescriptor::default(); + descriptor.pov_hash = pov_hash; + + executor::block_on(async move { + handle_fetch( + &mut state, + &mut ctx, + hash_a, + descriptor, + pov_send, + ).await.unwrap(); + + assert_eq!(state.relay_parent_state[&hash_a].fetching[&pov_hash].len(), 1); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::SendValidationMessage(peers, message) + ) => { + assert_eq!(peers, vec![peer_a.clone()]); + assert_eq!( + message, + awaiting_message(hash_a, vec![pov_hash]), + ); + } + ) + }); +} + +#[test] +fn peer_view_change_leads_to_us_informing() { + let hash_a: Hash = [0; 32].into(); + let hash_b: Hash = [1; 32].into(); + + let peer_a = PeerId::random(); + + let (pov_a_send, _) = oneshot::channel(); + + let pov_a = make_pov(vec![1, 2, 3]); + let pov_a_hash = pov_a.hash(); + + let pov_b = make_pov(vec![4, 5, 6]); + let pov_b_hash = pov_b.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let mut b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + // pov_a is still being fetched, whereas the fetch of pov_b has already + // completed, as implied by the empty vector. + b.fetching.insert(pov_a_hash, vec![pov_a_send]); + b.fetching.insert(pov_b_hash, vec![]); + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + // peer A doesn't yet have hash_a in its view. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_b, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerViewChange(peer_a.clone(), View(vec![hash_a, hash_b])), + ).await.unwrap(); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::SendValidationMessage(peers, message) + ) => { + assert_eq!(peers, vec![peer_a.clone()]); + assert_eq!( + message, + awaiting_message(hash_a, vec![pov_a_hash]), + ); + } + ) + }); +} + +#[test] +fn peer_complete_fetch_and_is_rewarded() { + let hash_a: Hash = [0; 32].into(); + + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + + let (pov_send, pov_recv) = oneshot::channel(); + + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let mut b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + // pov is being fetched. + b.fetching.insert(pov_hash, vec![pov_send]); + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + // peers A and B are functionally the same. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s.insert( + peer_b.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + // Peer A answers our request before peer B. + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + send_pov_message(hash_a, pov_hash, pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_b.clone(), + send_pov_message(hash_a, pov_hash, pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + assert_eq!(&*pov_recv.await.unwrap(), &pov); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, BENEFIT_FRESH_POV); + } + ); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_b); + assert_eq!(rep, BENEFIT_LATE_POV); + } + ); + }); +} + +#[test] +fn peer_punished_for_sending_bad_pov() { + let hash_a: Hash = [0; 32].into(); + + let peer_a = PeerId::random(); + + let (pov_send, _) = oneshot::channel(); + + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let bad_pov = make_pov(vec![6, 6, 6]); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let mut b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + // pov is being fetched. + b.fetching.insert(pov_hash, vec![pov_send]); + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + // Peer A answers our request: right relay parent, awaited hash, wrong PoV. + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + send_pov_message(hash_a, pov_hash, bad_pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + // didn't complete our sender. + assert_eq!(state.relay_parent_state[&hash_a].fetching[&pov_hash].len(), 1); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, COST_UNEXPECTED_POV); + } + ); + }); +} + +#[test] +fn peer_punished_for_sending_unexpected_pov() { + let hash_a: Hash = [0; 32].into(); + + let peer_a = PeerId::random(); + + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + // Peer A answers our request: right relay parent, awaited hash, wrong PoV. + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + send_pov_message(hash_a, pov_hash, pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, COST_UNEXPECTED_POV); + } + ); + }); +} + +#[test] +fn peer_punished_for_sending_pov_out_of_our_view() { + let hash_a: Hash = [0; 32].into(); + let hash_b: Hash = [1; 32].into(); + + let peer_a = PeerId::random(); + + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + // Peer A answers our request: right relay parent, awaited hash, wrong PoV. + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + send_pov_message(hash_b, pov_hash, pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, COST_UNEXPECTED_POV); + } + ); + }); +} + +#[test] +fn peer_reported_for_awaiting_too_much() { + let hash_a: Hash = [0; 32].into(); + + let peer_a = PeerId::random(); + let n_validators = 10; + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators, + }; + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + let max_plausibly_awaited = n_validators * 2; + + // The peer awaits a plausible (albeit unlikely) amount of PoVs. + for i in 0..max_plausibly_awaited { + let pov_hash = make_pov(vec![i as u8; 32]).hash(); + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + awaiting_message(hash_a, vec![pov_hash]), + ).focus().unwrap(), + ).await.unwrap(); + } + + assert_eq!(state.peer_state[&peer_a].awaited[&hash_a].len(), max_plausibly_awaited); + + // The last straw: + let last_pov_hash = make_pov(vec![max_plausibly_awaited as u8; 32]).hash(); + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + awaiting_message(hash_a, vec![last_pov_hash]), + ).focus().unwrap(), + ).await.unwrap(); + + // No more bookkeeping for you! + assert_eq!(state.peer_state[&peer_a].awaited[&hash_a].len(), max_plausibly_awaited); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, COST_APPARENT_FLOOD); + } + ); + }); +} + +#[test] +fn peer_reported_for_awaiting_outside_their_view() { + let hash_a: Hash = [0; 32].into(); + let hash_b: Hash = [1; 32].into(); + + let peer_a = PeerId::random(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + s.insert(hash_a, BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }); + + s.insert(hash_b, BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }); + + s + }, + peer_state: { + let mut s = HashMap::new(); + + // Peer has only hash A in its view. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a, hash_b]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + let pov_hash = make_pov(vec![1, 2, 3]).hash(); + + // Hash B is in our view but not the peer's + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + awaiting_message(hash_b, vec![pov_hash]), + ).focus().unwrap(), + ).await.unwrap(); + + assert!(state.peer_state[&peer_a].awaited.get(&hash_b).is_none()); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, COST_AWAITED_NOT_IN_VIEW); + } + ); + }); +} + +#[test] +fn peer_reported_for_awaiting_outside_our_view() { + let hash_a: Hash = [0; 32].into(); + let hash_b: Hash = [1; 32].into(); + + let peer_a = PeerId::random(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + s.insert(hash_a, BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }); + + s + }, + peer_state: { + let mut s = HashMap::new(); + + // Peer has hashes A and B in their view. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![]), (hash_b, vec![])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + let pov_hash = make_pov(vec![1, 2, 3]).hash(); + + // Hash B is in peer's view but not ours. + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + awaiting_message(hash_b, vec![pov_hash]), + ).focus().unwrap(), + ).await.unwrap(); + + // Illegal `awaited` is ignored. + assert!(state.peer_state[&peer_a].awaited[&hash_b].is_empty()); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, COST_AWAITED_NOT_IN_VIEW); + } + ); + }); +} + +#[test] +fn peer_complete_fetch_leads_to_us_completing_others() { + let hash_a: Hash = [0; 32].into(); + + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + + let (pov_send, pov_recv) = oneshot::channel(); + + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let mut b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + // pov is being fetched. + b.fetching.insert(pov_hash, vec![pov_send]); + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![])]), + ); + + // peer B is awaiting peer A's request. + s.insert( + peer_b.clone(), + make_peer_state(vec![(hash_a, vec![pov_hash])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + send_pov_message(hash_a, pov_hash, pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + assert_eq!(&*pov_recv.await.unwrap(), &pov); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, BENEFIT_FRESH_POV); + } + ); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::SendValidationMessage(peers, message) + ) => { + assert_eq!(peers, vec![peer_b.clone()]); + assert_eq!( + message, + send_pov_message(hash_a, pov_hash, pov.clone()), + ); + } + ); + + assert!(!state.peer_state[&peer_b].awaited[&hash_a].contains(&pov_hash)); + }); +} + +#[test] +fn peer_completing_request_no_longer_awaiting() { + let hash_a: Hash = [0; 32].into(); + + let peer_a = PeerId::random(); + + let (pov_send, pov_recv) = oneshot::channel(); + + let pov = make_pov(vec![1, 2, 3]); + let pov_hash = pov.hash(); + + let mut state = State { + relay_parent_state: { + let mut s = HashMap::new(); + let mut b = BlockBasedState { + known: HashMap::new(), + fetching: HashMap::new(), + n_validators: 10, + }; + + // pov is being fetched. + b.fetching.insert(pov_hash, vec![pov_send]); + + s.insert(hash_a, b); + s + }, + peer_state: { + let mut s = HashMap::new(); + + // peer A is registered as awaiting. + s.insert( + peer_a.clone(), + make_peer_state(vec![(hash_a, vec![pov_hash])]), + ); + + s + }, + our_view: View(vec![hash_a]), + metrics: Default::default(), + }; + + let pool = sp_core::testing::TaskExecutor::new(); + let (mut ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + + executor::block_on(async move { + handle_network_update( + &mut state, + &mut ctx, + NetworkBridgeEvent::PeerMessage( + peer_a.clone(), + send_pov_message(hash_a, pov_hash, pov.clone()), + ).focus().unwrap(), + ).await.unwrap(); + + assert_eq!(&*pov_recv.await.unwrap(), &pov); + + assert_matches!( + handle.recv().await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep) + ) => { + assert_eq!(peer, peer_a); + assert_eq!(rep, BENEFIT_FRESH_POV); + } + ); + + // We received the PoV from peer A, so we do not consider it awaited by peer A anymore. + assert!(!state.peer_state[&peer_a].awaited[&hash_a].contains(&pov_hash)); + }); +} diff --git a/node/network/protocol/Cargo.toml b/node/network/protocol/Cargo.toml index abcb6ae2adda..11e1cc4180e3 100644 --- a/node/network/protocol/Cargo.toml +++ b/node/network/protocol/Cargo.toml @@ -9,6 +9,4 @@ description = "Primitives types for the Node-side" polkadot-primitives = { path = "../../../primitives" } polkadot-node-primitives = { path = "../../primitives" } parity-scale-codec = { version = "1.3.4", default-features = false, features = ["derive"] } -runtime_primitives = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/network/protocol/src/lib.rs b/node/network/protocol/src/lib.rs index 7658048ca5c8..31ec729d8b86 100644 --- a/node/network/protocol/src/lib.rs +++ b/node/network/protocol/src/lib.rs @@ -16,9 +16,13 @@ //! Network protocol types for parachains. +#![deny(unused_crate_dependencies, unused_results)] +#![warn(missing_docs)] + use polkadot_primitives::v1::Hash; use parity_scale_codec::{Encode, Decode}; use std::convert::TryFrom; +use std::fmt; pub use sc_network::{ReputationChange, PeerId}; @@ -32,6 +36,15 @@ pub type ProtocolVersion = u32; #[derive(Debug, Clone, Copy, PartialEq)] pub struct WrongVariant; +impl fmt::Display for WrongVariant { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "Wrong message variant") + } +} + +impl std::error::Error for WrongVariant {} + + /// The peer-sets that the network manages. Different subsystems will use different peer-sets. #[derive(Debug, Clone, Copy, PartialEq)] pub enum PeerSet { diff --git a/node/network/statement-distribution/Cargo.toml b/node/network/statement-distribution/Cargo.toml index 74baa9ad6e37..fa0b3786d014 100644 --- a/node/network/statement-distribution/Cargo.toml +++ b/node/network/statement-distribution/Cargo.toml @@ -7,13 +7,9 @@ edition = "2018" [dependencies] futures = "0.3.5" -log = "0.4.8" -futures-timer = "3.0.2" -streamunordered = "0.5.1" +log = "0.4.11" polkadot-primitives = { path = "../../../primitives" } node-primitives = { package = "polkadot-node-primitives", path = "../../primitives" } -parity-scale-codec = "1.3.4" -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } @@ -22,7 +18,6 @@ arrayvec = "0.5.1" indexmap = "1.4.0" [dev-dependencies] -parking_lot = "0.10.0" polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } assert_matches = "1.3.0" sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/network/statement-distribution/src/lib.rs b/node/network/statement-distribution/src/lib.rs index f0db3800c458..c294776dd2c3 100644 --- a/node/network/statement-distribution/src/lib.rs +++ b/node/network/statement-distribution/src/lib.rs @@ -19,6 +19,9 @@ //! This is responsible for distributing signed statements about candidate //! validity amongst validators. +#![deny(unused_crate_dependencies)] +#![warn(missing_docs)] + use polkadot_subsystem::{ Subsystem, SubsystemResult, SubsystemContext, SpawnedSubsystem, ActiveLeavesUpdate, FromOverseer, OverseerSignal, diff --git a/node/overseer/Cargo.toml b/node/overseer/Cargo.toml index bf0fd7785832..5341cb42e014 100644 --- a/node/overseer/Cargo.toml +++ b/node/overseer/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] futures = "0.3.5" -log = "0.4.8" +log = "0.4.11" futures-timer = "3.0.2" streamunordered = "0.5.1" polkadot-primitives = { path = "../../primitives" } @@ -21,6 +21,6 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-node-network-protocol = { path = "../network/protocol" } futures = { version = "0.3.5", features = ["thread-pool"] } futures-timer = "3.0.2" -femme = "2.0.1" -log = "0.4.8" -kv-log-macro = "1.0.6" +femme = "2.1.1" +log = "0.4.11" +kv-log-macro = "1.0.7" diff --git a/node/overseer/src/lib.rs b/node/overseer/src/lib.rs index e33a5756c80e..f54535e9e14b 100644 --- a/node/overseer/src/lib.rs +++ b/node/overseer/src/lib.rs @@ -54,6 +54,11 @@ //! .................................................................. //! ``` +// #![deny(unused_results)] +// unused dependencies can not work for test and examples at the same time +// yielding false positives +#![warn(missing_docs)] + use std::fmt::Debug; use std::pin::Pin; use std::sync::Arc; @@ -96,7 +101,6 @@ const STOP_DELAY: u64 = 1; // Target for logs. const LOG_TARGET: &'static str = "overseer"; - /// A type of messages that are sent from [`Subsystem`] to [`Overseer`]. /// /// It wraps a system-wide [`AllMessages`] type that represents all possible @@ -160,7 +164,7 @@ impl From> for BlockInfo { } } -/// Some event from outer world. +/// Some event from the outer world. enum Event { BlockImported(BlockInfo), BlockFinalized(BlockInfo), @@ -173,7 +177,7 @@ enum Event { enum ExternalRequest { WaitForActivation { hash: Hash, - response_channel: oneshot::Sender<()>, + response_channel: oneshot::Sender>, }, } @@ -208,7 +212,7 @@ impl OverseerHandler { /// Note that due the fact the overseer doesn't store the whole active-leaves set, only deltas, /// the response channel may never return if the hash was deactivated before this call. /// In this case, it's the caller's responsibility to ensure a timeout is set. - pub async fn wait_for_activation(&mut self, hash: Hash, response_channel: oneshot::Sender<()>) -> SubsystemResult<()> { + pub async fn wait_for_activation(&mut self, hash: Hash, response_channel: oneshot::Sender>) -> SubsystemResult<()> { self.events_tx.send(Event::ExternalRequest(ExternalRequest::WaitForActivation { hash, response_channel @@ -303,7 +307,11 @@ impl SubsystemContext for OverseerSubsystemContext { } async fn recv(&mut self) -> SubsystemResult> { - self.rx.next().await.ok_or(SubsystemError) + self.rx.next().await + .ok_or(SubsystemError::Context( + "No more messages in rx queue to process" + .to_owned() + )) } async fn spawn(&mut self, name: &'static str, s: Pin + Send>>) @@ -398,7 +406,7 @@ pub struct Overseer { s: S, /// Here we keep handles to spawned subsystems to be notified when they terminate. - running_subsystems: FuturesUnordered>, + running_subsystems: FuturesUnordered>>, /// Gather running subsystms' outbound streams into one. running_subsystems_rx: StreamUnordered>, @@ -407,7 +415,7 @@ pub struct Overseer { events_rx: mpsc::Receiver, /// External listeners waiting for a hash to be in the active-leave set. - activation_external_listeners: HashMap>>, + activation_external_listeners: HashMap>>>, /// A set of leaves that `Overseer` starts working with. /// @@ -1267,7 +1275,7 @@ where loop { select! { - _ = self.running_subsystems.next() => { + x = self.running_subsystems.next() => { if self.running_subsystems.is_empty() { break; } @@ -1285,7 +1293,7 @@ where for (hash, number) in leaves.into_iter() { update.activated.push(hash); - self.active_leaves.insert(hash, number); + let _ = self.active_leaves.insert(hash, number); self.on_head_activated(&hash); } @@ -1331,7 +1339,7 @@ where if let Poll::Ready(Some(finished)) = poll!(self.running_subsystems.next()) { log::error!(target: LOG_TARGET, "Subsystem finished unexpectedly {:?}", finished); self.stop().await; - return Err(SubsystemError); + return finished; } // Looks like nothing is left to be polled, let's take a break. @@ -1353,7 +1361,7 @@ where match self.active_leaves.entry(block.hash) { hash_map::Entry::Vacant(entry) => { update.activated.push(block.hash); - entry.insert(block.number); + let _ = entry.insert(block.number); self.on_head_activated(&block.hash); }, hash_map::Entry::Occupied(entry) => { @@ -1541,7 +1549,7 @@ where if let Some(listeners) = self.activation_external_listeners.remove(hash) { for listener in listeners { // it's fine if the listener is no longer interested - let _ = listener.send(()); + let _ = listener.send(Ok(())); } } } @@ -1567,7 +1575,7 @@ where ExternalRequest::WaitForActivation { hash, response_channel } => { if self.active_leaves.get(&hash).is_some() { // it's fine if the listener is no longer interested - let _ = response_channel.send(()); + let _ = response_channel.send(Ok(())); } else { self.activation_external_listeners.entry(hash).or_default().push(response_channel); } @@ -1586,7 +1594,7 @@ where fn spawn( spawner: &mut S, - futures: &mut FuturesUnordered>, + futures: &mut FuturesUnordered>>, streams: &mut StreamUnordered>, s: impl Subsystem>, ) -> SubsystemResult> { @@ -1604,8 +1612,8 @@ fn spawn( spawner.spawn(name, fut); - streams.push(from_rx); - futures.push(Box::pin(rx.map(|_| ()))); + let _ = streams.push(from_rx); + futures.push(Box::pin(rx.map(|e| { log::warn!("Dropping error {:?}", e); Ok(()) }))); let instance = Some(SubsystemInstance { tx: to_tx, diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index a593c6918f31..4c2fe7715600 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -20,6 +20,8 @@ //! not shared between the node and the runtime. This crate builds on top of the primitives defined //! there. +#![deny(missing_docs)] + use futures::Future; use parity_scale_codec::{Decode, Encode}; use polkadot_primitives::v1::{ diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index d5a2ef90bf26..c7a1af7c8209 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -16,12 +16,14 @@ //! Polkadot service. Specialized wrapper over substrate service. +#![deny(unused_results)] + pub mod chain_spec; mod grandpa_support; mod client; - use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; +#[cfg(feature = "full-node")] use log::info; use polkadot_node_core_proposer::ProposerFactory; use polkadot_overseer::{AllSubsystems, BlockInfo, Overseer, OverseerHandler}; @@ -383,7 +385,7 @@ pub fn new_full( })?; if config.offchain_worker.enabled { - service::build_offchain_workers( + let _ = service::build_offchain_workers( &config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(), ); } @@ -650,7 +652,7 @@ fn new_light(mut config: Configuration) -> Result<(TaskManage })?; if config.offchain_worker.enabled { - service::build_offchain_workers( + let _ = service::build_offchain_workers( &config, backend.clone(), task_manager.spawn_handle(), diff --git a/node/subsystem-test-helpers/Cargo.toml b/node/subsystem-test-helpers/Cargo.toml index 5f1388136a35..24f49ec3a1c7 100644 --- a/node/subsystem-test-helpers/Cargo.toml +++ b/node/subsystem-test-helpers/Cargo.toml @@ -7,7 +7,6 @@ description = "Subsystem traits and message definitions" [dependencies] async-trait = "0.1" -derive_more = "0.99.9" futures = "0.3.5" futures-timer = "3.0.2" log = "0.4.8" diff --git a/node/subsystem-test-helpers/src/lib.rs b/node/subsystem-test-helpers/src/lib.rs index ae1dd91ad1c5..0c9c8b05608b 100644 --- a/node/subsystem-test-helpers/src/lib.rs +++ b/node/subsystem-test-helpers/src/lib.rs @@ -16,6 +16,8 @@ //! Utilities for testing subsystems. +#![warn(missing_docs)] + use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem::{ FromOverseer, SubsystemContext, SubsystemError, SubsystemResult, Subsystem, @@ -169,7 +171,8 @@ impl SubsystemContext } async fn recv(&mut self) -> SubsystemResult> { - self.rx.next().await.ok_or(SubsystemError) + self.rx.next().await + .ok_or_else(|| SubsystemError::Context("Receiving end closed".to_owned())) } async fn spawn( diff --git a/node/subsystem-util/Cargo.toml b/node/subsystem-util/Cargo.toml index 68c6853485d5..f03cea134827 100644 --- a/node/subsystem-util/Cargo.toml +++ b/node/subsystem-util/Cargo.toml @@ -7,20 +7,18 @@ description = "Subsystem traits and message definitions" [dependencies] async-trait = "0.1" -derive_more = "0.99.9" futures = "0.3.5" futures-timer = "3.0.2" -log = "0.4.8" +log = "0.4.11" +thiserror = "1.0.21" parity-scale-codec = "1.3.4" parking_lot = { version = "0.10.0", optional = true } pin-project = "0.4.22" -smallvec = "1.4.1" streamunordered = "0.5.1" polkadot-node-primitives = { path = "../primitives" } polkadot-node-subsystem = { path = "../subsystem" } polkadot-primitives = { path = "../../primitives" } -polkadot-statement-table = { path = "../../statement-table" } sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/node/subsystem-util/src/lib.rs b/node/subsystem-util/src/lib.rs index efd55900b2b5..fb9e160b721d 100644 --- a/node/subsystem-util/src/lib.rs +++ b/node/subsystem-util/src/lib.rs @@ -22,6 +22,10 @@ //! //! This crate also reexports Prometheus metric types which are expected to be implemented by subsystems. +#![deny(unused_results)] +// #![deny(unused_crate_dependencies] causes false positives +// https://github.com/rust-lang/rust/issues/57274 +#![warn(missing_docs)] use polkadot_node_subsystem::{ errors::{ChainApiError, RuntimeApiError}, @@ -63,6 +67,7 @@ use std::{ time::Duration, }; use streamunordered::{StreamUnordered, StreamYield}; +use thiserror::Error; pub mod validator_discovery; @@ -82,35 +87,38 @@ pub const JOB_GRACEFUL_STOP_DURATION: Duration = Duration::from_secs(1); /// Capacity of channels to and from individual jobs pub const JOB_CHANNEL_CAPACITY: usize = 64; - /// Utility errors -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] pub enum Error { /// Attempted to send or receive on a oneshot channel which had been canceled - #[from] - Oneshot(oneshot::Canceled), + #[error(transparent)] + Oneshot(#[from] oneshot::Canceled), /// Attempted to send on a MPSC channel which has been canceled - #[from] - Mpsc(mpsc::SendError), + #[error(transparent)] + Mpsc(#[from] mpsc::SendError), /// A subsystem error - #[from] - Subsystem(SubsystemError), + #[error(transparent)] + Subsystem(#[from] SubsystemError), /// An error in the Chain API. - #[from] - ChainApi(ChainApiError), + #[error(transparent)] + ChainApi(#[from] ChainApiError), /// An error in the Runtime API. - #[from] - RuntimeApi(RuntimeApiError), + #[error(transparent)] + RuntimeApi(#[from] RuntimeApiError), /// The type system wants this even though it doesn't make sense - #[from] - Infallible(std::convert::Infallible), + #[error(transparent)] + Infallible(#[from] std::convert::Infallible), /// Attempted to convert from an AllMessages to a FromJob, and failed. + #[error("AllMessage not relevant to Job")] SenderConversion(String), /// The local node is not a validator. + #[error("Node is not a validator")] NotAValidator, /// The desired job is not present in the jobs list. + #[error("Relay parent {0} not of interest")] JobNotFound(Hash), /// Already forwarding errors to another sender + #[error("AlreadyForwarding")] AlreadyForwarding, } @@ -496,7 +504,7 @@ pub trait JobTrait: Unpin { /// Message type from the job. Typically a subset of AllMessages. type FromJob: 'static + Into + Send; /// Job runtime error. - type Error: 'static + std::fmt::Debug + Send; + type Error: 'static + std::error::Error + Send; /// Extra arguments this job needs to run properly. /// /// If no extra information is needed, it is perfectly acceptable to set it to `()`. @@ -538,13 +546,14 @@ pub trait JobTrait: Unpin { /// Error which can be returned by the jobs manager /// /// Wraps the utility error type and the job-specific error -#[derive(Debug, derive_more::From)] -pub enum JobsError { +#[derive(Debug, Error)] +pub enum JobsError { /// utility error - #[from] - Utility(Error), + #[error("Utility")] + Utility(#[source] Error), /// internal job error - Job(JobError), + #[error("Internal")] + Job(#[source] JobError), } /// Jobs manager for a subsystem @@ -645,7 +654,7 @@ impl Jobs { outgoing_msgs_handle, }; - self.running.insert(parent_hash, handle); + let _ = self.running.insert(parent_hash, handle); Ok(()) } @@ -654,7 +663,7 @@ impl Jobs { pub async fn stop_job(&mut self, parent_hash: Hash) -> Result<(), Error> { match self.running.remove(&parent_hash) { Some(handle) => { - Pin::new(&mut self.outgoing_msgs).remove(handle.outgoing_msgs_handle); + let _ = Pin::new(&mut self.outgoing_msgs).remove(handle.outgoing_msgs_handle); handle.stop().await; Ok(()) } @@ -830,7 +839,8 @@ where let metrics = metrics.clone(); if let Err(e) = jobs.spawn_job(hash, run_args.clone(), metrics) { log::error!("Failed to spawn a job: {:?}", e); - Self::fwd_err(Some(hash), e.into(), err_tx).await; + let e = JobsError::Utility(e); + Self::fwd_err(Some(hash), e, err_tx).await; return true; } } @@ -838,7 +848,8 @@ where for hash in deactivated { if let Err(e) = jobs.stop_job(hash).await { log::error!("Failed to stop a job: {:?}", e); - Self::fwd_err(Some(hash), e.into(), err_tx).await; + let e = JobsError::Utility(e); + Self::fwd_err(Some(hash), e, err_tx).await; return true; } } @@ -863,7 +874,8 @@ where .await { log::error!("failed to stop all jobs on conclude signal: {:?}", e); - Self::fwd_err(None, Error::from(e).into(), err_tx).await; + let e = Error::from(e); + Self::fwd_err(None, JobsError::Utility(e), err_tx).await; } return true; @@ -874,14 +886,16 @@ where Some(hash) => { if let Err(err) = jobs.send_msg(hash, to_job).await { log::error!("Failed to send a message to a job: {:?}", err); - Self::fwd_err(Some(hash), err.into(), err_tx).await; + let e = JobsError::Utility(err); + Self::fwd_err(Some(hash), e, err_tx).await; return true; } } None => { if let Err(err) = Job::handle_unanchored_msg(to_job) { log::error!("Failed to handle unhashed message: {:?}", err); - Self::fwd_err(None, JobsError::Job(err), err_tx).await; + let e = JobsError::Job(err); + Self::fwd_err(None, e, err_tx).await; return true; } } @@ -891,7 +905,8 @@ where Ok(Signal(BlockFinalized(_))) => {} Err(err) => { log::error!("error receiving message from subsystem context: {:?}", err); - Self::fwd_err(None, Error::from(err).into(), err_tx).await; + let e = JobsError::Utility(Error::from(err)); + Self::fwd_err(None, e, err_tx).await; return true; } } @@ -906,7 +921,8 @@ where ) { let msg = outgoing.expect("the Jobs stream never ends; qed"); if let Err(e) = ctx.send_message(msg.into()).await { - Self::fwd_err(None, Error::from(e).into(), err_tx).await; + let e = JobsError::Utility(e.into()); + Self::fwd_err(None, e, err_tx).await; } } } @@ -1032,6 +1048,8 @@ pub struct Timeout { /// Extends `Future` to allow time-limited futures. pub trait TimeoutExt: Future { + /// Adds a timeout of `duration` to the given `Future`. + /// Returns a new `Future`. fn timeout(self, duration: Duration) -> Timeout where Self: Sized, @@ -1066,6 +1084,7 @@ impl Future for Timeout { #[cfg(test)] mod tests { use super::{Error as UtilError, JobManager, JobTrait, JobsError, TimeoutExt, ToJobTrait}; + use thiserror::Error; use polkadot_node_subsystem::{ messages::{AllMessages, CandidateSelectionMessage}, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, Subsystem, @@ -1156,10 +1175,10 @@ mod tests { // Error will mostly be a wrapper to make the try operator more convenient; // deriving From implementations for most variants is recommended. // It must implement Debug for logging. - #[derive(Debug, derive_more::From)] + #[derive(Debug, Error)] enum Error { - #[from] - Sending(mpsc::SendError), + #[error(transparent)] + Sending(#[from]mpsc::SendError), } impl JobTrait for FakeCandidateSelectionJob { @@ -1261,7 +1280,7 @@ mod tests { fn starting_and_stopping_job_works() { let relay_parent: Hash = [0; 32].into(); let mut run_args = HashMap::new(); - run_args.insert( + let _ = run_args.insert( relay_parent.clone(), vec![FromJob::Test], ); @@ -1317,7 +1336,7 @@ mod tests { fn sending_to_a_non_running_job_do_not_stop_the_subsystem() { let relay_parent = Hash::repeat_byte(0x01); let mut run_args = HashMap::new(); - run_args.insert( + let _ = run_args.insert( relay_parent.clone(), vec![FromJob::Test], ); diff --git a/node/subsystem-util/src/validator_discovery.rs b/node/subsystem-util/src/validator_discovery.rs index 4fb964945b01..ac5bf1d47066 100644 --- a/node/subsystem-util/src/validator_discovery.rs +++ b/node/subsystem-util/src/validator_discovery.rs @@ -24,6 +24,7 @@ use futures::{ task::{Poll, self}, stream, }; +use thiserror::Error; use polkadot_node_subsystem::{ errors::RuntimeApiError, SubsystemError, @@ -34,17 +35,17 @@ use polkadot_primitives::v1::{Hash, ValidatorId, AuthorityDiscoveryId}; use sc_network::PeerId; /// Error when making a request to connect to validators. -#[derive(Debug, derive_more::From)] +#[derive(Debug, Error)] pub enum Error { /// Attempted to send or receive on a oneshot channel which had been canceled - #[from] - Oneshot(oneshot::Canceled), + #[error(transparent)] + Oneshot(#[from] oneshot::Canceled), /// A subsystem error. - #[from] - Subsystem(SubsystemError), + #[error(transparent)] + Subsystem(#[from] SubsystemError), /// An error in the Runtime API. - #[from] - RuntimeApi(RuntimeApiError), + #[error(transparent)] + RuntimeApi(#[from] RuntimeApiError), } /// Utility function to make it easier to connect to validators. diff --git a/node/subsystem/Cargo.toml b/node/subsystem/Cargo.toml index dfdac1e7cc12..55c45b7e3d1f 100644 --- a/node/subsystem/Cargo.toml +++ b/node/subsystem/Cargo.toml @@ -21,6 +21,7 @@ polkadot-statement-table = { path = "../../statement-table" } sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } smallvec = "1.4.1" sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +thiserror = "1.0.21" [dev-dependencies] assert_matches = "1.3.0" diff --git a/node/subsystem/src/errors.rs b/node/subsystem/src/errors.rs index 40edb1b3c148..5af573c87fc1 100644 --- a/node/subsystem/src/errors.rs +++ b/node/subsystem/src/errors.rs @@ -32,6 +32,8 @@ impl core::fmt::Display for RuntimeApiError { } } +impl std::error::Error for RuntimeApiError {} + /// A description of an error causing the chain API request to be unservable. #[derive(Debug, Clone)] pub struct ChainApiError { @@ -55,3 +57,5 @@ impl core::fmt::Display for ChainApiError { write!(f, "{}", self.msg) } } + +impl std::error::Error for ChainApiError {} diff --git a/node/subsystem/src/lib.rs b/node/subsystem/src/lib.rs index dea391c5b2c3..99f7b2c3cc90 100644 --- a/node/subsystem/src/lib.rs +++ b/node/subsystem/src/lib.rs @@ -31,6 +31,7 @@ use futures::future::BoxFuture; use polkadot_primitives::v1::Hash; use async_trait::async_trait; use smallvec::SmallVec; +use thiserror::Error; use crate::messages::AllMessages; @@ -105,6 +106,7 @@ pub enum FromOverseer { }, } + /// An error type that describes faults that may happen /// /// These are: @@ -112,30 +114,42 @@ pub enum FromOverseer { /// * Subsystems dying when they are not expected to /// * Subsystems not dying when they are told to die /// * etc. -#[derive(Debug, PartialEq, Eq)] -pub struct SubsystemError; - -impl From for SubsystemError { - fn from(_: mpsc::SendError) -> Self { - Self - } -} - -impl From for SubsystemError { - fn from(_: oneshot::Canceled) -> Self { - Self - } -} - -impl From for SubsystemError { - fn from(_: futures::task::SpawnError) -> Self { - Self - } +#[derive(Error, Debug)] +pub enum SubsystemError { + /// A notification connection is no longer valid. + #[error(transparent)] + NotifyCancellation(#[from] oneshot::Canceled), + + /// Queue does not accept another item. + #[error(transparent)] + QueueError(#[from] mpsc::SendError), + + /// An attempt to spawn a futures task did not succeed. + #[error(transparent)] + TaskSpawn(#[from] futures::task::SpawnError), + + /// An infallable error. + #[error(transparent)] + Infallible(#[from] std::convert::Infallible), + + /// An other error lacking particular type information. + #[error("Failed to {0}")] + Context(String), + + /// Per origin (or subsystem) annotations to wrap an error. + #[error("Error originated in {origin}")] + FromOrigin { + /// An additional anotation tag for the origin of `source`. + origin: &'static str, + /// The wrapped error. Marked as source for tracking the error chain. + #[source] source: Box + }, } -impl From for SubsystemError { - fn from(e: std::convert::Infallible) -> Self { - match e {} +impl SubsystemError { + /// Adds a `str` as `origin` to the given error `err`. + pub fn with_origin(origin: &'static str, err: E) -> Self { + Self::FromOrigin { origin, source: Box::new(err) } } } diff --git a/node/subsystem/src/messages.rs b/node/subsystem/src/messages.rs index e6df5791d4c8..c408dac3853e 100644 --- a/node/subsystem/src/messages.rs +++ b/node/subsystem/src/messages.rs @@ -23,7 +23,7 @@ //! Subsystems' APIs are defined separately from their implementation, leading to easier mocking. use futures::channel::{mpsc, oneshot}; - +use thiserror::Error; use polkadot_node_network_protocol::{ v1 as protocol_v1, NetworkBridgeEvent, ReputationChange, PeerId, }; @@ -97,7 +97,8 @@ impl CandidateBackingMessage { } /// Blanket error for validation failing for internal reasons. -#[derive(Debug)] +#[derive(Debug, Error)] +#[error("Validation failed with {0:?}")] pub struct ValidationFailed(pub String); /// Messages received by the Validation subsystem. diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 855cdcc6a21d..3acf2965fa63 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -17,7 +17,7 @@ sp-wasm-interface = { git = "https://github.com/paritytech/substrate", branch = polkadot-core-primitives = { path = "../core-primitives", default-features = false } # all optional crates. -derive_more = { version = "0.99.2", optional = true } +derive_more = { version = "0.99.11", optional = true } serde = { version = "1.0.102", default-features = false, features = [ "derive" ], optional = true } sp-externalities = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } diff --git a/validation/Cargo.toml b/validation/Cargo.toml index 60fe47a873ce..81ea0f746ee1 100644 --- a/validation/Cargo.toml +++ b/validation/Cargo.toml @@ -15,7 +15,6 @@ consensus = { package = "sp-consensus", git = "https://github.com/paritytech/sub runtime_primitives = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "master" } futures = "0.3.4" log = "0.4.8" -derive_more = "0.14.1" codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } grandpa = { package = "sc-finality-grandpa", git = "https://github.com/paritytech/substrate", branch = "master" } inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master" } @@ -26,6 +25,7 @@ block-builder = { package = "sc-block-builder", git = "https://github.com/parity trie = { package = "sp-trie", git = "https://github.com/paritytech/substrate", branch = "master" } babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", git = "https://github.com/paritytech/substrate", branch = "master" } +thiserror = "1.0.21" [dev-dependencies] sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/validation/src/error.rs b/validation/src/error.rs index 913b110e7f67..0665a70f3312 100644 --- a/validation/src/error.rs +++ b/validation/src/error.rs @@ -16,24 +16,25 @@ //! Errors that can occur during the validation process. +use thiserror::Error; + /// Error type for validation -#[derive(Debug, derive_more::Display, derive_more::From)] +#[derive(Debug, Error)] pub enum Error { /// Client error - Client(sp_blockchain::Error), + #[error(transparent)] + Client(#[from] sp_blockchain::Error), /// Consensus error - Consensus(consensus::error::Error), + #[error(transparent)] + Consensus(#[from] consensus::error::Error), /// Unexpected error checking inherents - #[display(fmt = "Unexpected error while checking inherents: {}", _0)] + #[error("Unexpected error while checking inherents: {0}")] InherentError(inherents::Error), } -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Client(ref err) => Some(err), - Error::Consensus(ref err) => Some(err), - _ => None, - } + +impl std::convert::From for Error { + fn from(inner: inherents::Error) -> Self { + Self::InherentError(inner) } }