diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 7cc24ee1e5..21fdf12ddb 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1287,30 +1287,30 @@ dependencies = [ [[package]] name = "libp2p" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core-derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-dns 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-floodsub 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-kad 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mdns 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-mplex 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-noise 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-plaintext 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ratelimit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-secio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-tcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-uds 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-websocket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-yamux 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-core-derive 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-dns 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-floodsub 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-identify 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-kad 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-mdns 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-mplex 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-noise 0.3.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-ping 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-plaintext 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-ratelimit 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-secio 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-tcp 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-uds 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-websocket 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-yamux 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "parity-multihash 0.1.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "stdweb 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1322,7 +1322,7 @@ dependencies = [ [[package]] name = "libp2p-core" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1333,15 +1333,15 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "multistream-select 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "multistream-select 0.3.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "parity-multihash 0.1.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rw-stream-sink 0.1.1 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "secp256k1 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1357,7 +1357,7 @@ dependencies = [ [[package]] name = "libp2p-core-derive" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1366,12 +1366,12 @@ dependencies = [ [[package]] name = "libp2p-dns" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "tokio-dns-unofficial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1379,14 +1379,14 @@ dependencies = [ [[package]] name = "libp2p-floodsub" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "cuckoofilter 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "protobuf 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1398,14 +1398,14 @@ dependencies = [ [[package]] name = "libp2p-identify" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1419,7 +1419,7 @@ dependencies = [ [[package]] name = "libp2p-kad" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1427,12 +1427,12 @@ dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-identify 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-ping 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-identify 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "libp2p-ping 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "parity-multihash 0.1.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1447,15 +1447,15 @@ dependencies = [ [[package]] name = "libp2p-mdns" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1468,12 +1468,12 @@ dependencies = [ [[package]] name = "libp2p-mplex" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1484,12 +1484,12 @@ dependencies = [ [[package]] name = "libp2p-noise" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "curve25519-dalek 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1502,14 +1502,14 @@ dependencies = [ [[package]] name = "libp2p-ping" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1521,21 +1521,21 @@ dependencies = [ [[package]] name = "libp2p-plaintext" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-ratelimit" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "aio-limited 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1544,7 +1544,7 @@ dependencies = [ [[package]] name = "libp2p-secio" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "asn1_der 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1553,12 +1553,12 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rw-stream-sink 0.1.1 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "secp256k1 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "stdweb 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1570,12 +1570,12 @@ dependencies = [ [[package]] name = "libp2p-tcp" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "tk-listen 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1584,25 +1584,25 @@ dependencies = [ [[package]] name = "libp2p-uds" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libp2p-websocket" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rw-stream-sink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "rw-stream-sink 0.1.1 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "stdweb 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "websocket 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1611,10 +1611,10 @@ dependencies = [ [[package]] name = "libp2p-yamux" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "yamux 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1807,7 +1807,7 @@ dependencies = [ [[package]] name = "multistream-select" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2179,13 +2179,13 @@ dependencies = [ [[package]] name = "parity-multiaddr" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-multihash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-multihash 0.1.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2193,7 +2193,7 @@ dependencies = [ [[package]] name = "parity-multihash" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2728,7 +2728,7 @@ dependencies = [ [[package]] name = "rw-stream-sink" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20#afd9623fccaac5ce1c41db1d82ad4def2652390f" dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3943,6 +3943,7 @@ dependencies = [ "substrate-consensus-common 0.1.0", "substrate-keyring 0.1.0", "substrate-network-libp2p 0.1.0", + "substrate-peerset 0.1.0", "substrate-primitives 0.1.0", "substrate-test-client 0.1.0", "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3958,7 +3959,7 @@ dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libp2p 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3966,6 +3967,7 @@ dependencies = [ "serde_derive 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-peerset 0.1.0", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3982,6 +3984,22 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-peerset" +version = "0.1.0" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-primitives" version = "0.1.0" @@ -5165,24 +5183,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" -"checksum libp2p 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bec5a2470cf03c5f0a74c55fc7b26fe969a1530d09d40e02ed76b287e3f9d9" -"checksum libp2p-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d53c885b93897902e5fdfd02340459bd940e970b73dd2cf506d4009e527489bf" -"checksum libp2p-core-derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e00ab475ca93c07ce539691003b682a0ff025f7dfb51e4ab29f5474457ded59b" -"checksum libp2p-dns 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "983bc7e80b6b5c89f5c6b674880bc058d37f481812692431914732ee2aeaf87a" -"checksum libp2p-floodsub 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aca5042008a7f7ddc53f5d40e6411ffebd66af22989a364303f2996fc5f39c79" -"checksum libp2p-identify 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9036ddde527bba0ae8a9037af7637c6c7a10ccdcdff6d2b00e7fe01f7620d400" -"checksum libp2p-kad 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9f42265a4ad1c3a153465babcf64ea32d33b3872f4e2a27fb1dc3d1ed9301fd" -"checksum libp2p-mdns 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "260cb7644da51ee2680c6413d350cee35441e78699b38e63600397867e32e3ab" -"checksum libp2p-mplex 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938678badaa29182f826308d86a89db53b99dd401121c551a979fd1b0a87af48" -"checksum libp2p-noise 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8820b79d0967de619e5dc991f84ffd5b888d06969bc62cf041412b2e9fc1e5c" -"checksum libp2p-ping 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad1b0983dbc2bf126583555c7a855ceaa28abfe764b9ede087d308b80ff7b2a" -"checksum libp2p-plaintext 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7cb42f7def0cb74781f6793f0fecbcfa0db79baee80adf18215abc30b1b6912c" -"checksum libp2p-ratelimit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f6971c2fc1cd938cd702b2320d3b5e3b5a353b1a3310de974769c3fa670d26e" -"checksum libp2p-secio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48a881b9c1b6ff0d35d985437d9f59d2d3ee2c529375aa188a978a1bd1d6f261" -"checksum libp2p-tcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79583caf603d93dc43ee314409c57e070e9a70c760631eea91822a25e898d1af" -"checksum libp2p-uds 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d72965117a6b0ead9b78af8682b0ab6d8c3d71dbb9aadbcb947ef2ef78c2c8a" -"checksum libp2p-websocket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b69b73fcef9d58ae90c6742389a1d29e14dcf3a4ab30bba2f0ca7e984fa59f9f" -"checksum libp2p-yamux 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "563222e2c303516fa097686573f003ee74761eb8d6b6df4204ae1aa07f5dc16c" +"checksum libp2p 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-core 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-core-derive 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-dns 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-floodsub 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-identify 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-kad 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-mdns 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-mplex 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-noise 0.3.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-ping 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-plaintext 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-ratelimit 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-secio 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-tcp 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-uds 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-websocket 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum libp2p-yamux 0.5.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" "checksum librocksdb-sys 5.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b9024327233e7fac7982440f73301c00046d438c5b1011e8f4e394226ce19007" "checksum libsecp256k1 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "688e8d65e495567c2c35ea0001b26b9debf0b4ea11f8cccc954233b75fc3428a" "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" @@ -5204,7 +5222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum multistream-select 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ed84364f0e921a32204896952ee80c7befc14a7a39f2c56cd955d71e8dae6" +"checksum multistream-select 0.3.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" "checksum names 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef320dab323286b50fb5cdda23f61c796a72a89998ab565ca32525c5c556f2da" "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" @@ -5227,8 +5245,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21c9c3a1623c71ed83964ff28cac6126e178920f7646d32c337eacb9152b2907" "checksum parity-codec-derive 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "864e9f66b58c0b38f0d6b511b6576afa2b678ae801b64220553bced57ac12df9" "checksum parity-crypto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b9db194dfbcfe3b398d63d765437a5c7232d59906e203055f0e993f6458ff1" -"checksum parity-multiaddr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61ae6944d4435d41f4d0f12108c5cbb9207cbb14bc8f2b4984c6e930dc9c8e41" -"checksum parity-multihash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e8eab0287ccde7821e337a124dc5a4f1d6e4c25d10cc91e3f9361615dd95076" +"checksum parity-multiaddr 0.2.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" +"checksum parity-multihash 0.1.0 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" "checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" "checksum parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fec5048fba72a2e01baeb0d08089db79aead4b57e2443df172fb1840075a233" "checksum parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4d05f1349491390b1730afba60bb20d55761bef489a954546b58b4b34e1e2ac" @@ -5288,7 +5306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rw-stream-sink 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d548a40fe17c3a77d54b82457b79fcc9b8a288d509ca20fbf5aa1dac386d22d6" +"checksum rw-stream-sink 0.1.1 (git+https://github.com/tomaka/libp2p-rs?branch=substrate-tmp-2019-03-20)" = "" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 4366458832..d1dbbe3305 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -100,3 +100,4 @@ is-it-maintained-open-issues = { repository = "paritytech/substrate" } [profile.release] # Substrate runtime requires unwinding. panic = "unwind" + diff --git a/substrate/core/network-libp2p/Cargo.toml b/substrate/core/network-libp2p/Cargo.toml index 35761c8c57..78bda5b193 100644 --- a/substrate/core/network-libp2p/Cargo.toml +++ b/substrate/core/network-libp2p/Cargo.toml @@ -13,7 +13,7 @@ bytes = "0.4" error-chain = { version = "0.12", default-features = false } fnv = "1.0" futures = "0.1" -libp2p = { version = "0.5.0", default-features = false, features = ["secio-secp256k1", "libp2p-websocket"] } +libp2p = { git = "https://github.com/tomaka/libp2p-rs", branch = "substrate-tmp-2019-03-20", default-features = false, features = ["secio-secp256k1", "libp2p-websocket"] } parking_lot = "0.7.1" lazy_static = "1.2" log = "0.4" @@ -22,6 +22,7 @@ serde = "1.0.70" serde_derive = "1.0.70" serde_json = "1.0.24" smallvec = "0.6" +substrate-peerset = { path = "../peerset" } tokio = "0.1" tokio-io = "0.1" tokio-timer = "0.2" diff --git a/substrate/core/network-libp2p/src/behaviour.rs b/substrate/core/network-libp2p/src/behaviour.rs index c71df6f7c5..e2e3762d53 100644 --- a/substrate/core/network-libp2p/src/behaviour.rs +++ b/substrate/core/network-libp2p/src/behaviour.rs @@ -15,14 +15,13 @@ // along with Substrate. If not, see . use crate::custom_proto::{CustomProto, CustomProtoOut, RegisteredProtocol}; -use crate::NetworkConfiguration; use futures::prelude::*; use libp2p::NetworkBehaviour; use libp2p::core::{Multiaddr, PeerId, ProtocolsHandler, PublicKey}; use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction}; use libp2p::core::swarm::{NetworkBehaviourEventProcess, PollParameters}; use libp2p::identify::{Identify, IdentifyEvent, protocol::IdentifyInfo}; -use libp2p::kad::{Kademlia, KademliaOut, KadConnectionType}; +use libp2p::kad::{Kademlia, KademliaOut}; use libp2p::ping::{Ping, PingEvent}; use log::{debug, trace, warn}; use std::{cmp, io, fmt, time::Duration, time::Instant}; @@ -50,21 +49,35 @@ pub struct Behaviour { impl Behaviour { /// Builds a new `Behaviour`. - // TODO: redundancy between config and local_public_key (https://github.com/libp2p/rust-libp2p/issues/745) - pub fn new(config: &NetworkConfiguration, local_public_key: PublicKey, protocols: RegisteredProtocol) -> Self { + pub fn new( + user_agent: String, + local_public_key: PublicKey, + protocol: RegisteredProtocol, + known_addresses: Vec<(PeerId, Multiaddr)>, + peerset: substrate_peerset::PeersetMut, + ) -> Self { let identify = { let proto_version = "/substrate/1.0".to_string(); - let user_agent = format!("{} ({})", config.client_version, config.node_name); Identify::new(proto_version, user_agent, local_public_key.clone()) }; - let local_peer_id = local_public_key.into_peer_id(); - let custom_protocols = CustomProto::new(config, &local_peer_id, protocols); + let custom_protocols = CustomProto::new(protocol, peerset); + + let mut kademlia = Kademlia::without_init(local_public_key.into_peer_id()); + for (peer_id, addr) in &known_addresses { + kademlia.add_connected_address(peer_id, addr.clone()); + } + kademlia.initialize(); Behaviour { ping: Ping::new(), custom_protocols, - discovery: DiscoveryBehaviour::new(local_peer_id), + discovery: DiscoveryBehaviour { + user_defined: known_addresses, + kademlia, + next_kad_random_query: Delay::new(Instant::now()), + duration_to_next_kad: Duration::from_secs(60), + }, identify, events: Vec::new(), } @@ -81,67 +94,9 @@ impl Behaviour { self.custom_protocols.send_packet(target, data) } - /// Returns the number of peers in the topology. - pub fn num_topology_peers(&self) -> usize { - self.custom_protocols.num_topology_peers() - } - - /// Flushes the topology to the disk. - pub fn flush_topology(&mut self) -> Result<(), io::Error> { - self.custom_protocols.flush_topology() - } - - /// Perform a cleanup pass, removing all obsolete addresses and peers. - /// - /// This should be done from time to time. - pub fn cleanup(&mut self) { - self.custom_protocols.cleanup(); - } - - /// Returns the list of reserved nodes. - pub fn reserved_peers(&self) -> impl Iterator { - self.custom_protocols.reserved_peers() - } - - /// Try to add a reserved peer. - pub fn add_reserved_peer(&mut self, peer_id: PeerId, addr: Multiaddr) { - self.custom_protocols.add_reserved_peer(peer_id, addr) - } - - /// Try to remove a reserved peer. - /// - /// If we are in reserved mode and we were connected to a node with this peer ID, then this - /// method will disconnect it and return its index. - pub fn remove_reserved_peer(&mut self, peer_id: PeerId) { - self.custom_protocols.remove_reserved_peer(peer_id) - } - - /// Returns true if we only accept reserved nodes. - pub fn is_reserved_only(&self) -> bool { - self.custom_protocols.is_reserved_only() - } - - /// Start accepting all peers again if we weren't. - pub fn accept_unreserved_peers(&mut self) { - self.custom_protocols.accept_unreserved_peers() - } - - /// Start refusing non-reserved nodes. Returns the list of nodes that have been disconnected. - pub fn deny_unreserved_peers(&mut self) { - self.custom_protocols.deny_unreserved_peers() - } - - /// Disconnects a peer and bans it for a little while. - /// - /// Same as `drop_node`, except that the same peer will not be able to reconnect later. - #[inline] - pub fn ban_node(&mut self, peer_id: PeerId) { - self.custom_protocols.ban_peer(peer_id) - } - - /// Returns a list of all the peers that are banned, and until when. - pub fn banned_nodes(&self) -> impl Iterator { - self.custom_protocols.banned_peers() + /// Returns the list of nodes that we know exist in the network. + pub fn known_peers(&self) -> impl Iterator { + self.discovery.kademlia.kbuckets_entries() } /// Returns true if we try to open protocols with the given peer. @@ -154,6 +109,13 @@ impl Behaviour { self.custom_protocols.is_open(peer_id) } + /// Adds a hard-coded address for the given peer, that never expires. + pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { + if self.discovery.user_defined.iter().all(|(p, a)| *p != peer_id && *a != addr) { + self.discovery.user_defined.push((peer_id, addr)); + } + } + /// Disconnects the custom protocols from a peer. /// /// The peer will still be able to use Kademlia or other protocols, but will get disconnected @@ -167,16 +129,6 @@ impl Behaviour { pub fn drop_node(&mut self, peer_id: &PeerId) { self.custom_protocols.disconnect_peer(peer_id) } - - /// Returns the list of peers in the topology. - pub fn known_peers(&self) -> impl Iterator { - self.custom_protocols.known_peers() - } - - /// Returns a list of addresses known for this peer, and their reputation score. - pub fn known_addresses(&mut self, peer_id: &PeerId) -> impl Iterator { - self.custom_protocols.known_addresses(peer_id) - } } /// Event that can be emitted by the behaviour. @@ -283,10 +235,7 @@ impl NetworkBehaviourEventProcess for Behav for addr in &info.listen_addrs { self.discovery.kademlia.add_connected_address(&peer_id, addr.clone()); } - self.custom_protocols.add_discovered_addrs( - &peer_id, - info.listen_addrs.iter().map(|addr| (addr.clone(), true)) - ); + self.custom_protocols.add_discovered_node(&peer_id); self.events.push(BehaviourOut::Identified { peer_id, info }); } IdentifyEvent::Error { .. } => {} @@ -301,17 +250,16 @@ impl NetworkBehaviourEventProcess for Behav impl NetworkBehaviourEventProcess for Behaviour { fn inject_event(&mut self, out: KademliaOut) { match out { - KademliaOut::Discovered { peer_id, addresses, ty } => { - self.custom_protocols.add_discovered_addrs( - &peer_id, - addresses.into_iter().map(|addr| (addr, ty == KadConnectionType::Connected)) - ); + KademliaOut::Discovered { .. } => {} + KademliaOut::KBucketAdded { peer_id, .. } => { + self.custom_protocols.add_discovered_node(&peer_id); } KademliaOut::FindNodeResult { key, closer_peers } => { - trace!(target: "sub-libp2p", "Kademlia query for {:?} yielded {:?} results", + trace!(target: "sub-libp2p", "Libp2p => Query for {:?} yielded {:?} results", key, closer_peers.len()); if closer_peers.is_empty() { - warn!(target: "sub-libp2p", "Kademlia random query has yielded empty results"); + warn!(target: "sub-libp2p", "Libp2p => Random Kademlia query has yielded empty \ + results"); } } // We never start any GET_PROVIDERS query. @@ -343,6 +291,9 @@ impl Behaviour { /// Implementation of `NetworkBehaviour` that discovers the nodes on the network. pub struct DiscoveryBehaviour { + /// User-defined list of nodes and their addresses. Typically includes bootstrap nodes and + /// reserved nodes. + user_defined: Vec<(PeerId, Multiaddr)>, /// Kademlia requests and answers. kademlia: Kademlia, /// Stream that fires when we need to perform the next random Kademlia query. @@ -351,16 +302,6 @@ pub struct DiscoveryBehaviour { duration_to_next_kad: Duration, } -impl DiscoveryBehaviour { - fn new(local_peer_id: PeerId) -> Self { - DiscoveryBehaviour { - kademlia: Kademlia::without_init(local_peer_id), - next_kad_random_query: Delay::new(Instant::now()), - duration_to_next_kad: Duration::from_secs(1), - } - } -} - impl NetworkBehaviour for DiscoveryBehaviour where TSubstream: AsyncRead + AsyncWrite, @@ -373,7 +314,21 @@ where } fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.kademlia.addresses_of_peer(peer_id) + let mut list = self.user_defined.iter() + .filter_map(|(p, a)| if p == peer_id { Some(a.clone()) } else { None }) + .collect::>(); + list.extend(self.kademlia.addresses_of_peer(peer_id)); + trace!(target: "sub-libp2p", "Addresses of {:?} are {:?}", peer_id, list); + if list.is_empty() { + if self.kademlia.kbuckets_entries().any(|p| p == peer_id) { + debug!(target: "sub-libp2p", "Requested dialing to {:?} (peer in k-buckets), \ + and no address was found", peer_id); + } else { + debug!(target: "sub-libp2p", "Requested dialing to {:?} (peer not in k-buckets), \ + and no address was found", peer_id); + } + } + list } fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { @@ -417,8 +372,8 @@ where Ok(Async::NotReady) => break, Ok(Async::Ready(_)) => { let random_peer_id = PeerId::random(); - debug!(target: "sub-libp2p", "Starting random Kademlia request for {:?}", - random_peer_id); + debug!(target: "sub-libp2p", "Libp2p <= Starting random Kademlia request for \ + {:?}", random_peer_id); self.kademlia.find_node(random_peer_id); // Reset the `Delay` to the next random. @@ -427,7 +382,7 @@ where Duration::from_secs(60)); }, Err(err) => { - warn!(target: "sub-libp2p", "Kad query timer errored: {:?}", err); + warn!(target: "sub-libp2p", "Kademlia query timer errored: {:?}", err); break } } diff --git a/substrate/core/network-libp2p/src/custom_proto/behaviour.rs b/substrate/core/network-libp2p/src/custom_proto/behaviour.rs index c8b931e4fb..7893501df5 100644 --- a/substrate/core/network-libp2p/src/custom_proto/behaviour.rs +++ b/substrate/core/network-libp2p/src/custom_proto/behaviour.rs @@ -15,65 +15,34 @@ // along with Substrate. If not, see . use crate::custom_proto::handler::{CustomProtoHandlerProto, CustomProtoHandlerOut, CustomProtoHandlerIn}; -use crate::custom_proto::topology::NetTopology; use crate::custom_proto::upgrade::{CustomMessage, RegisteredProtocol}; -use crate::{NetworkConfiguration, NonReservedPeerMode}; -use crate::parse_str_addr; -use fnv::{FnvHashMap, FnvHashSet}; +use fnv::FnvHashMap; use futures::prelude::*; use libp2p::core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; -use libp2p::core::{Endpoint, Multiaddr, PeerId}; -use log::{debug, trace, warn}; +use libp2p::core::{Multiaddr, PeerId}; +use log::{debug, error, trace, warn}; use smallvec::SmallVec; -use std::{cmp, error, io, marker::PhantomData, path::Path, time::Duration, time::Instant}; +use std::{collections::hash_map::Entry, cmp, error, io, marker::PhantomData, mem, time::Duration, time::Instant}; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_timer::Delay; - -// File where the network topology is stored. -const NODES_FILE: &str = "nodes.json"; -// Duration during which a peer is disabled. -const PEER_DISABLE_DURATION: Duration = Duration::from_secs(5 * 60); /// Network behaviour that handles opening substreams for custom protocols with other nodes. pub struct CustomProto { /// List of protocols to open with peers. Never modified. protocol: RegisteredProtocol, - /// Topology of the network. - topology: NetTopology, + /// Receiver for instructions about who to connect to or disconnect from. + peerset: substrate_peerset::PeersetMut, - /// List of peers for which the custom protocol is open. - opened_peers: FnvHashSet, + /// List of peers in our state. + peers: FnvHashMap, - /// List of peer handlers that were enabled. - /// - /// Note that it is possible for a peer to be in the shutdown process, in which case it will - /// not be in this list but will be present in `opened_peers`. - /// It is also possible that we have *just* enabled a peer, in which case it will be in this - /// list but not in `opened_peers`. - enabled_peers: FnvHashSet, + /// List of incoming messages we have sent to the peer set manager and that are waiting for an + /// answer. + incoming: SmallVec<[IncomingPeer; 6]>, - /// Maximum number of incoming non-reserved connections, taken from the config. Never modified. - max_incoming_connections: usize, - - /// Maximum number of outgoing non-reserved connections, taken from the config. Never modified. - max_outgoing_connections: usize, - - /// If true, only reserved peers can connect. - reserved_only: bool, - - /// List of the IDs of the peers we are connected to, and whether we're dialing or listening. - connected_peers: FnvHashMap, - - /// List of the IDs of the reserved peers. We always try to maintain a connection these peers. - reserved_peers: FnvHashSet, - - /// List of the IDs of peers that are forbidden, and the moment their ban expires. - banned_peers: Vec<(PeerId, Instant)>, - - /// When this delay expires, we need to synchronize our active connections with the - /// network topology. - next_connect_to_nodes: Delay, + /// We generate indices to identify incoming connections. This is the next value for the index + /// to use when a connection is incoming. + next_incoming_index: substrate_peerset::IncomingIndex, /// Events to produce from `poll()`. events: SmallVec<[NetworkBehaviourAction, CustomProtoOut>; 4]>, @@ -82,6 +51,87 @@ pub struct CustomProto { marker: PhantomData, } +/// State of a peer we're connected to. +#[derive(Debug)] +enum PeerState { + /// State is poisoned. This is a temporary state for a peer and we should always switch back + /// to it later. If it is found in the wild, that means there was either a panic or a bug in + /// the state machine code. + Poisoned, + + /// The peer misbehaved. If the PSM wants us to connect to this node, we will add an artificial + /// delay to the connection. + Banned { + /// Until when the node is banned. + until: Instant, + }, + + /// The peerset requested that we connect to this peer. We are not connected to this node. + PendingRequest { + /// When to actually start dialing. + timer: tokio_timer::Delay, + }, + + /// The peerset requested that we connect to this peer. We are currently dialing this peer. + Requested, + + /// We are connected to this peer but the peerset refused it. This peer can still perform + /// Kademlia queries and such, but should get disconnected in a few seconds. + Disabled { + /// How we are connected to this peer. + connected_point: ConnectedPoint, + /// If true, we still have a custom protocol open with it. It will likely get closed in + /// a short amount of time, but we need to keep the information in order to not have a + /// state mismatch. + open: bool, + /// If `Some`, the node is banned until the given `Instant`. + banned_until: Option, + }, + + /// We are connected to this peer but we are not opening any Substrate substream. The handler + /// will be enabled when `timer` fires. This peer can still perform Kademlia queries and such, + /// but should get disconnected in a few seconds. + DisabledPendingEnable { + /// How we are connected to this peer. + connected_point: ConnectedPoint, + /// If true, we still have a custom protocol open with it. It will likely get closed in + /// a short amount of time, but we need to keep the information in order to not have a + /// state mismatch. + open: bool, + /// When to enable this remote. + timer: tokio_timer::Delay, + }, + + /// We are connected to this peer and the peerset has accepted it. The handler is in the + /// enabled state. + Enabled { + /// How we are connected to this peer. + connected_point: ConnectedPoint, + /// If true, we have a custom protocol open with this peer. + open: bool, + }, + + /// We are connected to this peer, and we sent an incoming message to the peerset. The handler + /// is in initialization mode. We are waiting for the Accept or Reject from the peerset. There + /// is a corresponding entry in `incoming`. + Incoming { + /// How we are connected to this peer. + connected_point: ConnectedPoint, + }, +} + +/// State of an "incoming" message sent to the peer set manager. +#[derive(Debug)] +struct IncomingPeer { + /// Id of the node that is concerned. + peer_id: PeerId, + /// If true, this "incoming" still corresponds to an actual connection. If false, then the + /// connection corresponding to it has been closed or replaced already. + alive: bool, + /// Id that the we sent to the peerset. + incoming_id: substrate_peerset::IncomingIndex, +} + /// Event that can be emitted by the `CustomProto`. #[derive(Debug)] pub enum CustomProtoOut { @@ -122,164 +172,123 @@ pub enum CustomProtoOut { } impl CustomProto { - /// Creates a `CustomProto`. - pub fn new(config: &NetworkConfiguration, local_peer_id: &PeerId, protocol: RegisteredProtocol) -> Self { - // Initialize the topology of the network. - let mut topology = if let Some(ref path) = config.net_config_path { - let path = Path::new(path).join(NODES_FILE); - debug!(target: "sub-libp2p", "Initializing peer store for JSON file {:?}", path); - NetTopology::from_file(local_peer_id.clone(), path) - } else { - debug!(target: "sub-libp2p", "No peers file configured ; peers won't be saved"); - NetTopology::memory(local_peer_id.clone()) - }; - - // Add the bootstrap nodes to the topology. - for bootnode in config.boot_nodes.iter() { - if let Ok((peer_id, addr)) = parse_str_addr(bootnode) { - topology.add_bootstrap_addr(&peer_id, addr.clone()); - } - } - - let max_incoming_connections = config.in_peers as usize; - let max_outgoing_connections = config.out_peers as usize; - - // Expected maximum number of connections. - let connec_cap = max_incoming_connections - .saturating_add(max_outgoing_connections) - .saturating_add(4); // We add an arbitrary number for reserved peers slots - + /// Creates a `CustomProtos`. + pub fn new( + protocol: RegisteredProtocol, + peerset: substrate_peerset::PeersetMut, + ) -> Self { CustomProto { protocol, - topology, - max_incoming_connections, - max_outgoing_connections, - reserved_only: config.non_reserved_mode == NonReservedPeerMode::Deny, - connected_peers: Default::default(), - reserved_peers: Default::default(), - banned_peers: Vec::new(), - opened_peers: FnvHashSet::with_capacity_and_hasher(connec_cap, Default::default()), - enabled_peers: FnvHashSet::with_capacity_and_hasher(connec_cap, Default::default()), - next_connect_to_nodes: Delay::new(Instant::now()), + peerset, + peers: FnvHashMap::default(), + incoming: SmallVec::new(), + next_incoming_index: substrate_peerset::IncomingIndex(0), events: SmallVec::new(), marker: PhantomData, } } - /// Returns the list of reserved nodes. - pub fn reserved_peers(&self) -> impl Iterator { - self.reserved_peers.iter() - } - - /// Adds a reserved peer. - pub fn add_reserved_peer(&mut self, peer_id: PeerId, addr: Multiaddr) { - self.topology.add_bootstrap_addr(&peer_id, addr); - self.reserved_peers.insert(peer_id); - - // Trigger a `connect_to_nodes` round. - self.next_connect_to_nodes = Delay::new(Instant::now()); - } - - /// Removes a reserved peer. - /// - /// If we are in reserved mode and we were connected to a node with this peer ID, then this - /// method will disconnect it and return its index. - pub fn remove_reserved_peer(&mut self, peer_id: PeerId) { - self.reserved_peers.remove(&peer_id); - } - - /// Returns true if we only accept reserved nodes. - pub fn is_reserved_only(&self) -> bool { - self.reserved_only - } - - /// Start accepting all peers again if we weren't. - pub fn accept_unreserved_peers(&mut self) { - if !self.reserved_only { - return - } - - self.reserved_only = false; - // Trigger a `connect_to_nodes` round. - self.next_connect_to_nodes = Delay::new(Instant::now()); - } - - /// Start refusing non-reserved nodes. - pub fn deny_unreserved_peers(&mut self) { - if self.reserved_only { - return - } - - self.reserved_only = true; - - // Disconnecting nodes that are connected to us and that aren't reserved - let reserved_peers = &mut self.reserved_peers; - let events = &mut self.events; - self.enabled_peers.retain(move |peer_id| { - if reserved_peers.contains(peer_id) { - return true - } - events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Disable, - }); - false - }) - } - /// Disconnects the given peer if we are connected to it. - pub fn disconnect_peer(&mut self, peer: &PeerId) { - if self.reserved_peers.contains(peer) { - warn!(target: "sub-libp2p", "Ignored attempt to disconnect reserved peer {:?}", peer); - return; - } - - if self.enabled_peers.remove(peer) { - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer.clone(), - event: CustomProtoHandlerIn::Disable, - }); - } + pub fn disconnect_peer(&mut self, peer_id: &PeerId) { + debug!(target: "sub-libp2p", "Disconnecting {:?} by request from the external API", peer_id); + self.disconnect_peer_inner(peer_id, None); } - /// Disconnects the given peer if we are connected to it and disables it for a little while. - pub fn ban_peer(&mut self, peer_id: PeerId) { - if self.reserved_peers.contains(&peer_id) { - warn!(target: "sub-libp2p", "Ignored attempt to ban reserved peer {:?}", peer_id); - return; - } + /// Inner implementation of `disconnect_peer`. If `ban` is `Some`, we ban the node for the + /// specific duration. + fn disconnect_peer_inner(&mut self, peer_id: &PeerId, ban: Option) { + let mut entry = if let Entry::Occupied(entry) = self.peers.entry(peer_id.clone()) { + entry + } else { + return + }; - // Peer is already banned - if let Some(pos) = self.banned_peers.iter().position(|(p, _)| p == &peer_id) { - if self.banned_peers[pos].1 > Instant::now() { - return - } else { - self.banned_peers.remove(pos); - } - } + match mem::replace(entry.get_mut(), PeerState::Poisoned) { + // We're not connected anyway. + st @ PeerState::Disabled { .. } => *entry.into_mut() = st, + st @ PeerState::Requested => *entry.into_mut() = st, + st @ PeerState::PendingRequest { .. } => *entry.into_mut() = st, + st @ PeerState::Banned { .. } => *entry.into_mut() = st, - self.banned_peers.push((peer_id.clone(), Instant::now() + PEER_DISABLE_DURATION)); - if self.enabled_peers.remove(&peer_id) { - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id, - event: CustomProtoHandlerIn::Disable, - }); - } - } + // DisabledPendingEnable => Disabled. + PeerState::DisabledPendingEnable { open, connected_point, timer } => { + debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); + self.peerset.dropped(peer_id); + let banned_until = Some(if let Some(ban) = ban { + cmp::max(timer.deadline(), Instant::now() + ban) + } else { + timer.deadline() + }); + *entry.into_mut() = PeerState::Disabled { open, connected_point, banned_until } + }, - /// Returns a list of all the peers that are banned, and until when. - pub fn banned_peers(&self) -> impl Iterator { - self.banned_peers.iter().map(|&(ref id, until)| (id, until)) + // Enabled => Disabled. + PeerState::Enabled { open, connected_point } => { + debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); + self.peerset.dropped(peer_id); + debug!(target: "sub-libp2p", "Handler({:?}) <= Disable", peer_id); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: peer_id.clone(), + event: CustomProtoHandlerIn::Disable, + }); + let banned_until = ban.map(|dur| Instant::now() + dur); + *entry.into_mut() = PeerState::Disabled { open, connected_point, banned_until } + }, + + // Incoming => Disabled. + PeerState::Incoming { connected_point, .. } => { + let inc = if let Some(inc) = self.incoming.iter_mut() + .find(|i| i.peer_id == *entry.key() && i.alive) { + inc + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: no entry in \ + incoming for incoming peer"); + return + }; + + inc.alive = false; + debug!(target: "sub-libp2p", "Handler({:?}) <= Disable", peer_id); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: peer_id.clone(), + event: CustomProtoHandlerIn::Disable, + }); + let banned_until = ban.map(|dur| Instant::now() + dur); + *entry.into_mut() = PeerState::Disabled { open: false, connected_point, banned_until } + }, + + PeerState::Poisoned => + error!(target: "sub-libp2p", "State of {:?} is poisoned", peer_id), + } } /// Returns true if we try to open protocols with the given peer. pub fn is_enabled(&self, peer_id: &PeerId) -> bool { - self.enabled_peers.contains(peer_id) + match self.peers.get(peer_id) { + None => false, + Some(PeerState::Disabled { .. }) => false, + Some(PeerState::DisabledPendingEnable { .. }) => false, + Some(PeerState::Enabled { .. }) => true, + Some(PeerState::Incoming { .. }) => false, + Some(PeerState::Requested) => false, + Some(PeerState::PendingRequest { .. }) => false, + Some(PeerState::Banned { .. }) => false, + Some(PeerState::Poisoned) => false, + } } /// Returns true if we have opened a protocol with the given peer. pub fn is_open(&self, peer_id: &PeerId) -> bool { - self.opened_peers.contains(peer_id) + match self.peers.get(peer_id) { + None => false, + Some(PeerState::Disabled { open, .. }) => *open, + Some(PeerState::DisabledPendingEnable { open, .. }) => *open, + Some(PeerState::Enabled { open, .. }) => *open, + Some(PeerState::Incoming { .. }) => false, + Some(PeerState::Requested) => false, + Some(PeerState::PendingRequest { .. }) => false, + Some(PeerState::Banned { .. }) => false, + Some(PeerState::Poisoned) => false, + } } /// Sends a message to a peer. @@ -289,6 +298,11 @@ impl CustomProto { /// Also note that even we have a valid open substream, it may in fact be already closed /// without us knowing, in which case the packet will not be received. pub fn send_packet(&mut self, target: &PeerId, message: TMessage) { + if !self.is_open(target) { + return; + } + + trace!(target: "sub-libp2p", "Handler({:?}) <= Packet", target); self.events.push(NetworkBehaviourAction::SendEvent { peer_id: target.clone(), event: CustomProtoHandlerIn::SendCustomMessage { @@ -297,134 +311,235 @@ impl CustomProto { }); } - /// Indicates to the topology that we have discovered new addresses for a given node. - pub fn add_discovered_addrs( - &mut self, - peer_id: &PeerId, - addrs: I, - ) where I: Iterator { - if self.topology.add_discovered_addrs(peer_id, addrs) { - // Trigger a `connect_to_nodes` round. - self.next_connect_to_nodes = Delay::new(Instant::now()); - } + /// Indicates to the peerset that we have discovered new addresses for a given node. + pub fn add_discovered_node(&mut self, peer_id: &PeerId) { + debug!(target: "sub-libp2p", "PSM <= Discovered({:?})", peer_id); + self.peerset.discovered(peer_id.clone()) } - /// Returns the number of peers in the topology. - pub fn num_topology_peers(&self) -> usize { - self.topology.num_peers() - } - - /// Flushes the topology to the disk. - pub fn flush_topology(&mut self) -> Result<(), io::Error> { - self.topology.flush_to_disk() - } - - /// Perform a cleanup pass, removing all obsolete addresses and peers. - /// - /// This should be done from time to time. - pub fn cleanup(&mut self) { - self.topology.cleanup(); - } - - /// Returns the list of peers in the topology. - pub fn known_peers(&self) -> impl Iterator { - self.topology.known_peers() - } - - /// Returns a list of addresses known for this peer, and their reputation score. - pub fn known_addresses(&mut self, peer_id: &PeerId) -> impl Iterator { - self.topology.addresses_of_peer(peer_id, true) - } - - /// Updates the attempted connections to nodes. - /// - /// Also updates `next_connect_to_nodes` with the earliest known moment when we need to - /// update connections again. - fn connect_to_nodes(&mut self, params: &mut PollParameters) { - // Value of `Instant::now()` grabbed once at the beginning. - let now = Instant::now(); - - // Make sure we are connected or connecting to all the reserved nodes. - for reserved in self.reserved_peers.iter() { - // TODO: don't generate an event if we're already in a pending connection (https://github.com/libp2p/rust-libp2p/issues/697) - if !self.enabled_peers.contains(&reserved) { - self.events.push(NetworkBehaviourAction::DialPeer { peer_id: reserved.clone() }); + /// Function that is called when the peerset wants us to connect to a node. + fn peerset_report_connect(&mut self, peer_id: PeerId) { + let mut occ_entry = match self.peers.entry(peer_id) { + Entry::Occupied(entry) => entry, + Entry::Vacant(entry) => { + // If there's no entry in `self.peers`, start dialing. + debug!(target: "sub-libp2p", "PSM => Connect({:?}): Starting to connect", entry.key()); + debug!(target: "sub-libp2p", "Libp2p <= Dial {:?}", entry.key()); + self.events.push(NetworkBehaviourAction::DialPeer { peer_id: entry.key().clone() }); + entry.insert(PeerState::Requested); + return; } - } + }; - // We're done with reserved node; return early if there's nothing more to do. - if self.reserved_only { - // We set a timeout to 60 seconds for trying to connect again, however in practice - // a round will happen as soon as we fail to dial, disconnect from a node, allow - // unreserved nodes, and so on. - self.next_connect_to_nodes.reset(now + Duration::from_secs(60)); + match mem::replace(occ_entry.get_mut(), PeerState::Poisoned) { + PeerState::Banned { until } => { + debug!(target: "sub-libp2p", "PSM => Connect({:?}): Will start to connect at \ + until {:?}", occ_entry.key(), until); + *occ_entry.into_mut() = PeerState::PendingRequest { + timer: tokio_timer::Delay::new(until), + }; + }, + + PeerState::Disabled { open, connected_point, banned_until: None } => { + debug!(target: "sub-libp2p", "PSM => Connect({:?}): Enabling previously-idle \ + connection through {:?}", occ_entry.key(), connected_point); + debug!(target: "sub-libp2p", "Handler({:?}) <= Enable", occ_entry.key()); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: occ_entry.key().clone(), + event: CustomProtoHandlerIn::Enable(connected_point.clone().into()), + }); + *occ_entry.into_mut() = PeerState::Enabled { connected_point, open }; + }, + + PeerState::Disabled { open, connected_point, banned_until: Some(banned) } => { + debug!(target: "sub-libp2p", "PSM => Connect({:?}): Has idle connection through \ + {:?} but node is banned until {:?}", occ_entry.key(), connected_point, banned); + *occ_entry.into_mut() = PeerState::DisabledPendingEnable { + connected_point, + open, + timer: tokio_timer::Delay::new(banned), + }; + }, + + PeerState::Incoming { connected_point, .. } => { + debug!(target: "sub-libp2p", "PSM => Connect({:?}): Enabling incoming \ + connection through {:?}", occ_entry.key(), connected_point); + if let Some(inc) = self.incoming.iter_mut() + .find(|i| i.peer_id == *occ_entry.key() && i.alive) { + inc.alive = false; + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: no entry in \ + incoming for incoming peer") + } + debug!(target: "sub-libp2p", "Handler({:?}) <= Enable", occ_entry.key()); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: occ_entry.key().clone(), + event: CustomProtoHandlerIn::Enable(connected_point.clone().into()), + }); + *occ_entry.into_mut() = PeerState::Enabled { connected_point, open: false }; + }, + + st @ PeerState::Enabled { .. } => { + warn!(target: "sub-libp2p", "PSM => Connect({:?}): Already connected to this \ + peer", occ_entry.key()); + *occ_entry.into_mut() = st; + }, + st @ PeerState::DisabledPendingEnable { .. } => { + warn!(target: "sub-libp2p", "PSM => Connect({:?}): Already have an idle \ + connection to this peer and waiting to enable it", occ_entry.key()); + *occ_entry.into_mut() = st; + }, + st @ PeerState::Requested { .. } | st @ PeerState::PendingRequest { .. } => { + warn!(target: "sub-libp2p", "PSM => Connect({:?}): Received a previous \ + request for that peer", occ_entry.key()); + *occ_entry.into_mut() = st; + }, + + PeerState::Poisoned => + error!(target: "sub-libp2p", "State of {:?} is poisoned", occ_entry.key()), + } + } + + /// Function that is called when the peerset wants us to disconnect from a node. + fn peerset_report_disconnect(&mut self, peer_id: PeerId) { + let mut entry = match self.peers.entry(peer_id) { + Entry::Occupied(entry) => entry, + Entry::Vacant(entry) => { + debug!(target: "sub-libp2p", "PSM => Drop({:?}): Node already disabled", entry.key()); + return + } + }; + + match mem::replace(entry.get_mut(), PeerState::Poisoned) { + st @ PeerState::Disabled { .. } | st @ PeerState::Banned { .. } => { + debug!(target: "sub-libp2p", "PSM => Drop({:?}): Node already disabled", entry.key()); + *entry.into_mut() = st; + }, + + PeerState::DisabledPendingEnable { open, connected_point, timer } => { + debug!(target: "sub-libp2p", "PSM => Drop({:?}): Interrupting pending \ + enable", entry.key()); + *entry.into_mut() = PeerState::Disabled { + open, + connected_point, + banned_until: Some(timer.deadline()), + }; + }, + + PeerState::Enabled { open, connected_point } => { + debug!(target: "sub-libp2p", "PSM => Drop({:?}): Disabling connection", entry.key()); + debug!(target: "sub-libp2p", "Handler({:?}) <= Disable", entry.key()); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: entry.key().clone(), + event: CustomProtoHandlerIn::Disable, + }); + *entry.into_mut() = PeerState::Disabled { open, connected_point, banned_until: None } + }, + st @ PeerState::Incoming { .. } => { + error!(target: "sub-libp2p", "PSM => Drop({:?}): Was in incoming mode", + entry.key()); + *entry.into_mut() = st; + }, + PeerState::Requested => { + // We don't cancel dialing. Libp2p doesn't expose that on purpose, as other + // sub-systems (such as the discovery mechanism) may require dialing this node as + // well at the same time. + debug!(target: "sub-libp2p", "PSM => Drop({:?}): Was not yet connected", entry.key()); + entry.remove(); + }, + PeerState::PendingRequest { timer } => { + debug!(target: "sub-libp2p", "PSM => Drop({:?}): Was not yet connected", entry.key()); + *entry.into_mut() = PeerState::Banned { until: timer.deadline() } + }, + + PeerState::Poisoned => + error!(target: "sub-libp2p", "State of {:?} is poisoned", entry.key()), + } + } + + /// Function that is called when the peerset wants us to accept an incoming node. + fn peerset_report_accept(&mut self, index: substrate_peerset::IncomingIndex) { + let incoming = if let Some(pos) = self.incoming.iter().position(|i| i.incoming_id == index) { + self.incoming.remove(pos) + } else { + error!(target: "sub-libp2p", "PSM => Accept({:?}): Invalid index", index); + return + }; + + if !incoming.alive { + debug!(target: "sub-libp2p", "PSM => Accept({:?}, {:?}): Obsolete incoming, + sending back dropped", index, incoming.peer_id); + debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", incoming.peer_id); + self.peerset.dropped(&incoming.peer_id); return } - // Counter of number of connections to open, decreased when we open one. - let mut num_to_open = { - let num_outgoing_connections = self.enabled_peers - .iter() - .filter(|p| self.connected_peers.get(p).map(|c| c.is_dialer()).unwrap_or(false)) - .filter(|p| !self.reserved_peers.contains(p)) - .count(); - self.max_outgoing_connections - num_outgoing_connections + let state = if let Some(state) = self.peers.get_mut(&incoming.peer_id) { + state + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: no entry in peers \ + corresponding to an alive incoming"); + return }; - trace!(target: "sub-libp2p", "Connect-to-nodes round; attempting to fill {:?} slots", - num_to_open); - // We first try to enable existing connections. - for peer_id in self.connected_peers.keys() { - if num_to_open == 0 { - break - } + let connected_point = if let PeerState::Incoming { connected_point } = state { + connected_point.clone() + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: entry in peers corresponding \ + to an alive incoming is not in incoming state"); + return + }; - if self.enabled_peers.contains(peer_id) { - continue; - } + debug!(target: "sub-libp2p", "PSM => Accept({:?}, {:?}): Enabling connection \ + through {:?}", index, incoming.peer_id, connected_point); + debug!(target: "sub-libp2p", "Handler({:?}) <= Enable", incoming.peer_id); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: incoming.peer_id, + event: CustomProtoHandlerIn::Enable(connected_point.clone().into()), + }); - if let Some((_, expire)) = self.banned_peers.iter().find(|(p, _)| p == peer_id) { - if *expire >= now { - continue; - } - } + *state = PeerState::Enabled { open: false, connected_point }; + } - trace!(target: "sub-libp2p", "Enabling custom protocols with {:?} (active)", peer_id); - num_to_open -= 1; - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Enable(Endpoint::Dialer), - }); + /// Function that is called when the peerset wants us to reject an incoming node. + fn peerset_report_reject(&mut self, index: substrate_peerset::IncomingIndex) { + let incoming = if let Some(pos) = self.incoming.iter().position(|i| i.incoming_id == index) { + self.incoming.remove(pos) + } else { + error!(target: "sub-libp2p", "PSM => Reject({:?}): Invalid index", index); + return + }; + + if !incoming.alive { + error!(target: "sub-libp2p", "PSM => Reject({:?}, {:?}): Obsolete incoming, \ + ignoring", index, incoming.peer_id); + return } - // Then, try to open new connections. - let local_peer_id = params.local_peer_id().clone(); - let (to_try, will_change) = self.topology.addrs_to_attempt(); - for (peer_id, _) in to_try { - if num_to_open == 0 { - break - } + let state = if let Some(state) = self.peers.get_mut(&incoming.peer_id) { + state + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: no entry in peers \ + corresponding to an alive incoming"); + return + }; - if peer_id == &local_peer_id { - continue - } + let connected_point = if let PeerState::Incoming { connected_point } = state { + connected_point.clone() + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: entry in peers corresponding \ + to an alive incoming is not in incoming state"); + return + }; - if self.connected_peers.contains_key(&peer_id) { - continue - } - - if let Some((_, ban_end)) = self.banned_peers.iter().find(|(p, _)| p == peer_id) { - if *ban_end > now { - continue - } - } - - num_to_open -= 1; - self.events.push(NetworkBehaviourAction::DialPeer { peer_id: peer_id.clone() }); - } - - // Next round is when we expect the topology will change. - self.next_connect_to_nodes.reset(cmp::min(will_change, now + Duration::from_secs(60))); + debug!(target: "sub-libp2p", "PSM => Reject({:?}, {:?}): Rejecting connection through \ + {:?}", index, incoming.peer_id, connected_point); + debug!(target: "sub-libp2p", "Handler({:?}) <= Disable", incoming.peer_id); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: incoming.peer_id, + event: CustomProtoHandlerIn::Disable, + }); + *state = PeerState::Disabled { open: false, connected_point, banned_until: None }; } } @@ -440,135 +555,199 @@ where CustomProtoHandlerProto::new(self.protocol.clone()) } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.topology.addresses_of_peer(peer_id, false).map(|(a, _)| a.clone()).collect() + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + Vec::new() } - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { - // When a peer connects, its handler is initially in the disabled state. We make sure that - // the peer is allowed, and if so we put it in the enabled state. - - self.connected_peers.insert(peer_id.clone(), endpoint.clone()); - - let is_reserved = self.reserved_peers.contains(&peer_id); - if self.reserved_only && !is_reserved { - debug!(target: "sub-libp2p", "Ignoring {:?} because we're in reserved mode", peer_id); - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Disable, - }); - return - } - - // Check whether peer is banned. - if !is_reserved { - if let Some((_, expire)) = self.banned_peers.iter().find(|(p, _)| p == &peer_id) { - if *expire >= Instant::now() { - debug!(target: "sub-libp2p", "Ignoring banned peer {:?}", peer_id); - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Disable, - }); - return + fn inject_connected(&mut self, peer_id: PeerId, connected_point: ConnectedPoint) { + match (self.peers.entry(peer_id), connected_point) { + (Entry::Occupied(mut entry), connected_point) => { + match mem::replace(entry.get_mut(), PeerState::Poisoned) { + PeerState::Requested => { + debug!(target: "sub-libp2p", "Libp2p => Connected({:?}): Connection \ + requested by PSM (through {:?})", entry.key(), connected_point); + debug!(target: "sub-libp2p", "Handler({:?}) <= Enable", entry.key()); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: entry.key().clone(), + event: CustomProtoHandlerIn::Enable(connected_point.clone().into()), + }); + *entry.into_mut() = PeerState::Enabled { open: false, connected_point }; + } + PeerState::PendingRequest { timer } => { + debug!(target: "sub-libp2p", "Libp2p => Connected({:?}): Connection \ + requested by PSM (through {:?}) but node is banned", entry.key(), + connected_point); + debug!(target: "sub-libp2p", "Handler({:?}) <= Disable", entry.key()); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: entry.key().clone(), + event: CustomProtoHandlerIn::Disable, + }); + *entry.into_mut() = PeerState::DisabledPendingEnable { + open: false, + connected_point, + timer, + }; + } + st @ _ => { + // This is a serious bug either in this state machine or in libp2p. + error!(target: "sub-libp2p", "Received inject_connected for \ + already-connected node"); + *entry.into_mut() = st; + return + } } } - } - // Check the limits on the ingoing and outgoing connections. - match endpoint { - ConnectedPoint::Dialer { .. } => { - let num_outgoing = self.enabled_peers.iter() - .filter(|p| self.connected_peers.get(p).map(|c| c.is_dialer()).unwrap_or(false)) - .filter(|p| !self.reserved_peers.contains(p)) - .count(); - - debug_assert!(num_outgoing <= self.max_outgoing_connections); - if num_outgoing == self.max_outgoing_connections { - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Disable, - }); - return - } + (Entry::Vacant(entry), connected_point @ ConnectedPoint::Listener { .. }) => { + let incoming_id = self.next_incoming_index.clone(); + self.next_incoming_index.0 = match self.next_incoming_index.0.checked_add(1) { + Some(v) => v, + None => { + error!(target: "sub-libp2p", "Overflow in next_incoming_index"); + return + } + }; + debug!(target: "sub-libp2p", "Libp2p => Connected({:?}): Incoming connection", + entry.key()); + debug!(target: "sub-libp2p", "PSM <= Incoming({:?}, {:?}): Through {:?}", + incoming_id, entry.key(), connected_point); + self.peerset.incoming(entry.key().clone(), incoming_id); + self.incoming.push(IncomingPeer { + peer_id: entry.key().clone(), + alive: true, + incoming_id, + }); + entry.insert(PeerState::Incoming { connected_point }); } - ConnectedPoint::Listener { .. } => { - let num_ingoing = self.enabled_peers.iter() - .filter(|p| self.connected_peers.get(p).map(|c| c.is_listener()).unwrap_or(false)) - .filter(|p| !self.reserved_peers.contains(p)) - .count(); - debug_assert!(num_ingoing <= self.max_incoming_connections); - if num_ingoing == self.max_incoming_connections { - debug!(target: "sub-libp2p", "Ignoring incoming connection from {:?} because \ - we're full", peer_id); - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Disable, - }); - return - } + (Entry::Vacant(entry), connected_point) => { + debug!(target: "sub-libp2p", "Libp2p => Connected({:?}): Requested by something \ + else than PSM, disabling", entry.key()); + debug!(target: "sub-libp2p", "Handler({:?}) <= Disable", entry.key()); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: entry.key().clone(), + event: CustomProtoHandlerIn::Disable, + }); + entry.insert(PeerState::Disabled { open: false, connected_point, banned_until: None }); } } - - // If everything is fine, enable the node. - debug_assert!(!self.enabled_peers.contains(&peer_id)); - // We ask the handler to actively open substreams only if we are the dialer; otherwise - // the two nodes will race to be the first to open the unique allowed substream. - if endpoint.is_dialer() { - trace!(target: "sub-libp2p", "Enabling custom protocols with {:?} (active)", peer_id); - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Enable(Endpoint::Dialer), - }); - } else { - trace!(target: "sub-libp2p", "Enabling custom protocols with {:?} (passive)", peer_id); - self.events.push(NetworkBehaviourAction::SendEvent { - peer_id: peer_id.clone(), - event: CustomProtoHandlerIn::Enable(Endpoint::Listener), - }); - } - - self.topology.set_connected(&peer_id, &endpoint); - self.enabled_peers.insert(peer_id); } fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { - let was_connected = self.connected_peers.remove(&peer_id); - debug_assert!(was_connected.is_some()); + match self.peers.remove(peer_id) { + None | Some(PeerState::Requested) | Some(PeerState::PendingRequest { .. }) | + Some(PeerState::Banned { .. }) => + // This is a serious bug either in this state machine or in libp2p. + error!(target: "sub-libp2p", "Received inject_disconnected for non-connected \ + node {:?}", peer_id), - self.topology.set_disconnected(peer_id, &endpoint); + Some(PeerState::Disabled { open, banned_until, .. }) => { + debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}): Was disabled \ + (through {:?})", peer_id, endpoint); + if let Some(until) = banned_until { + self.peers.insert(peer_id.clone(), PeerState::Banned { until }); + } + if open { + debug!(target: "sub-libp2p", "External API <= Closed({:?})", peer_id); + let event = CustomProtoOut::CustomProtocolClosed { + peer_id: peer_id.clone(), + result: Ok(()), + }; - if self.opened_peers.remove(&peer_id) { - let event = CustomProtoOut::CustomProtocolClosed { - peer_id: peer_id.clone(), - result: Ok(()), - }; + self.events.push(NetworkBehaviourAction::GenerateEvent(event)); + } + } - self.events.push(NetworkBehaviourAction::GenerateEvent(event)); + Some(PeerState::DisabledPendingEnable { open, timer, .. }) => { + debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}): Was disabled \ + (through {:?}) but pending enable", peer_id, endpoint); + debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); + self.peerset.dropped(peer_id); + self.peers.insert(peer_id.clone(), PeerState::Banned { until: timer.deadline() }); + if open { + debug!(target: "sub-libp2p", "External API <= Closed({:?})", peer_id); + let event = CustomProtoOut::CustomProtocolClosed { + peer_id: peer_id.clone(), + result: Ok(()), + }; + + self.events.push(NetworkBehaviourAction::GenerateEvent(event)); + } + } + + Some(PeerState::Enabled { open, .. }) => { + debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}): Was enabled \ + (through {:?})", peer_id, endpoint); + debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); + self.peerset.dropped(peer_id); + + if open { + debug!(target: "sub-libp2p", "External API <= Closed({:?})", peer_id); + let event = CustomProtoOut::CustomProtocolClosed { + peer_id: peer_id.clone(), + result: Ok(()), + }; + + self.events.push(NetworkBehaviourAction::GenerateEvent(event)); + } + } + + // In the incoming state, we don't report "Dropped". Instead we will just ignore the + // corresponding Accept/Reject. + Some(PeerState::Incoming { .. }) => { + if let Some(state) = self.incoming.iter_mut().find(|i| i.peer_id == *peer_id) { + debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}): Was in incoming \ + mode (id {:?}, through {:?})", peer_id, state.incoming_id, endpoint); + state.alive = false; + } else { + error!(target: "sub-libp2p", "State mismatch in libp2p: no entry in incoming \ + corresponding to an incoming state in peers") + } + } + + Some(PeerState::Poisoned) => + error!(target: "sub-libp2p", "State of {:?} is poisoned", peer_id), } - - // Trigger a `connect_to_nodes` round. - self.next_connect_to_nodes = Delay::new(Instant::now()); - - self.enabled_peers.remove(peer_id); } - fn inject_dial_failure(&mut self, peer_id: Option<&PeerId>, addr: &Multiaddr, error: &dyn error::Error) { - if let Some(peer_id) = peer_id.as_ref() { - debug!(target: "sub-libp2p", "Failed to reach peer {:?} through {} => {:?}", peer_id, addr, error); - self.topology.set_unreachable(addr); + fn inject_addr_reach_failure(&mut self, peer_id: Option<&PeerId>, addr: &Multiaddr, error: &dyn error::Error) { + trace!(target: "sub-libp2p", "Libp2p => Reach failure for {:?} through {:?}: {:?}", peer_id, addr, error); + } - // Trigger a `connect_to_nodes` round. - self.next_connect_to_nodes = Delay::new(Instant::now()); + fn inject_dial_failure(&mut self, peer_id: &PeerId) { + if let Entry::Occupied(mut entry) = self.peers.entry(peer_id.clone()) { + match mem::replace(entry.get_mut(), PeerState::Poisoned) { + // The node is not in our list. + st @ PeerState::Banned { .. } => { + trace!(target: "sub-libp2p", "Libp2p => Dial failure for {:?}", peer_id); + *entry.into_mut() = st; + }, + + // "Basic" situation: we failed to reach a node that the peerset requested. + PeerState::Requested | PeerState::PendingRequest { .. } => { + debug!(target: "sub-libp2p", "Libp2p => Dial failure for {:?}", peer_id); + *entry.into_mut() = PeerState::Banned { + until: Instant::now() + Duration::from_secs(5) + }; + debug!(target: "sub-libp2p", "PSM <= Dropped({:?})", peer_id); + self.peerset.dropped(peer_id) + }, + + // We can still get dial failures even if we are already connected to the node, + // as an extra diagnostic for an earlier attempt. + st @ PeerState::Disabled { .. } | st @ PeerState::Enabled { .. } | + st @ PeerState::DisabledPendingEnable { .. } | st @ PeerState::Incoming { .. } => { + debug!(target: "sub-libp2p", "Libp2p => Dial failure for {:?}", peer_id); + *entry.into_mut() = st; + }, + + PeerState::Poisoned => + error!(target: "sub-libp2p", "State of {:?} is poisoned", peer_id), + } } else { - // This code path is only reached if `peer_id` is None, which means that we dialed an - // address without knowing the `PeerId` to expect. We don't currently do that, except - // in one situation: for convenience, we accept bootstrap node addresses in the format - // `IP:PORT`. - // There is no reason this trigger a `connect_to_nodes` round in that situation. - debug!(target: "sub-libp2p", "Failed to reach {} => {:?}", addr, error); - self.topology.set_unreachable(addr); + // The node is not in our list. + trace!(target: "sub-libp2p", "Libp2p => Dial failure for {:?}", peer_id); } } @@ -579,8 +758,18 @@ where ) { match event { CustomProtoHandlerOut::CustomProtocolClosed { result } => { - self.opened_peers.remove(&source); + debug!(target: "sub-libp2p", "Handler({:?}) => Closed({:?})", source, result); + match self.peers.get_mut(&source) { + Some(PeerState::Enabled { ref mut open, .. }) if *open => + *open = false, + Some(PeerState::Disabled { ref mut open, .. }) if *open => + *open = false, + Some(PeerState::DisabledPendingEnable { ref mut open, .. }) if *open => + *open = false, + _ => error!(target: "sub-libp2p", "State mismatch in the custom protos handler"), + } + debug!(target: "sub-libp2p", "External API <= Closed({:?})", source); let event = CustomProtoOut::CustomProtocolClosed { result, peer_id: source, @@ -588,12 +777,23 @@ where self.events.push(NetworkBehaviourAction::GenerateEvent(event)); } - CustomProtoHandlerOut::CustomProtocolOpen { version } => { - debug_assert!(!self.is_open(&source)); - self.opened_peers.insert(source.clone()); - let endpoint = self.connected_peers.get(&source) - .expect("We only receive events from connected nodes; QED").clone(); + CustomProtoHandlerOut::CustomProtocolOpen { version } => { + debug!(target: "sub-libp2p", "Handler({:?}) => Open: version {:?}", source, version); + let endpoint = match self.peers.get_mut(&source) { + Some(PeerState::Enabled { ref mut open, ref connected_point }) | + Some(PeerState::DisabledPendingEnable { ref mut open, ref connected_point, .. }) | + Some(PeerState::Disabled { ref mut open, ref connected_point, .. }) if !*open => { + *open = true; + connected_point.clone() + } + _ => { + error!(target: "sub-libp2p", "State mismatch in the custom protos handler"); + return + } + }; + + debug!(target: "sub-libp2p", "External API <= Open({:?})", source); let event = CustomProtoOut::CustomProtocolOpen { version, peer_id: source, @@ -602,8 +802,11 @@ where self.events.push(NetworkBehaviourAction::GenerateEvent(event)); } + CustomProtoHandlerOut::CustomMessage { message } => { debug_assert!(self.is_open(&source)); + trace!(target: "sub-libp2p", "Handler({:?}) => Message", source); + trace!(target: "sub-libp2p", "External API <= Message({:?})", source); let event = CustomProtoOut::CustomMessage { peer_id: source, message, @@ -611,8 +814,11 @@ where self.events.push(NetworkBehaviourAction::GenerateEvent(event)); } + CustomProtoHandlerOut::Clogged { messages } => { debug_assert!(self.is_open(&source)); + trace!(target: "sub-libp2p", "Handler({:?}) => Clogged", source); + trace!(target: "sub-libp2p", "External API <= Clogged({:?})", source); warn!(target: "sub-libp2p", "Queue of packets to send to {:?} is \ pretty large", source); self.events.push(NetworkBehaviourAction::GenerateEvent(CustomProtoOut::Clogged { @@ -620,43 +826,88 @@ where messages, })); } - CustomProtoHandlerOut::ProtocolError { error, is_severe } => { - if is_severe { - warn!(target: "sub-libp2p", "Network misbehaviour from {:?}: {:?}", - source, error); - self.ban_peer(source); - } else { - debug!(target: "sub-libp2p", "Network misbehaviour from {:?}: {:?}", - source, error); - self.disconnect_peer(&source); - } + + // Don't do anything for non-severe errors except report them. + CustomProtoHandlerOut::ProtocolError { is_severe, ref error } if !is_severe => { + debug!(target: "sub-libp2p", "Handler({:?}) => Benign protocol error: {:?}", + source, error) + } + + CustomProtoHandlerOut::ProtocolError { error, .. } => { + debug!(target: "sub-libp2p", "Handler({:?}) => Severe protocol error: {:?}", + source, error); + self.disconnect_peer_inner(&source, Some(Duration::from_secs(5))); } } } fn poll( &mut self, - params: &mut PollParameters, + _params: &mut PollParameters, ) -> Async< NetworkBehaviourAction< CustomProtoHandlerIn, Self::OutEvent, >, > { + // Poll for instructions from the peerset. + // Note that the peerset is a *best effort* crate, and we have to use defensive programming. loop { - match self.next_connect_to_nodes.poll() { - Ok(Async::Ready(())) => self.connect_to_nodes(params), + match self.peerset.poll() { + Ok(Async::Ready(Some(substrate_peerset::Message::Accept(index)))) => { + self.peerset_report_accept(index); + } + Ok(Async::Ready(Some(substrate_peerset::Message::Reject(index)))) => { + self.peerset_report_reject(index); + } + Ok(Async::Ready(Some(substrate_peerset::Message::Connect(id)))) => { + self.peerset_report_connect(id); + } + Ok(Async::Ready(Some(substrate_peerset::Message::Drop(id)))) => { + self.peerset_report_disconnect(id); + } + Ok(Async::Ready(None)) => { + error!(target: "sub-libp2p", "Peerset receiver stream has returned None"); + break; + } Ok(Async::NotReady) => break, Err(err) => { - warn!(target: "sub-libp2p", "Connect-to-nodes timer errored: {:?}", err); + error!(target: "sub-libp2p", "Peerset receiver stream has errored: {:?}", err); break } } } - // Clean up `banned_peers` - self.banned_peers.retain(|(_, end)| *end > Instant::now()); - self.banned_peers.shrink_to_fit(); + for (peer_id, peer_state) in self.peers.iter_mut() { + match mem::replace(peer_state, PeerState::Poisoned) { + PeerState::PendingRequest { mut timer } => { + if let Ok(Async::NotReady) = timer.poll() { + *peer_state = PeerState::PendingRequest { timer }; + continue; + } + + debug!(target: "sub-libp2p", "Libp2p <= Dial {:?} now that ban has expired", peer_id); + self.events.push(NetworkBehaviourAction::DialPeer { peer_id: peer_id.clone() }); + *peer_state = PeerState::Requested; + } + + PeerState::DisabledPendingEnable { mut timer, connected_point, open } => { + if let Ok(Async::NotReady) = timer.poll() { + *peer_state = PeerState::DisabledPendingEnable { timer, connected_point, open }; + continue; + } + + debug!(target: "sub-libp2p", "Handler({:?}) <= Enable now that ban has expired", peer_id); + self.events.push(NetworkBehaviourAction::SendEvent { + peer_id: peer_id.clone(), + event: CustomProtoHandlerIn::Enable(connected_point.clone().into()), + }); + *peer_state = PeerState::Enabled { connected_point, open }; + } + + st @ _ => *peer_state = st, + } + } if !self.events.is_empty() { return Async::Ready(self.events.remove(0)) diff --git a/substrate/core/network-libp2p/src/custom_proto/handler.rs b/substrate/core/network-libp2p/src/custom_proto/handler.rs index c2c2cf07b7..6f083b8dbd 100644 --- a/substrate/core/network-libp2p/src/custom_proto/handler.rs +++ b/substrate/core/network-libp2p/src/custom_proto/handler.rs @@ -799,10 +799,6 @@ where TSubstream: AsyncRead + AsyncWrite, TMessage: CustomMessage { is_severe, error: Box::new(err), })); - - // If we failed to open a substream, there is little chance that we manage to open any - // other substream ever again on this connection, and thus we disable the handler. - self.disable(); } fn connection_keep_alive(&self) -> KeepAlive { diff --git a/substrate/core/network-libp2p/src/custom_proto/mod.rs b/substrate/core/network-libp2p/src/custom_proto/mod.rs index 279f9a2334..cf2bf57153 100644 --- a/substrate/core/network-libp2p/src/custom_proto/mod.rs +++ b/substrate/core/network-libp2p/src/custom_proto/mod.rs @@ -19,5 +19,4 @@ pub use self::upgrade::{CustomMessage, CustomMessageId, RegisteredProtocol}; mod behaviour; mod handler; -mod topology; mod upgrade; diff --git a/substrate/core/network-libp2p/src/custom_proto/topology.rs b/substrate/core/network-libp2p/src/custom_proto/topology.rs deleted file mode 100644 index 4e498c560c..0000000000 --- a/substrate/core/network-libp2p/src/custom_proto/topology.rs +++ /dev/null @@ -1,725 +0,0 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate 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 Substrate. If not, see .? - -use fnv::FnvHashMap; -use libp2p::{core::swarm::ConnectedPoint, Multiaddr, PeerId}; -use log::{debug, info, trace, warn}; -use serde_derive::{Serialize, Deserialize}; -use std::{cmp, fs}; -use std::io::{Read, Cursor, Error as IoError, ErrorKind as IoErrorKind, Write, BufReader, BufWriter}; -use std::path::{Path, PathBuf}; -use std::time::{Duration, Instant, SystemTime}; - -/// For each address we're connected to, a period of this duration increases the score by 1. -const CONNEC_DURATION_PER_SCORE: Duration = Duration::from_secs(10); -/// Maximum number of addresses for a given peer. If there are more than this number of addresses, -/// the ones with a lower score will be removed. -const MAX_ADDRESSES_PER_PEER: usize = 10; -/// Maximum value for the score. -const MAX_SCORE: u32 = 100; -/// When we successfully connect to a node, raises its score to the given minimum value. -const CONNECTED_MINIMUM_SCORE: u32 = 20; -/// Initial score that a node discovered through Kademlia receives, where we have a hint that the -/// node is reachable. -const DISCOVERY_INITIAL_SCORE_CONNECTABLE: u32 = 15; -/// Initial score that a node discovered through Kademlia receives, without any hint. -const DISCOVERY_INITIAL_SCORE: u32 = 10; -/// Score adjustement when we fail to connect to an address. -const SCORE_DIFF_ON_FAILED_TO_CONNECT: i32 = -1; -/// Default time-to-live for addresses discovered through Kademlia. -/// After this time has elapsed and no connection has succeeded, the address will be removed. -const KADEMLIA_DISCOVERY_EXPIRATION: Duration = Duration::from_secs(2 * 3600); -/// After a successful connection, the TTL is set to a minimum at this amount. -const EXPIRATION_PUSH_BACK_CONNEC: Duration = Duration::from_secs(2 * 3600); -/// Initial score that a bootstrap node receives when registered. -const BOOTSTRAP_NODE_SCORE: u32 = 100; -/// Time to live of a boostrap node. This only applies if you start the node later *without* -/// that bootstrap node configured anymore. -const BOOTSTRAP_NODE_EXPIRATION: Duration = Duration::from_secs(24 * 3600); -/// The first time we fail to connect to an address, wait this duration before trying again. -const FIRST_CONNECT_FAIL_BACKOFF: Duration = Duration::from_secs(2); -/// Every time we fail to connect to an address, multiply the backoff by this constant. -const FAIL_BACKOFF_MULTIPLIER: u32 = 2; -/// We need a maximum value for the backoff, overwise we risk an overflow. -const MAX_BACKOFF: Duration = Duration::from_secs(30 * 60); - -/// Stores information about the topology of the network. -#[derive(Debug)] -pub struct NetTopology { - /// The actual storage. Never contains a key for `local_peer_id`. - store: FnvHashMap, - /// Optional path to the file that caches the serialized version of `store`. - cache_path: Option, - /// PeerId of the local node. - local_peer_id: PeerId, -} - -impl NetTopology { - /// Initializes a new `NetTopology` that isn't tied to any file. - /// - /// `flush_to_disk()` will be a no-op. - #[inline] - pub fn memory(local_peer_id: PeerId) -> NetTopology { - NetTopology { - store: Default::default(), - cache_path: None, - local_peer_id, - } - } - - /// Builds a `NetTopology` that will use `path` as a cache. - /// - /// This function tries to load a known topology from the file. If the file doesn't exist - /// or contains garbage data, the execution still continues. - /// - /// Calling `flush_to_disk()` in the future writes to the given path. - pub fn from_file>(local_peer_id: PeerId, path: P) -> NetTopology { - let path = path.as_ref(); - debug!(target: "sub-libp2p", "Initializing peer store for JSON file {:?}", path); - let store = try_load(path, &local_peer_id); - NetTopology { - store, - cache_path: Some(path.to_owned()), - local_peer_id, - } - } - - /// Writes the topology into the path passed to `from_file`. - /// - /// No-op if the object was created with `memory()`. - pub fn flush_to_disk(&mut self) -> Result<(), IoError> { - let path = match self.cache_path { - Some(ref p) => p, - None => return Ok(()) - }; - - let file = fs::File::create(path)?; - // TODO: the capacity of the BufWriter is kind of arbitrary ; decide better - serialize(BufWriter::with_capacity(1024 * 1024, file), &mut self.store) - } - - /// Returns the number of peers in the topology, excluding the local peer. - #[inline] - pub fn num_peers(&self) -> usize { - self.store.len() - } - - /// Perform a cleanup pass, removing all obsolete addresses and peers. - /// - /// This should be done from time to time. - pub fn cleanup(&mut self) { - let now_systime = SystemTime::now(); - self.store.retain(|_, peer| { - let new_addrs = peer.addrs - .drain(..) - .filter(|a| a.expires > now_systime || a.is_connected()) - .collect(); - peer.addrs = new_addrs; - !peer.addrs.is_empty() - }); - } - - /// Returns a list of all the known addresses of peers, ordered by the - /// order in which we should attempt to connect to them. - /// - /// Because of expiration and back-off mechanisms, this list can grow - /// by itself over time. The `Instant` that is returned corresponds to - /// the earlier known time when a new entry will be added automatically to - /// the list. - pub fn addrs_to_attempt(&mut self) -> (impl Iterator, Instant) { - // TODO: optimize - let now = Instant::now(); - let now_systime = SystemTime::now(); - let mut instant = now + Duration::from_secs(3600); - let mut addrs_out = Vec::new(); - - let mut peer_addrs = Vec::new(); - - 'peer_loop: for (peer, info) in &mut self.store { - peer_addrs.clear(); - - for addr in &mut info.addrs { - let (score, is_connected) = addr.score_and_is_connected(); - if is_connected { - continue 'peer_loop - } - if score == 0 || addr.expires < now_systime { - continue - } - if addr.back_off_until > now { - instant = cmp::min(instant, addr.back_off_until); - continue - } - - peer_addrs.push(((peer, &addr.addr), score)); - } - - for val in peer_addrs.drain(..) { - addrs_out.push(val); - } - } - - addrs_out.sort_by(|a, b| b.1.cmp(&a.1)); - (addrs_out.into_iter().map(|a| a.0), instant) - } - - /// Adds an address corresponding to a boostrap node. - /// - /// We assume that the address is valid, so its score starts very high. - pub fn add_bootstrap_addr(&mut self, peer: &PeerId, addr: Multiaddr) { - if *peer == self.local_peer_id { - return - } - - let now_systime = SystemTime::now(); - let now = Instant::now(); - - let peer = peer_access(&mut self.store, peer); - - let mut found = false; - let new_addrs = peer.addrs - .drain(..) - .filter_map(|a| { - if a.expires < now_systime && !a.is_connected() { - return None - } - if a.addr == addr { - found = true; - } - Some(a) - }) - .collect(); - peer.addrs = new_addrs; - - if !found { - peer.addrs.push(Addr { - addr, - expires: now_systime + BOOTSTRAP_NODE_EXPIRATION, - back_off_until: now, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - score: AddrScore { - connected_since: None, - score: BOOTSTRAP_NODE_SCORE, - latest_score_update: now, - }, - }); - } - } - - /// Indicates the topology that we have discovered new addresses for a given node. - /// - /// Returns `true` if the topology has changed in some way. Returns `false` if calling this - /// method was a no-op. - pub fn add_discovered_addrs( - &mut self, - peer_id: &PeerId, - addrs: I, - ) -> bool - where I: Iterator { - if *peer_id == self.local_peer_id { - return false - } - - let mut addrs: Vec<_> = addrs.collect(); - - if addrs.len() > 40 { - warn!(target: "sub-libp2p", "Attempt to add more than 40 addresses for {:?}", peer_id); - addrs.truncate(40); - } - - let now_systime = SystemTime::now(); - let now = Instant::now(); - - let peer = peer_access(&mut self.store, peer_id); - - let new_addrs = peer.addrs - .drain(..) - .filter(|a| { - if a.expires < now_systime && !a.is_connected() { - return false - } - addrs.retain(|(addr, _)| *addr != a.addr); - true - }) - .collect(); - peer.addrs = new_addrs; - - let mut anything_changed = false; - - if !addrs.is_empty() { - anything_changed = true; - trace!( - target: "sub-libp2p", - "Peer store: adding addresses {:?} for {:?}", - addrs, - peer_id, - ); - } - - 'addrs_inserter: for (addr, connectable) in addrs { - let initial_score = if connectable { - DISCOVERY_INITIAL_SCORE_CONNECTABLE - } else { - DISCOVERY_INITIAL_SCORE - }; - - // Enforce `MAX_ADDRESSES_PER_PEER` before inserting, or skip this entry. - while peer.addrs.len() >= MAX_ADDRESSES_PER_PEER { - let pos = peer.addrs.iter_mut().position(|addr| addr.score() <= initial_score); - if let Some(pos) = pos { - let _ = peer.addrs.remove(pos); - } else { - continue 'addrs_inserter; - } - } - - // `addrs` can contain duplicates, therefore we would insert the same address twice. - if peer.addrs.iter().any(|a| a.addr == addr) { - continue; - } - - peer.addrs.push(Addr { - addr, - expires: now_systime + KADEMLIA_DISCOVERY_EXPIRATION, - back_off_until: now, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - score: AddrScore { - connected_since: None, - score: initial_score, - latest_score_update: now, - }, - }); - } - - anything_changed - } - - /// Returns the list of peers that are stored in the topology. - #[inline] - pub fn known_peers(&self) -> impl Iterator { - self.store.keys() - } - - /// Returns the addresses stored for a specific peer, and their reputation score. - /// - /// If `include_expired` is true, includes expired addresses that shouldn't be taken into - /// account when dialing. - #[inline] - pub fn addresses_of_peer(&mut self, peer: &PeerId, include_expired: bool) - -> impl Iterator { - let now_st = SystemTime::now(); - let now_is = Instant::now(); - - let mut list = self.store.get_mut(peer).into_iter().flat_map(|p| p.addrs.iter_mut()).filter_map(move |addr| { - let (score, connected) = addr.score_and_is_connected(); - if include_expired || (addr.expires >= now_st && score > 0 && addr.back_off_until < now_is) || connected { - Some((score, &addr.addr)) - } else { - None - } - }).collect::>(); - list.sort_by(|a, b| a.0.cmp(&b.0)); - // TODO: meh, optimize - list.into_iter().map(|(score, addr)| (addr, score)) - } - - /// Marks the given peer as connected through the given endpoint. - pub fn set_connected(&mut self, peer: &PeerId, endpoint: &ConnectedPoint) { - if *peer == self.local_peer_id { - return - } - - let addr = match endpoint { - ConnectedPoint::Dialer { address } => address, - ConnectedPoint::Listener { .. } => return - }; - - let now = Instant::now(); - - // Just making sure that we have an entry for this peer in `store`, but don't use it. - let _ = peer_access(&mut self.store, peer); - - for (peer_in_store, info_in_store) in self.store.iter_mut() { - if peer == peer_in_store { - if let Some(addr) = info_in_store.addrs.iter_mut().find(|a| &a.addr == addr) { - addr.connected_now(CONNECTED_MINIMUM_SCORE); - addr.back_off_until = now; - addr.next_back_off = FIRST_CONNECT_FAIL_BACKOFF; - continue - } - - info_in_store.addrs.push(Addr { - addr: addr.clone(), - expires: SystemTime::now() + EXPIRATION_PUSH_BACK_CONNEC, - back_off_until: now, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - score: AddrScore { - connected_since: Some(now), - latest_score_update: now, - score: CONNECTED_MINIMUM_SCORE, - }, - }); - - } else { - // Set the score to 0 for any address that matches the one we connected to. - for addr_in_store in &mut info_in_store.addrs { - if &addr_in_store.addr == addr { - addr_in_store.adjust_score(-(MAX_SCORE as i32)); - } - } - } - } - } - - /// Marks the given peer as disconnected. The endpoint is the one we were connected to. - pub fn set_disconnected(&mut self, _: &PeerId, endpoint: &ConnectedPoint) { - let addr = match endpoint { - ConnectedPoint::Dialer { address } => address, - ConnectedPoint::Listener { .. } => return - }; - - // Note that we used to have different score values here in the past, but there really - // isn't much point in doing so in practice. - let score_diff = -3; - - for info in self.store.values_mut() { - for a in info.addrs.iter_mut() { - if &a.addr == addr { - a.disconnected_now(score_diff); - a.back_off_until = Instant::now() + a.next_back_off; - a.next_back_off = cmp::min(a.next_back_off * FAIL_BACKOFF_MULTIPLIER, MAX_BACKOFF); - let expires_push_back = SystemTime::now() + EXPIRATION_PUSH_BACK_CONNEC; - if a.expires < expires_push_back { - a.expires = expires_push_back; - } - return - } - } - } - } - - /// Indicates to the topology that we failed to reach a node when dialing the given address. - pub fn set_unreachable(&mut self, addr: &Multiaddr) { - for info in self.store.values_mut() { - for a in info.addrs.iter_mut() { - if &a.addr != addr { - continue - } - - // It is possible that we are connected to this address, and that the dial failure - // concerns another peer. - if a.is_connected() { - continue - } - - a.adjust_score(SCORE_DIFF_ON_FAILED_TO_CONNECT); - trace!(target: "sub-libp2p", "Back off for {} = {:?}", addr, a.next_back_off); - a.back_off_until = Instant::now() + a.next_back_off; - a.next_back_off = cmp::min(a.next_back_off * FAIL_BACKOFF_MULTIPLIER, MAX_BACKOFF); - } - } - } -} - -fn peer_access<'a>(store: &'a mut FnvHashMap, peer: &PeerId) -> &'a mut PeerInfo { - // TODO: should be optimizable if HashMap gets a better API - store.entry(peer.clone()).or_insert_with(Default::default) -} - -#[derive(Debug, Clone, Default)] -struct PeerInfo { - /// Addresses of that peer. - addrs: Vec, -} - -#[derive(Debug)] -struct Addr { - /// The multiaddress. - addr: Multiaddr, - /// When the address expires. - expires: SystemTime, - next_back_off: Duration, - /// Don't try to connect to this node until `Instant`. - back_off_until: Instant, - score: AddrScore, -} - -impl Clone for Addr { - fn clone(&self) -> Addr { - Addr { - addr: self.addr.clone(), - expires: self.expires, - next_back_off: self.next_back_off, - back_off_until: self.back_off_until, - score: self.score.clone(), - } - } -} - -#[derive(Debug, Clone)] -struct AddrScore { - /// If connected, contains the moment when we connected. `None` if we're not connected. - connected_since: Option, - /// Score of this address. Potentially needs to be updated based on `latest_score_update`. - score: u32, - /// When we last updated the score. - latest_score_update: Instant, -} - -impl Addr { - /// Sets the addr to connected. If the score is lower than the given value, raises it to this - /// value. - fn connected_now(&mut self, raise_to_min: u32) { - let now = Instant::now(); - Addr::flush(&mut self.score, now); - self.score.connected_since = Some(now); - if self.score.score < raise_to_min { - self.score.score = raise_to_min; - } - } - - /// Applies a modification to the score. - fn adjust_score(&mut self, score_diff: i32) { - Addr::flush(&mut self.score, Instant::now()); - if score_diff >= 0 { - self.score.score = cmp::min(MAX_SCORE, self.score.score + score_diff as u32); - } else { - self.score.score = self.score.score.saturating_sub(-score_diff as u32); - } - } - - /// Sets the addr to disconnected and applies a modification to the score. - fn disconnected_now(&mut self, score_diff: i32) { - Addr::flush(&mut self.score, Instant::now()); - self.score.connected_since = None; - if score_diff >= 0 { - self.score.score = cmp::min(MAX_SCORE, self.score.score + score_diff as u32); - } else { - self.score.score = self.score.score.saturating_sub(-score_diff as u32); - } - } - - /// Returns true if we are connected to this addr. - fn is_connected(&self) -> bool { - self.score.connected_since.is_some() - } - - /// Returns the score, and true if we are connected to this addr. - fn score_and_is_connected(&mut self) -> (u32, bool) { - Addr::flush(&mut self.score, Instant::now()); - let is_connected = self.score.connected_since.is_some(); - (self.score.score, is_connected) - } - - /// Updates `score` and `latest_score_update`, and returns the score. - fn score(&mut self) -> u32 { - Addr::flush(&mut self.score, Instant::now()); - self.score.score - } - - fn flush(score: &mut AddrScore, now: Instant) { - if let Some(connected_since) = score.connected_since { - let potential_score: u32 = div_dur_with_dur(now - connected_since, CONNEC_DURATION_PER_SCORE); - // We flush when we connect to an address. - debug_assert!(score.latest_score_update >= connected_since); - let effective_score: u32 = - div_dur_with_dur(score.latest_score_update - connected_since, CONNEC_DURATION_PER_SCORE); - let to_add = potential_score.saturating_sub(effective_score); - score.score = cmp::min(MAX_SCORE, score.score + to_add); - } - - score.latest_score_update = now; - } -} - -/// Divides a `Duration` with a `Duration`. This exists in the stdlib but isn't stable yet. -// TODO: remove this function once stable -fn div_dur_with_dur(a: Duration, b: Duration) -> u32 { - let a_ms = a.as_secs() * 1_000_000 + u64::from(a.subsec_micros()); - let b_ms = b.as_secs() * 1_000_000 + u64::from(b.subsec_micros()); - (a_ms / b_ms) as u32 -} - -/// Serialized version of a `PeerInfo`. Suitable for storage in the cache file. -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SerializedPeerInfo { - addrs: Vec, -} - -/// Serialized version of an `Addr`. Suitable for storage in the cache file. -#[derive(Debug, Clone, Serialize, Deserialize)] -struct SerializedAddr { - addr: String, - expires: SystemTime, - score: u32, -} - -impl<'a> From<&'a mut Addr> for SerializedAddr { - fn from(addr: &'a mut Addr) -> SerializedAddr { - SerializedAddr { - addr: addr.addr.to_string(), - expires: addr.expires, - score: addr.score(), - } - } -} - -/// Attempts to load storage from a file. -/// Ignores any entry equal to `local_peer_id`. -/// Deletes the file and returns an empty map if the file doesn't exist, cannot be opened -/// or is corrupted. -fn try_load(path: impl AsRef, local_peer_id: &PeerId) -> FnvHashMap { - let path = path.as_ref(); - if !path.exists() { - debug!(target: "sub-libp2p", "Peer storage file {:?} doesn't exist", path); - return Default::default() - } - - let mut file = match fs::File::open(path) { - // TODO: the capacity of the BufReader is kind of arbitrary ; decide better - Ok(f) => BufReader::with_capacity(1024 * 1024, f), - Err(err) => { - warn!(target: "sub-libp2p", "Failed to open peer storage file: {:?}", err); - info!(target: "sub-libp2p", "Deleting peer storage file {:?}", path); - let _ = fs::remove_file(path); - return Default::default() - } - }; - - // We want to support empty files (and treat them as an empty recordset). Unfortunately - // `serde_json` will always produce an error if we do this ("unexpected EOF at line 0 - // column 0"). Therefore we start by reading one byte from the file in order to check - // for EOF. - - let mut first_byte = [0]; - let num_read = match file.read(&mut first_byte) { - Ok(f) => f, - Err(err) => { - // TODO: DRY - warn!(target: "sub-libp2p", "Failed to read peer storage file: {:?}", err); - info!(target: "sub-libp2p", "Deleting peer storage file {:?}", path); - let _ = fs::remove_file(path); - return Default::default() - } - }; - - if num_read == 0 { - // File is empty. - debug!(target: "sub-libp2p", "Peer storage file {:?} is empty", path); - Default::default() - - } else { - let data = Cursor::new(first_byte).chain(file); - match serde_json::from_reader::<_, serde_json::Value>(data) { - Ok(serde_json::Value::Null) => Default::default(), - Ok(serde_json::Value::Object(map)) => - deserialize_tolerant(map.into_iter(), local_peer_id), - Ok(_) | Err(_) => { - // The `Ok(_)` case means that the file doesn't contain a map. - let _ = fs::remove_file(path); - Default::default() - }, - } - } -} - -/// Attempts to turn a deserialized version of the storage into the final version. -/// -/// Skips entries that are invalid or equal to `local_peer_id`. -fn deserialize_tolerant( - iter: impl Iterator, - local_peer_id: &PeerId -) -> FnvHashMap { - let now = Instant::now(); - let now_systime = SystemTime::now(); - - let mut out = FnvHashMap::default(); - for (peer, info) in iter { - let peer: PeerId = match peer.parse() { - Ok(p) => p, - Err(_) => continue, - }; - - if &peer == local_peer_id { - continue - } - - let info: SerializedPeerInfo = match serde_json::from_value(info) { - Ok(i) => i, - Err(_) => continue, - }; - - let mut addrs = Vec::with_capacity(info.addrs.len()); - for addr in info.addrs { - let multiaddr = match addr.addr.parse() { - Ok(a) => a, - Err(_) => continue, - }; - - if addr.expires < now_systime { - continue - } - - addrs.push(Addr { - addr: multiaddr, - expires: addr.expires, - next_back_off: FIRST_CONNECT_FAIL_BACKOFF, - back_off_until: now, - score: AddrScore { - connected_since: None, - score: addr.score, - latest_score_update: now, - }, - }); - } - - if addrs.is_empty() { - continue - } - - out.insert(peer, PeerInfo { addrs }); - } - - out -} - -/// Attempts to turn a deserialized version of the storage into the final version. -/// -/// Skips entries that are invalid or expired. -fn serialize(out: W, map: &mut FnvHashMap) -> Result<(), IoError> { - let now = SystemTime::now(); - let array: FnvHashMap<_, _> = map.iter_mut().filter_map(|(peer, info)| { - if info.addrs.is_empty() { - return None - } - - let peer = peer.to_base58(); - let info = SerializedPeerInfo { - addrs: info.addrs.iter_mut() - .filter_map(|a| if a.expires > now || a.is_connected() { - Some(a.into()) - } else { - None - }) - .collect(), - }; - - Some((peer, info)) - }).collect(); - - serde_json::to_writer_pretty(out, &array) - .map_err(|err| IoError::new(IoErrorKind::Other, err)) -} diff --git a/substrate/core/network-libp2p/src/custom_proto/upgrade.rs b/substrate/core/network-libp2p/src/custom_proto/upgrade.rs index 8f54aa18d0..fc9ed5bddb 100644 --- a/substrate/core/network-libp2p/src/custom_proto/upgrade.rs +++ b/substrate/core/network-libp2p/src/custom_proto/upgrade.rs @@ -16,7 +16,7 @@ use crate::ProtocolId; use bytes::Bytes; -use libp2p::core::{Endpoint, UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::ProtocolName}; +use libp2p::core::{Negotiated, Endpoint, UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade::ProtocolName}; use libp2p::tokio_codec::Framed; use log::warn; use std::{collections::VecDeque, io, iter, marker::PhantomData, vec::IntoIter as VecIntoIter}; @@ -92,7 +92,7 @@ pub struct RegisteredProtocolSubstream { /// If true, we should call `poll_complete` on the inner sink. requires_poll_complete: bool, /// The underlying substream. - inner: stream::Fuse>>>, + inner: stream::Fuse, UviBytes>>>, /// Id of the protocol. protocol_id: ProtocolId, /// Version of the protocol that was negotiated. @@ -385,7 +385,7 @@ where TSubstream: AsyncRead + AsyncWrite, fn upgrade_inbound( self, - socket: TSubstream, + socket: Negotiated, info: Self::Info, ) -> Self::Future { let framed = { @@ -418,7 +418,7 @@ where TSubstream: AsyncRead + AsyncWrite, fn upgrade_outbound( self, - socket: TSubstream, + socket: Negotiated, info: Self::Info, ) -> Self::Future { let framed = Framed::new(socket, UviBytes::default()); diff --git a/substrate/core/network-libp2p/src/lib.rs b/substrate/core/network-libp2p/src/lib.rs index aa5afb821b..1736fd44d7 100644 --- a/substrate/core/network-libp2p/src/lib.rs +++ b/substrate/core/network-libp2p/src/lib.rs @@ -106,12 +106,6 @@ pub struct NetworkState { /// List of addresses the node is currently listening on. pub listened_addresses: HashSet, // TODO (https://github.com/libp2p/rust-libp2p/issues/978): external_addresses: Vec, - /// If true, we only accept reserved peers. - pub is_reserved_only: bool, - /// PeerIds of the nodes that are marked as reserved. - pub reserved_peers: HashSet, - /// PeerIds of the nodes that are banned, and how long in the seconds the ban remains. - pub banned_peers: HashMap, /// List of node we're connected to. pub connected_peers: HashMap, /// List of node that we know of but that we're not connected to. @@ -137,15 +131,15 @@ pub struct NetworkStatePeer { /// If true, the peer is "open", which means that we have a Substrate-related protocol /// with this peer. pub open: bool, - /// List of addresses known for this node, with its reputation score. - pub known_addresses: HashMap, + /// List of addresses known for this node. + pub known_addresses: HashSet, } #[derive(Debug, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct NetworkStateNotConnectedPeer { - /// List of addresses known for this node, with its reputation score. - pub known_addresses: HashMap, + /// List of addresses known for this node. + pub known_addresses: HashSet, } #[derive(Debug, PartialEq, Serialize)] diff --git a/substrate/core/network-libp2p/src/service_task.rs b/substrate/core/network-libp2p/src/service_task.rs index 1ac8a6ed75..4e7e0a33a8 100644 --- a/substrate/core/network-libp2p/src/service_task.rs +++ b/substrate/core/network-libp2p/src/service_task.rs @@ -19,20 +19,19 @@ use crate::{ transport, NetworkState, NetworkStatePeer, NetworkStateNotConnectedPeer }; use crate::custom_proto::{CustomMessage, RegisteredProtocol}; -use crate::{NetworkConfiguration, NodeIndex, parse_str_addr}; +use crate::{NetworkConfiguration, NonReservedPeerMode, NodeIndex, parse_str_addr}; use fnv::FnvHashMap; use futures::{prelude::*, Stream}; -use libp2p::{multiaddr::Protocol, Multiaddr, PeerId}; +use libp2p::{multiaddr::Protocol, Multiaddr, core::swarm::NetworkBehaviour, PeerId}; use libp2p::core::{Swarm, nodes::Substream, transport::boxed::Boxed, muxing::StreamMuxerBox}; use libp2p::core::nodes::ConnectedPoint; use log::{debug, error, info, warn}; use std::collections::hash_map::Entry; use std::fs; -use std::io::{Error as IoError, ErrorKind as IoErrorKind}; +use std::io::Error as IoError; use std::path::Path; use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio_timer::Interval; +use std::time::Duration; /// Starts the substrate libp2p service. /// @@ -40,13 +39,48 @@ use tokio_timer::Interval; pub fn start_service( config: NetworkConfiguration, registered_custom: RegisteredProtocol, -) -> Result, IoError> +) -> Result<(Service, Arc), IoError> where TMessage: CustomMessage + Send + 'static { if let Some(ref path) = config.net_config_path { fs::create_dir_all(Path::new(path))?; } + // List of multiaddresses that we know in the network. + let mut known_addresses = Vec::new(); + let mut bootnodes = Vec::new(); + let mut reserved_nodes = Vec::new(); + + // Process the bootnodes. + for bootnode in config.boot_nodes.iter() { + match parse_str_addr(bootnode) { + Ok((peer_id, addr)) => { + bootnodes.push(peer_id.clone()); + known_addresses.push((peer_id, addr)); + }, + Err(_) => warn!(target: "sub-libp2p", "Not a valid bootnode address: {}", bootnode), + } + } + + // Initialize the reserved peers. + for reserved in config.reserved_nodes.iter() { + if let Ok((peer_id, addr)) = parse_str_addr(reserved) { + reserved_nodes.push(peer_id.clone()); + known_addresses.push((peer_id, addr)); + } else { + warn!(target: "sub-libp2p", "Not a valid reserved node address: {}", reserved); + } + } + + // Build the peerset. + let (peerset, peerset_receiver) = substrate_peerset::Peerset::from_config(substrate_peerset::PeersetConfig { + in_peers: 25, + out_peers: 25, + bootnodes, + reserved_only: config.non_reserved_mode == NonReservedPeerMode::Deny, + reserved_nodes, + }); + // Private and public keys configuration. let local_identity = config.node_key.clone().into_keypair()?; let local_public = local_identity.public(); @@ -54,7 +88,8 @@ where TMessage: CustomMessage + Send + 'static { // Build the swarm. let (mut swarm, bandwidth) = { - let behaviour = Behaviour::new(&config, local_public, registered_custom); + let user_agent = format!("{} ({})", config.client_version, config.node_name); + let behaviour = Behaviour::new(user_agent, local_public, registered_custom, known_addresses, peerset_receiver); let (transport, bandwidth) = transport::build_transport(local_identity); (Swarm::new(transport, behaviour, local_peer_id.clone()), bandwidth) }; @@ -75,36 +110,16 @@ where TMessage: CustomMessage + Send + 'static { Swarm::add_external_address(&mut swarm, addr.clone()); } - // Connect to the bootnodes. - for bootnode in config.boot_nodes.iter() { - match parse_str_addr(bootnode) { - Ok((peer_id, _)) => Swarm::dial(&mut swarm, peer_id), - Err(_) => warn!(target: "sub-libp2p", "Not a valid bootnode address: {}", bootnode), - } - } - - // Initialize the reserved peers. - for reserved in config.reserved_nodes.iter() { - if let Ok((peer_id, addr)) = parse_str_addr(reserved) { - swarm.add_reserved_peer(peer_id.clone(), addr); - Swarm::dial(&mut swarm, peer_id); - } else { - warn!(target: "sub-libp2p", "Not a valid reserved node address: {}", reserved); - } - } - - debug!(target: "sub-libp2p", "Topology started with {} entries", - swarm.num_topology_peers()); - - Ok(Service { + let service = Service { swarm, bandwidth, nodes_info: Default::default(), index_by_id: Default::default(), next_node_id: 1, - cleanup: Interval::new_interval(Duration::from_secs(60)), injected_events: Vec::new(), - }) + }; + + Ok((service, peerset)) } /// Event produced by the service. @@ -164,10 +179,6 @@ pub struct Service where TMessage: CustomMessage { /// Next index to assign to a node. next_node_id: NodeIndex, - /// Stream that fires when we need to cleanup and flush the topology, and cleanup the disabled - /// peers. - cleanup: Interval, - /// Events to produce on the Stream. injected_events: Vec>, } @@ -189,13 +200,11 @@ impl Service where TMessage: CustomMessage + Send + 'static { /// Returns a struct containing tons of useful information about the network. pub fn state(&mut self) -> NetworkState { - let now = Instant::now(); - let connected_peers = { let swarm = &mut self.swarm; self.nodes_info.values().map(move |info| { - let known_addresses = swarm.known_addresses(&info.peer_id) - .map(|(a, s)| (a.clone(), s)).collect(); + let known_addresses = NetworkBehaviour::addresses_of_peer(&mut **swarm, &info.peer_id) + .into_iter().collect(); (info.peer_id.to_base58(), NetworkStatePeer { endpoint: info.endpoint.clone().into(), @@ -214,10 +223,9 @@ where TMessage: CustomMessage + Send + 'static { let list = swarm.known_peers().filter(|p| !index_by_id.contains_key(p)) .cloned().collect::>(); list.into_iter().map(move |peer_id| { - let known_addresses = swarm.known_addresses(&peer_id) - .map(|(a, s)| (a.clone(), s)).collect(); (peer_id.to_base58(), NetworkStateNotConnectedPeer { - known_addresses, + known_addresses: NetworkBehaviour::addresses_of_peer(&mut **swarm, &peer_id) + .into_iter().collect(), }) }).collect() }; @@ -225,12 +233,6 @@ where TMessage: CustomMessage + Send + 'static { NetworkState { peer_id: Swarm::local_peer_id(&self.swarm).to_base58(), listened_addresses: Swarm::listeners(&self.swarm).cloned().collect(), - reserved_peers: self.swarm.reserved_peers().map(|p| p.to_base58()).collect(), - banned_peers: self.swarm.banned_nodes().map(|(p, until)| { - let dur = if until > now { until - now } else { Duration::new(0, 0) }; - (p.to_base58(), dur.as_secs()) - }).collect(), - is_reserved_only: self.swarm.is_reserved_only(), average_download_per_sec: self.bandwidth.average_download_per_sec(), average_upload_per_sec: self.bandwidth.average_upload_per_sec(), connected_peers, @@ -268,31 +270,6 @@ where TMessage: CustomMessage + Send + 'static { self.nodes_info.keys().cloned() } - /// Try to add a reserved peer. - pub fn add_reserved_peer(&mut self, peer_id: PeerId, addr: Multiaddr) { - self.swarm.add_reserved_peer(peer_id, addr); - } - - /// Try to remove a reserved peer. - /// - /// If we are in reserved mode and we were connected to a node with this peer ID, then this - /// method will disconnect it. - pub fn remove_reserved_peer(&mut self, peer_id: PeerId) { - self.swarm.remove_reserved_peer(peer_id); - } - - /// Start accepting all peers again if we weren't. - #[inline] - pub fn accept_unreserved_peers(&mut self) { - self.swarm.accept_unreserved_peers(); - } - - /// Start refusing non-reserved nodes. Disconnects the nodes that we are connected to that - /// aren't reserved. - pub fn deny_unreserved_peers(&mut self) { - self.swarm.deny_unreserved_peers(); - } - /// Returns the `PeerId` of a node. #[inline] pub fn peer_id_of_node(&self, node_index: NodeIndex) -> Option<&PeerId> { @@ -327,23 +304,10 @@ where TMessage: CustomMessage + Send + 'static { } } - /// Disconnects a peer and bans it for a little while. - /// - /// Same as `drop_node`, except that the same peer will not be able to reconnect later. - #[inline] - pub fn ban_node(&mut self, node_index: NodeIndex) { - if let Some(info) = self.nodes_info.get(&node_index) { - info!(target: "sub-libp2p", "Banned {:?} (#{:?}, {:?}, {:?})", info.peer_id, - node_index, info.endpoint, info.client_version); - self.swarm.ban_node(info.peer_id.clone()); - } - } - /// Disconnects a peer. /// /// This is asynchronous and will not immediately close the peer. /// Corresponding closing events will be generated once the closing actually happens. - #[inline] pub fn drop_node(&mut self, node_index: NodeIndex) { if let Some(info) = self.nodes_info.get(&node_index) { debug!(target: "sub-libp2p", "Dropping {:?} on purpose (#{:?}, {:?}, {:?})", @@ -352,6 +316,11 @@ where TMessage: CustomMessage + Send + 'static { } } + /// Adds a hard-coded address for the given peer, that never expires. + pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) { + self.swarm.add_known_address(peer_id, addr) + } + /// Get debug info for a given peer. pub fn peer_debug_info(&self, who: NodeIndex) -> String { if let Some(info) = self.nodes_info.get(&who) { @@ -394,7 +363,6 @@ where TMessage: CustomMessage + Send + 'static { loop { match self.swarm.poll() { Ok(Async::Ready(Some(BehaviourOut::CustomProtocolOpen { peer_id, version, endpoint }))) => { - debug!(target: "sub-libp2p", "Opened custom protocol with {:?}", peer_id); let node_index = self.index_of_peer_or_assign(peer_id.clone(), endpoint); break Ok(Async::Ready(Some(ServiceEvent::OpenedCustomProtocol { peer_id, @@ -403,8 +371,7 @@ where TMessage: CustomMessage + Send + 'static { debug_info: self.peer_debug_info(node_index), }))) } - Ok(Async::Ready(Some(BehaviourOut::CustomProtocolClosed { peer_id, result }))) => { - debug!(target: "sub-libp2p", "Custom protocol with {:?} closed: {:?}", peer_id, result); + Ok(Async::Ready(Some(BehaviourOut::CustomProtocolClosed { peer_id, .. }))) => { let node_index = *self.index_by_id.get(&peer_id).expect("index_by_id is always kept in sync with the state of the behaviour"); break Ok(Async::Ready(Some(ServiceEvent::ClosedCustomProtocol { node_index, @@ -457,40 +424,6 @@ where TMessage: CustomMessage + Send + 'static { } } } - - /// Polls the stream that fires when we need to cleanup and flush the topology. - fn poll_cleanup(&mut self) -> Poll>, IoError> { - loop { - match self.cleanup.poll() { - Ok(Async::NotReady) => return Ok(Async::NotReady), - Ok(Async::Ready(Some(_))) => { - debug!(target: "sub-libp2p", "Cleaning and flushing topology"); - self.swarm.cleanup(); - if let Err(err) = self.swarm.flush_topology() { - warn!(target: "sub-libp2p", "Failed to flush topology: {:?}", err); - } - debug!(target: "sub-libp2p", "Topology now contains {} nodes", - self.swarm.num_topology_peers()); - } - Ok(Async::Ready(None)) => { - warn!(target: "sub-libp2p", "Topology flush stream ended unexpectedly"); - return Ok(Async::Ready(None)) - } - Err(err) => { - warn!(target: "sub-libp2p", "Topology flush stream errored: {:?}", err); - return Err(IoError::new(IoErrorKind::Other, err)) - } - } - } - } -} - -impl Drop for Service where TMessage: CustomMessage { - fn drop(&mut self) { - if let Err(err) = self.swarm.flush_topology() { - warn!(target: "sub-libp2p", "Failed to flush topology: {:?}", err); - } - } } impl Stream for Service where TMessage: CustomMessage + Send + 'static { @@ -507,11 +440,6 @@ impl Stream for Service where TMessage: CustomMessage + Send Async::NotReady => (), } - match self.poll_cleanup()? { - Async::Ready(value) => return Ok(Async::Ready(value)), - Async::NotReady => (), - } - // The only way we reach this is if we went through all the `NotReady` paths above, // ensuring the current task is registered everywhere. Ok(Async::NotReady) diff --git a/substrate/core/network-libp2p/tests/test.rs b/substrate/core/network-libp2p/tests/test.rs index f8f204c5b8..d4043d1cb7 100644 --- a/substrate/core/network-libp2p/tests/test.rs +++ b/substrate/core/network-libp2p/tests/test.rs @@ -41,7 +41,7 @@ fn build_nodes(num: usize) -> Vec> }; let proto = substrate_network_libp2p::RegisteredProtocol::new(*b"tst", &[1]); - result.push(substrate_network_libp2p::start_service(config, proto).unwrap()); + result.push(substrate_network_libp2p::start_service(config, proto).unwrap().0); } result diff --git a/substrate/core/network/Cargo.toml b/substrate/core/network/Cargo.toml index 6e7e8599db..bed1946fa8 100644 --- a/substrate/core/network/Cargo.toml +++ b/substrate/core/network/Cargo.toml @@ -27,6 +27,7 @@ client = { package = "substrate-client", path = "../../core/client" } runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" } parity-codec = { version = "3.2", features = ["derive"] } network_libp2p = { package = "substrate-network-libp2p", path = "../../core/network-libp2p" } +peerset = { package = "substrate-peerset", path = "../../core/peerset" } tokio = "0.1.11" keyring = { package = "substrate-keyring", path = "../../core/keyring", optional = true } test_client = { package = "substrate-test-client", path = "../../core/test-client", optional = true } diff --git a/substrate/core/network/src/service.rs b/substrate/core/network/src/service.rs index 714192b7f0..e55db474ee 100644 --- a/substrate/core/network/src/service.rs +++ b/substrate/core/network/src/service.rs @@ -24,6 +24,7 @@ use parking_lot::{Mutex, RwLock}; use network_libp2p::{ProtocolId, NetworkConfiguration, NodeIndex, Severity}; use network_libp2p::{start_service, parse_str_addr, Service as NetworkService, ServiceEvent as NetworkServiceEvent}; use network_libp2p::{multiaddr, RegisteredProtocol, NetworkState}; +use peerset::Peerset; use consensus::import_queue::{ImportQueue, Link}; use crate::consensus_gossip::ConsensusGossip; use crate::message::{Message, ConsensusEngineId}; @@ -138,6 +139,9 @@ pub struct Service> { peers: Arc>>>, /// Network service network: Arc>>>, + /// Peerset manager (PSM); manages the reputation of nodes and indicates the network which + /// nodes it should be connected to or not. + peerset: Arc, /// Protocol sender protocol_sender: Sender>, /// Sender for messages to the background service task, and handle for the background thread. @@ -174,7 +178,7 @@ impl> Service { )?; let versions = [(protocol::CURRENT_VERSION as u8)]; let registered = RegisteredProtocol::new(protocol_id, &versions[..]); - let (thread, network) = start_thread( + let (thread, network, peerset) = start_thread( network_to_protocol_sender, network_port, params.network_config, @@ -186,6 +190,7 @@ impl> Service { is_offline, is_major_syncing, peers, + peerset, network, protocol_sender: protocol_sender.clone(), bg_thread: Some(thread), @@ -343,20 +348,21 @@ pub trait ManageNetwork { impl> ManageNetwork for Service { fn accept_unreserved_peers(&self) { - self.network.lock().accept_unreserved_peers(); + self.peerset.set_reserved_only(false); } fn deny_unreserved_peers(&self) { - self.network.lock().deny_unreserved_peers(); + self.peerset.set_reserved_only(true); } fn remove_reserved_peer(&self, peer: PeerId) { - self.network.lock().remove_reserved_peer(peer); + self.peerset.remove_reserved_peer(&peer); } fn add_reserved_peer(&self, peer: String) -> Result<(), String> { - let (addr, peer_id) = parse_str_addr(&peer).map_err(|e| format!("{:?}", e))?; - self.network.lock().add_reserved_peer(addr, peer_id); + let (peer_id, addr) = parse_str_addr(&peer).map_err(|e| format!("{:?}", e))?; + self.peerset.add_reserved_peer(peer_id.clone()); + self.network.lock().add_known_address(peer_id, addr); Ok(()) } @@ -466,10 +472,10 @@ fn start_thread( network_port: NetworkPort, config: NetworkConfiguration, registered: RegisteredProtocol>, -) -> Result<((oneshot::Sender<()>, thread::JoinHandle<()>), Arc>>>), Error> { +) -> Result<((oneshot::Sender<()>, thread::JoinHandle<()>), Arc>>>, Arc), Error> { // Start the main service. - let service = match start_service(config, registered) { - Ok(service) => Arc::new(Mutex::new(service)), + let (service, peerset) = match start_service(config, registered) { + Ok((service, peerset)) => (Arc::new(Mutex::new(service)), peerset), Err(err) => { warn!("Error starting network: {}", err); return Err(err.into()) @@ -493,7 +499,7 @@ fn start_thread( }; })?; - Ok(((close_tx, thread), service)) + Ok(((close_tx, thread), service, peerset)) } /// Runs the background thread that handles the networking. @@ -530,7 +536,9 @@ fn run_thread( match severity { Severity::Bad(message) => { info!(target: "sync", "Banning {:?} because {:?}", who, message); - network_service_2.lock().ban_node(who) + warn!(target: "sync", "Banning a node is a deprecated mechanism that \ + should be removed"); + network_service_2.lock().drop_node(who) }, Severity::Useless(message) => { info!(target: "sync", "Dropping {:?} because {:?}", who, message); diff --git a/substrate/core/peerset/Cargo.toml b/substrate/core/peerset/Cargo.toml new file mode 100644 index 0000000000..3eaf1d9747 --- /dev/null +++ b/substrate/core/peerset/Cargo.toml @@ -0,0 +1,20 @@ +[package] +description = "Connectivity manager based on reputation" +homepage = "http://parity.io" +license = "GPL-3.0" +name = "substrate-peerset" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +fnv = "1.0" +futures = "0.1" +libp2p = { git = "https://github.com/tomaka/libp2p-rs", branch = "substrate-tmp-2019-03-20", default-features = false } +log = "0.4" +parking_lot = "0.7" +rand = "0.6" +serde = "1.0.70" +serde_derive = "1.0.70" +serde_json = "1.0.24" +tokio-io = "0.1" diff --git a/substrate/core/peerset/src/lib.rs b/substrate/core/peerset/src/lib.rs new file mode 100644 index 0000000000..2f39b96a94 --- /dev/null +++ b/substrate/core/peerset/src/lib.rs @@ -0,0 +1,297 @@ +// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Peer Set Manager (PSM). Contains the strategy for choosing which nodes the network should be +//! connected to. + +use std::collections::HashSet; +use futures::{prelude::*, sync::mpsc}; +use libp2p::PeerId; +use parking_lot::Mutex; +use std::sync::Arc; + +/// Shared part of the peer set manager (PSM). Distributed around the code. +pub struct Peerset { + tx: mpsc::UnboundedSender, + inner: Mutex, +} + +struct Inner { + /// List of nodes that we know exist but we are not connected to. + discovered: Vec, + /// List of reserved nodes. + reserved: HashSet, + /// If true, we only accept reserved nodes. + reserved_only: bool, + /// Node slots. Each slot contains either `None` if the node is free, or `Some` if it is + /// assigned to a peer. + slots: Vec>, +} + +/// Message that can be sent by the peer set manager (PSM). +#[derive(Debug)] +pub enum Message { + /// Request to open a connection to the given peer. From the point of view of the PSM, we are + /// immediately connected. + Connect(PeerId), + + /// Drop the connection to the given peer, or cancel the connection attempt after a `Connect`. + Drop(PeerId), + + /// Equivalent to `Connect` for the peer corresponding to this incoming index. + Accept(IncomingIndex), + + /// Equivalent to `Drop` for the peer corresponding to this incoming index. + Reject(IncomingIndex), +} + +/// Opaque identifier for an incoming connection. Allocated by the network. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct IncomingIndex(pub u64); + +impl From for IncomingIndex { + fn from(val: u64) -> IncomingIndex { + IncomingIndex(val) + } +} + +/// Configuration to pass when creating the peer set manager. +#[derive(Debug)] +pub struct PeersetConfig { + /// Maximum number of ingoing links to peers. + pub in_peers: u32, + + /// Maximum number of outgoing links to peers. + pub out_peers: u32, + + /// List of bootstrap nodes to initialize the peer with. + /// + /// > **Note**: Keep in mind that the networking has to know an address for these nodes, + /// > otherwise it will not be able to connect to them. + pub bootnodes: Vec, + + /// If true, we only accept reserved nodes. + pub reserved_only: bool, + + /// List of nodes that we should always be connected to. + /// + /// > **Note**: Keep in mind that the networking has to know an address for these nodes, + /// > otherwise it will not be able to connect to them. + pub reserved_nodes: Vec, +} + +/// Side of the peer set manager owned by the network. In other words, the "receiving" side. +/// +/// Implements the `Stream` trait and can be polled for messages. The `Stream` never ends and never +/// errors. +pub struct PeersetMut { + parent: Arc, + rx: mpsc::UnboundedReceiver, +} + +impl Peerset { + /// Builds a new peerset from the given configuration. + pub fn from_config(config: PeersetConfig) -> (Arc, PeersetMut) { + let (tx, rx) = mpsc::unbounded(); + + let mut inner = Inner { + discovered: config.bootnodes.into_iter().collect(), + reserved: Default::default(), + reserved_only: config.reserved_only, + slots: (0 .. (config.in_peers + config.out_peers)).map(|_| None).collect(), + }; + + alloc_slots(&mut inner, &tx); + + let peerset = Arc::new(Peerset { + tx, + inner: Mutex::new(inner), + }); + + let rx = PeersetMut { + parent: peerset.clone(), + rx, + }; + + for reserved in config.reserved_nodes { + peerset.add_reserved_peer(reserved); + } + + (peerset, rx) + } + + /// Adds a new reserved peer. The peerset will make an effort to always remain connected to + /// this peer. + /// + /// Has no effect if the node was already a reserved peer. + /// + /// > **Note**: Keep in mind that the networking has to know an address for this node, + /// > otherwise it will not be able to connect to it. + pub fn add_reserved_peer(&self, peer_id: PeerId) { + let mut inner = self.inner.lock(); + if !inner.reserved.insert(peer_id.clone()) { + // Immediately return if this peer was already in the list. + return; + } + + // Assign a slot for this reserved peer. + if let Some(pos) = inner.slots.iter().position(|s| s.as_ref().map(|n| !inner.reserved.contains(n)).unwrap_or(true)) { + let _ = self.tx.unbounded_send(Message::Connect(peer_id.clone())); + inner.slots[pos] = Some(peer_id); + + } else { + // All slots are filled with reserved peers. + if inner.discovered.iter().all(|p| *p != peer_id) { + inner.discovered.push(peer_id); + } + } + } + + /// Remove a previously-added reserved peer. + /// + /// Has no effect if the node was not a reserved peer. + pub fn remove_reserved_peer(&self, peer_id: &PeerId) { + let mut inner = self.inner.lock(); + inner.reserved.remove(peer_id); + } + + /// Sets whether or not the peerset only has connections . + pub fn set_reserved_only(&self, reserved_only: bool) { + let mut inner = self.inner.lock(); + let inner = &mut *inner; // Fixes a borrowing issue. + inner.reserved_only = reserved_only; + + // Disconnect non-reserved nodes. + if reserved_only { + for slot in &mut inner.slots { + if let Some(peer) = slot.as_ref() { + if inner.reserved.contains(peer) { + continue; + } + + let _ = self.tx.unbounded_send(Message::Drop(peer.clone())); + } + + *slot = None; + } + } + } + + /// Reports an adjustement to the reputation of the given peer. + pub fn report_peer(&self, _peer_id: &PeerId, _score_diff: i32) { + // This is not implemented in this dummy implementation. + } +} + +fn alloc_slots(inner: &mut Inner, tx: &mpsc::UnboundedSender) { + if inner.reserved_only { + return; + } + + for slot in inner.slots.iter_mut() { + if slot.is_some() { + continue; + } + + if !inner.discovered.is_empty() { + let elem = inner.discovered.remove(0); + *slot = Some(elem.clone()); + let _ = tx.unbounded_send(Message::Connect(elem)); + } + } +} + +impl PeersetMut { + /// Indicate that we received an incoming connection. Must be answered either with + /// a corresponding `Accept` or `Reject`, except if we were already connected to this peer. + /// + /// Note that this mechanism is orthogonal to `Connect`/`Drop`. Accepting an incoming + /// connection implicitely means `Accept`, but incoming connections aren't cancelled by + /// `dropped`. + /// + /// Because of concurrency issues, it is acceptable to call `incoming` with a `PeerId` the + /// peerset is already connected to, in which case it must not answer. + pub fn incoming(&self, peer_id: PeerId, index: IncomingIndex) { + let mut inner = self.parent.inner.lock(); + if inner.slots.iter().any(|s| s.as_ref() == Some(&peer_id)) { + return + } + + if let Some(pos) = inner.slots.iter().position(|s| s.is_none()) { + inner.slots[pos] = Some(peer_id); + let _ = self.parent.tx.unbounded_send(Message::Accept(index)); + } else { + if inner.discovered.iter().all(|p| *p != peer_id) { + inner.discovered.push(peer_id); + } + let _ = self.parent.tx.unbounded_send(Message::Reject(index)); + } + } + + /// Indicate that we dropped an active connection with a peer, or that we failed to connect. + /// + /// Must only be called after the PSM has either generated a `Connect` message with this + /// `PeerId`, or accepted an incoming connection with this `PeerId`. + pub fn dropped(&self, peer_id: &PeerId) { + let mut inner = self.parent.inner.lock(); + + // Automatically connect back if reserved. + if inner.reserved.contains(peer_id) { + let _ = self.parent.tx.unbounded_send(Message::Connect(peer_id.clone())); + return + } + + // Otherwise, free the slot. + for slot in inner.slots.iter_mut() { + if slot.as_ref() == Some(peer_id) { + *slot = None; + } + } + + // Note: in this dummy implementation we consider that peers never expire. As soon as we + // are disconnected from a peer, we try again. + if inner.discovered.iter().all(|p| p != peer_id) { + inner.discovered.push(peer_id.clone()); + } + alloc_slots(&mut inner, &self.parent.tx); + } + + /// Adds a discovered peer id to the PSM. + /// + /// > **Note**: There is no equivalent "expired" message, meaning that it is the responsibility + /// > of the PSM to remove `PeerId`s that fail to dial too often. + pub fn discovered(&self, peer_id: PeerId) { + let mut inner = self.parent.inner.lock(); + + if inner.slots.iter().any(|p| p.as_ref() == Some(&peer_id)) { + return; + } + + if inner.discovered.iter().all(|p| *p != peer_id) { + inner.discovered.push(peer_id); + } + alloc_slots(&mut inner, &self.parent.tx); + } +} + +impl Stream for PeersetMut { + type Item = Message; + type Error = (); + + fn poll(&mut self) -> Poll, Self::Error> { + self.rx.poll() + } +} diff --git a/substrate/core/rpc/src/system/tests.rs b/substrate/core/rpc/src/system/tests.rs index 6aee5c3fa2..66f10694fb 100644 --- a/substrate/core/rpc/src/system/tests.rs +++ b/substrate/core/rpc/src/system/tests.rs @@ -50,9 +50,6 @@ impl network::SyncProvider for Status { network::NetworkState { peer_id: String::new(), listened_addresses: Default::default(), - is_reserved_only: false, - reserved_peers: Default::default(), - banned_peers: Default::default(), connected_peers: Default::default(), not_connected_peers: Default::default(), average_download_per_sec: 0, @@ -207,9 +204,6 @@ fn system_network_state() { network::NetworkState { peer_id: String::new(), listened_addresses: Default::default(), - is_reserved_only: false, - reserved_peers: Default::default(), - banned_peers: Default::default(), connected_peers: Default::default(), not_connected_peers: Default::default(), average_download_per_sec: 0,