diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 9a35b85c54..904b59b8a2 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -42,7 +42,7 @@ name = "aio-limited" version = "0.1.0" source = "git+https://github.com/paritytech/aio-limited.git#1f42497dcd2a6f85b83af97cd80314b26a1e4a9e" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -467,7 +467,7 @@ source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9 dependencies = [ "base64 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "chashmap 2.2.1 (git+https://github.com/redox-os/tfs)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -563,7 +563,7 @@ name = "exit-future" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -600,6 +600,18 @@ dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "finality-grandpa" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fixed-hash" version = "0.2.2" @@ -654,7 +666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -662,7 +674,7 @@ name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -787,7 +799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -869,7 +881,7 @@ name = "jsonrpc-core" version = "8.0.2" source = "git+https://github.com/paritytech/jsonrpc.git#53f814e94317313ccb485eb57e435c921a974cf0" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1031,7 +1043,7 @@ version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "libp2p-dns 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "libp2p-floodsub 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1063,7 +1075,7 @@ dependencies = [ "bs58 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.8 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "multihash 0.8.1-pre (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1083,7 +1095,7 @@ name = "libp2p-dns" version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1100,7 +1112,7 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.8 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1119,7 +1131,7 @@ source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9 dependencies = [ "bytes 0.4.8 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "libp2p-peerstore 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1142,7 +1154,7 @@ dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "datastore 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "libp2p-identify 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "libp2p-ping 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1165,7 +1177,7 @@ source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9 dependencies = [ "bytes 0.4.8 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1181,7 +1193,7 @@ source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9 dependencies = [ "bs58 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "datastore 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1195,7 +1207,7 @@ version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1212,7 +1224,7 @@ version = "0.1.1" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "aio-limited 0.1.0 (git+https://github.com/paritytech/aio-limited.git)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1225,7 +1237,7 @@ version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "libp2p-peerstore 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1247,7 +1259,7 @@ dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "ctr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1264,7 +1276,7 @@ name = "libp2p-tcp-transport" version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1278,7 +1290,7 @@ name = "libp2p-transport-timeout" version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1289,7 +1301,7 @@ name = "libp2p-uds" version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1301,7 +1313,7 @@ name = "libp2p-websocket" version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "multiaddr 0.3.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1317,7 +1329,7 @@ version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1509,7 +1521,7 @@ version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1582,7 +1594,7 @@ version = "0.1.0" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "node-primitives 0.1.0", "node-runtime 0.1.0", @@ -1631,7 +1643,7 @@ dependencies = [ name = "node-network" version = "0.1.0" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "node-consensus 0.1.0", "node-primitives 0.1.0", @@ -2169,7 +2181,7 @@ name = "relay" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2186,7 +2198,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2248,7 +2260,7 @@ version = "0.1.0" source = "git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d#8111062f0177fd7423626f2db9560273644a4c4d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2807,7 +2819,7 @@ version = "0.1.0" dependencies = [ "ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "node-cli 0.1.0", "vergen 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2817,7 +2829,7 @@ name = "substrate-bft" version = "0.1.0" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2843,7 +2855,7 @@ dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "names 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2867,7 +2879,7 @@ version = "0.1.0" dependencies = [ "error-chain 0.12.0 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.9.0 (git+https://github.com/paritytech/trie)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2935,6 +2947,23 @@ dependencies = [ "wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-finality-grandpa" +version = "0.1.0" +dependencies = [ + "finality-grandpa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 0.1.0", + "substrate-client 0.1.0", + "substrate-keyring 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-keyring" version = "0.1.0" @@ -2990,7 +3019,7 @@ dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3014,7 +3043,7 @@ dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (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.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3108,7 +3137,7 @@ version = "0.3.0" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3139,7 +3168,7 @@ version = "0.3.0" dependencies = [ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-client 0.1.0", @@ -3240,7 +3269,7 @@ name = "substrate-transaction-pool" version = "0.1.0" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3424,7 +3453,7 @@ name = "tk-listen" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3435,7 +3464,7 @@ name = "tokio" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3453,7 +3482,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3463,7 +3492,7 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3480,7 +3509,7 @@ name = "tokio-current-thread" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3489,7 +3518,7 @@ name = "tokio-dns-unofficial" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3500,7 +3529,7 @@ name = "tokio-executor" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3508,7 +3537,7 @@ name = "tokio-fs" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3519,7 +3548,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3528,7 +3557,7 @@ name = "tokio-proto" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3545,7 +3574,7 @@ name = "tokio-reactor" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3558,7 +3587,7 @@ name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3567,7 +3596,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3580,7 +3609,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3593,7 +3622,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3603,7 +3632,7 @@ name = "tokio-tls" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3615,7 +3644,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3628,7 +3657,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3894,7 +3923,7 @@ name = "want" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3919,7 +3948,7 @@ dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4036,7 +4065,7 @@ version = "0.1.0" source = "git+https://github.com/paritytech/yamux#5acf79ecfb69ccdcb65c9f624f285b79716a029d" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "nohash-hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4119,6 +4148,7 @@ dependencies = [ "checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" +"checksum finality-grandpa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9a8a25e71e4a11a1a5ef36a9ad09b4ae8c4e0bcf27a900137899ac678b01b0" "checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -4126,7 +4156,7 @@ dependencies = [ "checksum fs-swap 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31a94e9407e53addc49de767234a0b000978523c59117e5badb575ccbb8370f6" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" +"checksum futures 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "0c84b40c7e2de99ffd70602db314a7a8c26b2b3d830e6f7f7a142a8860ab3ca4" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index a48a1685b1..bd1a9a8746 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -24,6 +24,7 @@ members = [ "core/client", "core/client/db", "core/executor", + "core/finality-grandpa", "core/keyring", "core/misbehavior-check", "core/network", diff --git a/substrate/core/finality-grandpa/Cargo.toml b/substrate/core/finality-grandpa/Cargo.toml new file mode 100644 index 0000000000..ea8880137d --- /dev/null +++ b/substrate/core/finality-grandpa/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "substrate-finality-grandpa" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +futures = "0.1.17" +parity-codec = "2.0" +sr-primitives = { path = "../sr-primitives" } +substrate-primitives = { path = "../primitives" } +substrate-client = { path = "../client" } +log = "0.4" +tokio = "0.1.7" + +[dependencies.finality-grandpa] +version = "0.1.3" +features = ["derive-codec"] + +[dev-dependencies] +substrate-network = { path = "../network", features = ["test-helpers"] } +parking_lot = "0.4" +substrate-keyring = { path = "../keyring" } diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs new file mode 100644 index 0000000000..f66eabe7fe --- /dev/null +++ b/substrate/core/finality-grandpa/src/lib.rs @@ -0,0 +1,767 @@ +// Copyright 2018 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 . + +//! Integration of the GRANDPA finality gadget into substrate. +//! +//! This is a long-running future that produces finality notifications. + +extern crate finality_grandpa as grandpa; +extern crate futures; +extern crate substrate_client as client; +extern crate sr_primitives as runtime_primitives; +extern crate substrate_primitives; +extern crate tokio; +extern crate parity_codec as codec; + +#[macro_use] +extern crate log; + +#[cfg(test)] +extern crate substrate_network as network; + +#[cfg(test)] +extern crate parking_lot; + +#[cfg(test)] +extern crate substrate_keyring as keyring; + +use futures::prelude::*; +use futures::stream::Fuse; +use futures::sync::mpsc; +use client::{Client, ImportNotifications, backend::Backend, CallExecutor}; +use codec::{Encode, Decode}; +use runtime_primitives::traits::{As, NumberFor, Block as BlockT, Header as HeaderT}; +use runtime_primitives::generic::BlockId; +use substrate_primitives::{ed25519, AuthorityId, Blake2Hasher}; +use tokio::timer::Interval; + +use grandpa::Error as GrandpaError; +use grandpa::{voter, round::State as RoundState, Prevote, Precommit, Equivocation}; + +use std::collections::{VecDeque, HashMap}; +use std::sync::Arc; +use std::time::{Instant, Duration}; + +const LAST_COMPLETED_KEY: &[u8] = b"grandpa_completed_round"; + +/// A GRANDPA message for a substrate chain. +pub type Message = grandpa::Message<::Hash>; +/// A signed message. +pub type SignedMessage = grandpa::SignedMessage<::Hash, ed25519::Signature, AuthorityId>; + +/// Configuration for the GRANDPA service. +pub struct Config { + /// The expected duration for a message to be gossiped across the network. + pub gossip_duration: Duration, + /// The voters. + // TODO: make dynamic + pub voters: Vec, + /// The local signing key. + pub local_key: Option>, +} + +/// Errors that can occur while voting in GRANDPA. +#[derive(Debug)] +pub enum Error { + /// An error within grandpa. + Grandpa(GrandpaError), + /// A network error. + Network(String), + /// A blockchain error. + Blockchain(String), + /// A timer failed to fire. + Timer(::tokio::timer::Error), +} + +impl From for Error { + fn from(e: GrandpaError) -> Self { + Error::Grandpa(e) + } +} + +/// A handle to the network. This is generally implemented by providing some +/// handle to a gossip service or similar. +/// +/// Intended to be a lightweight handle such as an `Arc`. +pub trait Network: Clone { + /// A stream of input messages for a topic. + type In: Stream,Error=()>; + + /// Get a stream of messages for a specific round. This stream should + /// never logically conclude. + fn messages_for(&self, round: u64) -> Self::In; + + /// Send a message at a specific round out. + fn send_message(&self, round: u64, message: Vec); + + /// Clean up messages for a round. + fn drop_messages(&self, round: u64); +} + +/// Something which can determine if a block is known. +pub trait BlockStatus { + /// Return `Ok(Some(number))` or `Ok(None)` depending on whether the block + /// is definitely known and has been imported. + /// If an unexpected error occurs, return that. + fn block_number(&self, hash: Block::Hash) -> Result, Error>; +} + +impl BlockStatus for Arc> where + B: Backend, + E: CallExecutor, + NumberFor: As, +{ + fn block_number(&self, hash: Block::Hash) -> Result, Error> { + self.block_number_from_id(&BlockId::Hash(hash)) + .map_err(|e| Error::Blockchain(format!("{:?}", e))) + .map(|num| num.map(|n| n.as_())) + } +} + +/// Buffering imported messages until blocks with given hashes are imported. +pub struct UntilImported { + import_notifications: Fuse>, + status_check: Status, + inner: Fuse, + ready: VecDeque>, + check_pending: Interval, + pending: HashMap>>, +} + +impl, I: Stream> UntilImported { + fn new( + import_notifications: ImportNotifications, + status_check: Status, + stream: I, + ) -> Self { + // how often to check if pending messages that are waiting for blocks to be + // imported can be checked. + // + // the import notifications interval takes care of most of this; this is + // used in the event of missed import notifications + const CHECK_PENDING_INTERVAL: Duration = Duration::from_secs(5); + let now = Instant::now(); + + let check_pending = Interval::new(now + CHECK_PENDING_INTERVAL, CHECK_PENDING_INTERVAL); + UntilImported { + import_notifications: import_notifications.fuse(), + status_check, + inner: stream.fuse(), + ready: VecDeque::new(), + check_pending, + pending: HashMap::new(), + } + } +} + +impl, I> Stream for UntilImported + where I: Stream,Error=Error> +{ + type Item = SignedMessage; + type Error = Error; + + fn poll(&mut self) -> Poll>, Error> { + loop { + match self.inner.poll() { + Err(e) => return Err(e), + Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), + Ok(Async::Ready(Some(signed_message))) => { + let (&target_hash, target_number) = signed_message.target(); + + // new message: hold it until the block is known. + if let Some(number) = self.status_check.block_number(target_hash)? { + if number != target_number { + warn!( + target: "afg", + "Authority {:?} signed GRANDPA message with \ + wrong block number for hash {}", + signed_message.id, + target_hash + ); + } else { + self.ready.push_back(signed_message) + } + } else { + self.pending.entry(target_hash) + .or_insert_with(Vec::new) + .push(signed_message); + } + } + Ok(Async::NotReady) => break, + } + } + + loop { + match self.import_notifications.poll() { + Err(_) => return Err(Error::Network(format!("Failed to get new message"))), + Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), + Ok(Async::Ready(Some(notification))) => { + // new block imported. queue up all messages tied to that hash. + if let Some(messages) = self.pending.remove(¬ification.hash) { + self.ready.extend(messages); + } + } + Ok(Async::NotReady) => break, + } + } + + let mut update_interval = false; + while let Async::Ready(Some(_)) = self.check_pending.poll().map_err(Error::Timer)? { + update_interval = true; + } + + if update_interval { + let mut known_keys = Vec::new(); + for &block_hash in self.pending.keys() { + if let Some(number) = self.status_check.block_number(block_hash)? { + known_keys.push((block_hash, number)); + } + } + + for (known_hash, canon_number) in known_keys { + if let Some(mut pending_messages) = self.pending.remove(&known_hash) { + // verify canonicality of pending messages. + pending_messages.retain(|msg| { + let number_correct = msg.target().1 == canon_number; + if !number_correct { + warn!( + target: "afg", + "Authority {:?} signed GRANDPA message with \ + wrong block number for hash {}", + msg.id, + known_hash, + ); + } + number_correct + }); + self.ready.extend(pending_messages); + } + } + } + + if let Some(ready) = self.ready.pop_front() { + return Ok(Async::Ready(Some(ready))) + } + + if self.import_notifications.is_done() && self.inner.is_done() { + Ok(Async::Ready(None)) + } else { + Ok(Async::NotReady) + } + } +} + +// clears the network messages for inner round on drop. +struct ClearOnDrop { + round: u64, + inner: I, + network: N, +} + +impl Sink for ClearOnDrop { + type SinkItem = I::SinkItem; + type SinkError = I::SinkError; + + fn start_send(&mut self, item: Self::SinkItem) -> StartSend { + self.inner.start_send(item) + } + + fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + self.inner.poll_complete() + } + + fn close(&mut self) -> Poll<(), Self::SinkError> { + self.inner.close() + } +} + +impl Drop for ClearOnDrop { + fn drop(&mut self) { + self.network.drop_messages(self.round); + } +} + +// converts a message stream into a stream of signed messages. +// the output stream checks signatures also. +fn checked_message_stream(inner: S, voters: Vec) + -> impl Stream,Error=Error> where + S: Stream,Error=()> +{ + inner + .filter_map(|raw| { + let decoded = SignedMessage::::decode(&mut &raw[..]); + if decoded.is_none() { + debug!(target: "afg", "Skipping malformed message {:?}", raw); + } + decoded + }) + .and_then(move |msg| { + // check signature. + if !voters.contains(&msg.id) { + debug!(target: "afg", "Skipping message from unknown voter {}", msg.id); + return Ok(None); + } + + let as_public = ::ed25519::Public::from_raw(msg.id.0); + let encoded_raw = msg.message.encode(); + if ::ed25519::verify_strong(&msg.signature, &encoded_raw, as_public) { + Ok(Some(msg)) + } else { + debug!(target: "afg", "Skipping message with bad signature"); + Ok(None) + } + }) + .filter_map(|x| x) + .map_err(|()| Error::Network(format!("Failed to receive message on unbounded stream"))) +} + +fn outgoing_messages( + local_key: Option>, + voters: Vec, + round: u64, + network: N, +) -> ( + impl Stream,Error=Error>, + impl Sink,SinkError=Error>, +) { + let locals = local_key.and_then(|pair| { + let public = pair.public(); + voters.iter().find(|id| id.0 == public.0).map(move |id| (pair, id.clone())) + }); + + let (tx, rx) = mpsc::unbounded(); + let rx = rx + .map(move |msg: Message| { + // when locals exist. sign messages on import + if let Some((ref pair, local_id)) = locals { + let encoded = msg.encode(); + let signature = pair.sign(&encoded[..]); + let signed = SignedMessage:: { + message: msg, + signature, + id: local_id, + }; + + // forward to network. + network.send_message(round, signed.encode()); + Some(signed) + } else { + None + } + }) + .filter_map(|x| x) + .map_err(move |()| Error::Network( + format!("Failed to receive on unbounded receiver for round {}", round) + )); + + let tx = tx.sink_map_err(move |e| Error::Network(format!("Failed to broadcast message \ + to network in round {}: {:?}", round, e))); + + (rx, tx) +} + +/// The environment we run GRANDPA in. +pub struct Environment { + inner: Arc>, + voters: HashMap, + config: Config, + network: N, +} + +impl grandpa::Chain for Environment where + Block: 'static, + B: Backend + 'static, + E: CallExecutor + 'static, + N: Network + 'static, + N::In: 'static, + NumberFor: As, +{ + fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result, GrandpaError> { + let tree_route_res = ::client::blockchain::tree_route( + self.inner.backend().blockchain(), + BlockId::Hash(block), + BlockId::Hash(base), + ); + + let tree_route = match tree_route_res { + Ok(tree_route) => tree_route, + Err(e) => { + debug!(target: "afg", "Encountered error computing ancestry between block {:?} and base {:?}: {:?}", + block, base, e); + + return Err(GrandpaError::NotDescendent); + } + }; + + if tree_route.common_block().hash != base { + return Err(GrandpaError::NotDescendent); + } + + // skip one because our ancestry is meant to start from the parent of `block`, + // and `tree_route` includes it. + Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect()) + } + + fn best_chain_containing(&self, block: Block::Hash) -> Option<(Block::Hash, u32)> { + match self.inner.best_containing(block, None) { + Ok(Some(hash)) => { + let header = self.inner.header(&BlockId::Hash(hash)).ok()? + .expect("Header known to exist after `best_containing` call; qed"); + + Some((hash, header.number().as_())) + } + Ok(None) => None, + Err(e) => { + debug!(target: "afg", "Encountered error finding best chain containing {:?}: {:?}", block, e); + None + } + } + } +} + +impl voter::Environment for Environment where + Block: 'static, + B: Backend + 'static, + E: CallExecutor + 'static, + N: Network + 'static, + N::In: 'static, + NumberFor: As, +{ + type Timer = Box>; + type Id = AuthorityId; + type Signature = ed25519::Signature; + type In = Box, Error = Self::Error>>; + type Out = Box, SinkError = Self::Error>>; + type Error = Error; + + fn round_data( + &self, + round: u64 + ) -> voter::RoundData { + use client::BlockchainEvents; + use tokio::timer::Delay; + + let now = Instant::now(); + let prevote_timer = Delay::new(now + self.config.gossip_duration * 2); + let precommit_timer = Delay::new(now + self.config.gossip_duration * 4); + + // TODO: dispatch this with `mpsc::spawn`. + let incoming = checked_message_stream::( + self.network.messages_for(round), + self.config.voters.clone(), + ); + + let (out_rx, outgoing) = outgoing_messages::( + self.config.local_key.clone(), + self.config.voters.clone(), + round, + self.network.clone(), + ); + + // schedule incoming messages from the network to be held until + // corresponding blocks are imported. + let incoming = UntilImported::new( + self.inner.import_notification_stream(), + self.inner.clone(), + incoming, + ); + + // join incoming network messages with locally originating ones. + let incoming = Box::new(incoming.select(out_rx)); + + // schedule network message cleanup when sink drops. + let outgoing = Box::new(ClearOnDrop { + round, + network: self.network.clone(), + inner: outgoing, + }); + + voter::RoundData { + prevote_timer: Box::new(prevote_timer.map_err(Error::Timer)), + precommit_timer: Box::new(precommit_timer.map_err(Error::Timer)), + voters: self.voters.clone(), + incoming, + outgoing, + } + } + + fn completed(&self, round: u64, state: RoundState) { + let encoded_state = (round, state).encode(); + if let Err(e) = self.inner.backend() + .insert_aux(&[(LAST_COMPLETED_KEY, &encoded_state[..])], &[]) + { + warn!(target: "afg", "Error bookkeeping last completed round in DB: {:?}", e); + } + } + + fn finalize_block(&self, hash: Block::Hash, number: u32) { + // TODO: don't unconditionally notify. + if let Err(e) = self.inner.finalize_block(BlockId::Hash(hash), true) { + warn!(target: "afg", "Error applying finality to block {:?}: {:?}", (hash, number), e); + } + } + + fn prevote_equivocation( + &self, + _round: u64, + equivocation: ::grandpa::Equivocation, Self::Signature> + ) { + warn!(target: "afg", "Detected prevote equivocation in the finality worker: {:?}", equivocation); + // nothing yet; this could craft misbehavior reports of some kind. + } + + fn precommit_equivocation( + &self, + _round: u64, + equivocation: Equivocation, Self::Signature> + ) { + warn!(target: "afg", "Detected precommit equivocation in the finality worker: {:?}", equivocation); + // nothing yet + } +} + +/// Run a GRANDPA voter as a task. The returned future should be executed in a tokio runtime. +pub fn run_voter( + config: Config, + client: Arc>, + voters: HashMap, + network: N, +) -> Result,client::error::Error> where + Block::Hash: Ord, + B: Backend + 'static, + E: CallExecutor + 'static, + N: Network + 'static, + N::In: 'static, + NumberFor: As, +{ + let chain_info = client.info()?; + let genesis_hash = chain_info.chain.genesis_hash; + let last_finalized = ( + chain_info.chain.finalized_hash, + chain_info.chain.finalized_number.as_() + ); + + let (last_round_number, last_state) = match client.backend().get_aux(LAST_COMPLETED_KEY)? { + None => (0, RoundState::genesis((genesis_hash, 0))), + Some(raw) => <(u64, RoundState)>::decode(&mut &raw[..]) + .ok_or_else(|| ::client::error::ErrorKind::Backend( + format!("Last GRANDPA round state kept in invalid format") + ))? + }; + + let environment = Arc::new(Environment { + inner: client, + config, + voters, + network, + }); + + let voter = voter::Voter::new( + environment, + last_round_number, + last_state, + last_finalized, + ); + + Ok(voter.map_err(|e| warn!("GRANDPA Voter failed: {:?}", e))) +} + +#[cfg(test)] +mod tests { + use super::*; + use network::test::*; + use parking_lot::Mutex; + use tokio::runtime::current_thread; + use keyring::Keyring; + use client::BlockchainEvents; + + #[derive(Clone)] + struct TestGrandpaNetwork { + inner: Arc>, + peer_id: usize, + } + + impl TestGrandpaNetwork { + fn new(inner: Arc>, peer_id: usize,) -> Self { + TestGrandpaNetwork { + inner, + peer_id, + } + } + } + + fn round_to_topic(round: u64) -> Hash { + let mut hash = Hash::default(); + round.using_encoded(|s| { + let raw = hash.as_mut(); + raw[..8].copy_from_slice(s); + }); + hash + } + + impl Network for TestGrandpaNetwork { + type In = Box,Error=()>>; + + fn messages_for(&self, round: u64) -> Self::In { + use network::consensus_gossip::ConsensusMessage; + + let messages = self.inner.lock().peer(self.peer_id) + .with_spec(|spec, _| spec.gossip.messages_for(round_to_topic(round))); + + let messages = messages + .map_err( + move |_| panic!("Messages for round {} dropped too early", round) + ) + .map(|msg| match msg { + ConsensusMessage::ChainSpecific(raw, _) => { + let message = GossipMessage::decode(&mut &raw[..]).unwrap(); + message.data + } + _ => panic!("Only chain-specific messages come under this stream"), + }); + + Box::new(messages) + } + + fn send_message(&self, round: u64, message: Vec) { + let mut inner = self.inner.lock(); + inner.peer(self.peer_id).gossip_message(round_to_topic(round), message); + inner.route(); + } + + fn drop_messages(&self, round: u64) { + let topic = round_to_topic(round); + self.inner.lock().peer(self.peer_id) + .with_spec(|spec, _| spec.gossip.collect_garbage(|t| t == &topic)); + } + } + + const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500); + const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); + + #[test] + fn finalize_20_unanimous_3_peers() { + let mut net = TestNet::new(3); + net.peer(0).push_blocks(20, false); + net.sync(); + + let net = Arc::new(Mutex::new(net)); + let peers = &[ + (0, Keyring::Alice), + (1, Keyring::Bob), + (2, Keyring::Charlie), + ]; + + let voters: Vec<_> = peers.iter() + .map(|&(_, ref key)| AuthorityId(key.to_raw_public())) + .collect(); + + let mut finality_notifications = Vec::new(); + + let mut runtime = current_thread::Runtime::new().unwrap(); + for (peer_id, key) in peers { + let client = net.lock().peer(*peer_id).client().clone(); + finality_notifications.push( + client.finality_notification_stream() + .take_while(|n| Ok(n.header.number() < &20)) + .for_each(move |_| Ok(())) + ); + let voter = run_voter( + Config { + gossip_duration: TEST_GOSSIP_DURATION, + voters: voters.clone(), + local_key: Some(Arc::new(key.clone().into())), + }, + client, + voters.iter().map(|&id| (id, 1)).collect(), + TestGrandpaNetwork::new(net.clone(), *peer_id), + ).expect("all in order with client and network"); + + runtime.spawn(voter); + } + + // wait for all finalized on each. + let wait_for = ::futures::future::join_all(finality_notifications) + .map(|_| ()) + .map_err(|_| ()); + + let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) + .for_each(move |_| { net.lock().route_until_complete(); Ok(()) }) + .map(|_| ()) + .map_err(|_| ()); + + runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); + } + + #[test] + fn observer_can_finalize() { + let mut net = TestNet::new(4); + net.peer(0).push_blocks(20, false); + net.sync(); + + let net = Arc::new(Mutex::new(net)); + let peers = &[ + (0, Keyring::Alice), + (1, Keyring::Bob), + (2, Keyring::Charlie), + ]; + + let voters: HashMap<_, _> = peers.iter() + .map(|&(_, ref key)| (AuthorityId(key.to_raw_public()), 1)) + .collect(); + + let mut finality_notifications = Vec::new(); + + let mut runtime = current_thread::Runtime::new().unwrap(); + let all_peers = peers.iter() + .cloned() + .map(|(id, key)| (id, Some(Arc::new(key.into())))) + .chain(::std::iter::once((3, None))); + + for (peer_id, local_key) in all_peers { + let client = net.lock().peer(peer_id).client().clone(); + finality_notifications.push( + client.finality_notification_stream() + .take_while(|n| Ok(n.header.number() < &20)) + .for_each(move |_| Ok(())) + ); + let voter = run_voter( + Config { + gossip_duration: TEST_GOSSIP_DURATION, + voters: voters.keys().cloned().collect(), + local_key, + }, + client, + voters.clone(), + TestGrandpaNetwork::new(net.clone(), peer_id), + ).expect("all in order with client and network"); + + runtime.spawn(voter); + } + + // wait for all finalized on each. + let wait_for = ::futures::future::join_all(finality_notifications) + .map(|_| ()) + .map_err(|_| ()); + + let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) + .for_each(move |_| { net.lock().route_until_complete(); Ok(()) }) + .map(|_| ()) + .map_err(|_| ()); + + runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); + } +} diff --git a/substrate/core/network/Cargo.toml b/substrate/core/network/Cargo.toml index ecb79fccc1..d124359765 100644 --- a/substrate/core/network/Cargo.toml +++ b/substrate/core/network/Cargo.toml @@ -23,7 +23,15 @@ parity-codec = "2.0" parity-codec-derive = "2.0" substrate-network-libp2p = { path = "../../core/network-libp2p" } +env_logger = { version = "0.4", optional = true } +substrate-keyring = { path = "../../core/keyring", optional = true } +substrate-test-client = { path = "../../core/test-client", optional = true } + [dev-dependencies] -env_logger = "0.4" +env_logger = { version = "0.4" } substrate-keyring = { path = "../../core/keyring" } substrate-test-client = { path = "../../core/test-client" } + +[features] +default = [] +test-helpers = ["env_logger", "substrate-keyring", "substrate-test-client"] diff --git a/substrate/core/network/src/consensus_gossip.rs b/substrate/core/network/src/consensus_gossip.rs index 7e685f8542..2878da8c68 100644 --- a/substrate/core/network/src/consensus_gossip.rs +++ b/substrate/core/network/src/consensus_gossip.rs @@ -54,7 +54,7 @@ struct MessageEntry { /// Consensus network protocol handler. Manages statements and candidate requests. pub struct ConsensusGossip { peers: HashMap>, - message_sink: Option<(mpsc::UnboundedSender>, B::Hash)>, + live_message_sinks: HashMap>>, messages: Vec>, message_hashes: HashSet, } @@ -64,7 +64,7 @@ impl ConsensusGossip where B::Header: HeaderT { pub fn new() -> Self { ConsensusGossip { peers: HashMap::new(), - message_sink: None, + live_message_sinks: HashMap::new(), messages: Default::default(), message_hashes: Default::default(), } @@ -72,7 +72,7 @@ impl ConsensusGossip where B::Header: HeaderT { /// Closes all notification streams. pub fn abort(&mut self) { - self.message_sink = None; + self.live_message_sinks.clear(); } /// Handle new connected peer. @@ -150,23 +150,23 @@ impl ConsensusGossip where B::Header: HeaderT { } /// Handles incoming chain-specific message and repropagates - pub fn on_chain_specific(&mut self, protocol: &mut Context, who: NodeIndex, message: Vec, parent_hash: B::Hash) { + pub fn on_chain_specific(&mut self, protocol: &mut Context, who: NodeIndex, message: Vec, topic: B::Hash) { debug!(target: "gossip", "received chain-specific gossip message"); - if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::ChainSpecific(message, parent_hash)) { + if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::ChainSpecific(message, topic)) { debug!(target: "gossip", "handled incoming chain-specific message"); // propagate to other peers. self.multicast(protocol, message, Some(hash)); } } - /// Get a stream of messages relevant to consensus on top of a given parent hash. - pub fn messages_for(&mut self, parent_hash: B::Hash) -> mpsc::UnboundedReceiver> { + /// Get a stream of messages relevant to consensus for the given topic. + pub fn messages_for(&mut self, topic: B::Hash) -> mpsc::UnboundedReceiver> { let (sink, stream) = mpsc::unbounded(); for entry in self.messages.iter() { let message_matches = match entry.message { - ConsensusMessage::Bft(ref msg) => msg.parent_hash == parent_hash, - ConsensusMessage::ChainSpecific(_, ref h) => h == &parent_hash, + ConsensusMessage::Bft(ref msg) => msg.parent_hash == topic, + ConsensusMessage::ChainSpecific(_, ref h) => h == &topic, }; if message_matches { @@ -174,14 +174,14 @@ impl ConsensusGossip where B::Header: HeaderT { } } - self.message_sink = Some((sink, parent_hash)); + self.live_message_sinks.insert(topic, sink); stream } /// Multicast a chain-specific message to other authorities. - pub fn multicast_chain_specific(&mut self, protocol: &mut Context, message: Vec, parent_hash: B::Hash) { + pub fn multicast_chain_specific(&mut self, protocol: &mut Context, message: Vec, topic: B::Hash) { trace!(target:"gossip", "sending chain-specific message"); - self.multicast(protocol, ConsensusMessage::ChainSpecific(message, parent_hash), None); + self.multicast(protocol, ConsensusMessage::ChainSpecific(message, topic), None); } /// Multicast a BFT message to other authorities @@ -196,20 +196,21 @@ impl ConsensusGossip where B::Header: HeaderT { self.peers.remove(&who); } - /// Prune old or no longer relevant consensus messages. - /// Supply an optional block hash where consensus is known to have concluded. - pub fn collect_garbage(&mut self, best_hash: Option<&B::Hash>) { + /// Prune old or no longer relevant consensus messages. Provide a predicate + /// for pruning, which returns `false` when the items with a given topic should be pruned. + pub fn collect_garbage bool>(&mut self, predicate: P) { + self.live_message_sinks.retain(|_, sink| !sink.is_closed()); + let hashes = &mut self.message_hashes; let before = self.messages.len(); let now = Instant::now(); self.messages.retain(|entry| { - if entry.instant + MESSAGE_LIFETIME >= now && - best_hash.map_or(true, |parent_hash| - match entry.message { - ConsensusMessage::Bft(ref msg) => &msg.parent_hash != parent_hash, - ConsensusMessage::ChainSpecific(_, ref h) => h != parent_hash, - }) - { + let topic = match entry.message { + ConsensusMessage::Bft(ref msg) => &msg.parent_hash, + ConsensusMessage::ChainSpecific(_, ref h) => h, + }; + + if entry.instant + MESSAGE_LIFETIME >= now && predicate(topic) { true } else { hashes.remove(&entry.hash); @@ -223,7 +224,7 @@ impl ConsensusGossip where B::Header: HeaderT { } fn handle_incoming(&mut self, protocol: &mut Context, who: NodeIndex, message: ConsensusMessage) -> Option<(B::Hash, ConsensusMessage)> { - let (hash, parent, message) = match message { + let (hash, topic, message) = match message { ConsensusMessage::Bft(msg) => { let parent = msg.parent_hash; let generic = GenericMessage::BftMessage(msg); @@ -236,13 +237,13 @@ impl ConsensusGossip where B::Header: HeaderT { } ) } - ConsensusMessage::ChainSpecific(msg, parent) => { + ConsensusMessage::ChainSpecific(msg, topic) => { let generic = GenericMessage::ChainSpecific(msg); ( ::protocol::hash_message::(&generic), - parent, + topic, match generic { - GenericMessage::ChainSpecific(msg) => ConsensusMessage::ChainSpecific(msg, parent), + GenericMessage::ChainSpecific(msg) => ConsensusMessage::ChainSpecific(msg, topic), _ => panic!("`generic` is known to be the `ChainSpecific` variant; qed"), } ) @@ -254,14 +255,14 @@ impl ConsensusGossip where B::Header: HeaderT { return None; } - match (protocol.client().info(), protocol.client().header(&BlockId::Hash(parent))) { + match (protocol.client().info(), protocol.client().header(&BlockId::Hash(topic))) { (_, Err(e)) | (Err(e), _) => { debug!(target:"gossip", "Error reading blockchain: {:?}", e); return None; }, (Ok(info), Ok(Some(header))) => { if header.number() < &info.chain.best_number { - trace!(target:"gossip", "Ignored ancient message from {}, hash={}", who, parent); + trace!(target:"gossip", "Ignored ancient message from {}, hash={}", who, topic); return None; } }, @@ -269,16 +270,17 @@ impl ConsensusGossip where B::Header: HeaderT { } if let Some(ref mut peer) = self.peers.get_mut(&who) { + use std::collections::hash_map::Entry; peer.known_messages.insert(hash); - if let Some((sink, parent_hash)) = self.message_sink.take() { - if parent == parent_hash { - debug!(target: "gossip", "Pushing relevant consensus message to sink."); - if let Err(e) = sink.unbounded_send(message.clone()) { - trace!(target:"gossip", "Error broadcasting message notification: {:?}", e); - } + if let Entry::Occupied(mut entry) = self.live_message_sinks.entry(topic) { + debug!(target: "gossip", "Pushing relevant consensus message to sink."); + if let Err(e) = entry.get().unbounded_send(message.clone()) { + trace!(target:"gossip", "Error broadcasting message notification: {:?}", e); } - self.message_sink = Some((sink, parent_hash)); + if entry.get().is_closed() { + entry.remove_entry(); + } } } else { trace!(target:"gossip", "Ignored statement from unregistered peer {}", who); @@ -344,7 +346,7 @@ mod tests { consensus.message_hashes.insert(m2_hash); // nothing to collect - consensus.collect_garbage(None); + consensus.collect_garbage(|_topic| true); assert_eq!(consensus.messages.len(), 2); assert_eq!(consensus.message_hashes.len(), 2); @@ -357,13 +359,13 @@ mod tests { digest: Default::default(), }; - consensus.collect_garbage(Some(&H256::default())); + consensus.collect_garbage(|&topic| topic != Default::default()); assert_eq!(consensus.messages.len(), 2); assert_eq!(consensus.message_hashes.len(), 2); // header that matches one of the messages header.parent_hash = prev_hash; - consensus.collect_garbage(Some(&prev_hash)); + consensus.collect_garbage(|topic| topic != &prev_hash); assert_eq!(consensus.messages.len(), 1); assert_eq!(consensus.message_hashes.len(), 1); assert!(consensus.message_hashes.contains(&m2_hash)); @@ -371,7 +373,7 @@ mod tests { // make timestamp expired consensus.messages.clear(); push_msg!(m2_hash, now - MESSAGE_LIFETIME, m2); - consensus.collect_garbage(None); + consensus.collect_garbage(|_topic| true); assert!(consensus.messages.is_empty()); assert!(consensus.message_hashes.is_empty()); } diff --git a/substrate/core/network/src/import_queue.rs b/substrate/core/network/src/import_queue.rs index 1e1149dc6f..7b72ced33d 100644 --- a/substrate/core/network/src/import_queue.rs +++ b/substrate/core/network/src/import_queue.rs @@ -232,7 +232,7 @@ enum SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext> { /// Indirect link (through service). Indirect(&'a RwLock>, &'a Client, &'a E), /// Direct references are given. - #[cfg(test)] + #[cfg(any(test, feature = "test-helpers"))] Direct(&'a mut ChainSync, &'a mut Context), } @@ -402,7 +402,7 @@ impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext> SyncLink<'a, B, E> { /// Execute closure with locked ChainSync. fn with_sync, &mut Context)>(&mut self, closure: F) { match *self { - #[cfg(test)] + #[cfg(any(test, feature = "test-helpers"))] SyncLink::Direct(ref mut sync, ref mut protocol) => closure(*sync, *protocol), SyncLink::Indirect(ref sync, _, ref service) => @@ -417,7 +417,7 @@ impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext> SyncLink<'a, B, E> { impl<'a, B: 'static + BlockT, E: ExecuteInContext> SyncLinkApi for SyncLink<'a, B, E> { fn chain(&self) -> &Client { match *self { - #[cfg(test)] + #[cfg(any(test, feature = "test-helpers"))] SyncLink::Direct(_, ref protocol) => protocol.client(), SyncLink::Indirect(_, ref chain, _) => *chain, } @@ -447,6 +447,40 @@ impl<'a, B: 'static + BlockT, E: ExecuteInContext> SyncLinkApi for SyncLin } } + +/// Blocks import queue that is importing blocks in the same thread. +/// The boolean value indicates whether blocks should be imported without instant finality. +#[cfg(any(test, feature = "test-helpers"))] +pub struct SyncImportQueue(pub bool); + +#[cfg(any(test, feature = "test-helpers"))] +impl ImportQueue for SyncImportQueue { + fn clear(&self) { } + + fn stop(&self) { } + + fn status(&self) -> ImportQueueStatus { + ImportQueueStatus { + importing_count: 0, + best_importing_number: Zero::zero(), + } + } + + fn is_importing(&self, _hash: &B::Hash) -> bool { + false + } + + fn import_blocks(&self, sync: &mut ChainSync, protocol: &mut Context, blocks: (BlockOrigin, Vec>)) { + struct DummyExecuteInContext; + + impl ExecuteInContext for DummyExecuteInContext { + fn execute_in_context)>(&self, _closure: F) { } + } + + import_many_blocks(&mut SyncLink::Direct::<_, DummyExecuteInContext>(sync, protocol), None, blocks, self.0); + } +} + #[cfg(test)] pub mod tests { use client; @@ -457,35 +491,6 @@ pub mod tests { use runtime_primitives::generic::BlockId; use super::*; - /// Blocks import queue that is importing blocks in the same thread. - /// The boolean value indicates whether blocks should be imported without instant finality. - pub struct SyncImportQueue(pub bool); - struct DummyExecuteInContext; - - impl ExecuteInContext for DummyExecuteInContext { - fn execute_in_context)>(&self, _closure: F) { } - } - - impl ImportQueue for SyncImportQueue { - fn clear(&self) { } - - fn stop(&self) { } - - fn status(&self) -> ImportQueueStatus { - ImportQueueStatus { - importing_count: 0, - best_importing_number: Zero::zero(), - } - } - - fn is_importing(&self, _hash: &B::Hash) -> bool { - false - } - - fn import_blocks(&self, sync: &mut ChainSync, protocol: &mut Context, blocks: (BlockOrigin, Vec>)) { - import_many_blocks(&mut SyncLink::Direct::<_, DummyExecuteInContext>(sync, protocol), None, blocks, self.0); - } - } struct TestLink { chain: Arc>, diff --git a/substrate/core/network/src/lib.rs b/substrate/core/network/src/lib.rs index dfaf101482..7da1728f0e 100644 --- a/substrate/core/network/src/lib.rs +++ b/substrate/core/network/src/lib.rs @@ -37,9 +37,14 @@ extern crate rand; #[macro_use] extern crate error_chain; #[macro_use] extern crate parity_codec_derive; -#[cfg(test)] extern crate env_logger; -#[cfg(test)] extern crate substrate_keyring as keyring; -#[cfg(test)] extern crate substrate_test_client as test_client; +#[cfg(test)] +extern crate env_logger; + +#[cfg(any(test, feature = "test-helpers"))] +extern crate substrate_keyring as keyring; + +#[cfg(any(test, feature = "test-helpers"))] +extern crate substrate_test_client as test_client; mod service; mod sync; @@ -55,7 +60,8 @@ pub mod error; pub mod message; pub mod specialization; -#[cfg(test)] mod test; +#[cfg(any(test, feature = "test-helpers"))] +pub mod test; pub use chain::Client as ClientHandle; pub use service::{Service, FetchFuture, ConsensusService, BftMessageStream, diff --git a/substrate/core/network/src/test/mod.rs b/substrate/core/network/src/test/mod.rs index eb2d873016..89ea306be6 100644 --- a/substrate/core/network/src/test/mod.rs +++ b/substrate/core/network/src/test/mod.rs @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +#![allow(missing_docs)] + +#[cfg(test)] mod sync; use std::collections::{VecDeque, HashSet, HashMap}; @@ -22,7 +25,6 @@ use std::sync::Arc; use parking_lot::RwLock; use client; use client::block_builder::BlockBuilder; -use runtime_primitives::traits::Block as BlockT; use runtime_primitives::generic::BlockId; use io::SyncIo; use protocol::{Context, Protocol}; @@ -31,27 +33,45 @@ use config::ProtocolConfig; use service::TransactionPool; use network_libp2p::{NodeIndex, SessionInfo, Severity}; use keyring::Keyring; -use codec::Encode; -use import_queue::tests::SyncImportQueue; +use codec::{Encode, Decode}; +use import_queue::SyncImportQueue; use test_client::{self, TestClient}; -use test_client::runtime::{Block, Hash, Transfer, Extrinsic}; use specialization::Specialization; +use consensus_gossip::ConsensusGossip; -pub struct DummySpecialization; +pub use test_client::runtime::{Block, Hash, Transfer, Extrinsic}; + +/// The test specialization. +pub struct DummySpecialization { + /// Consensus gossip handle. + pub gossip: ConsensusGossip, +} + +#[derive(Encode, Decode)] +pub struct GossipMessage { + /// The topic to classify under. + pub topic: Hash, + /// The data to send. + pub data: Vec, +} impl Specialization for DummySpecialization { fn status(&self) -> Vec { vec![] } - fn on_connect(&mut self, _ctx: &mut Context, _peer_id: NodeIndex, _status: ::message::Status) { - + fn on_connect(&mut self, ctx: &mut Context, peer_id: NodeIndex, status: ::message::Status) { + self.gossip.new_peer(ctx, peer_id, status.roles); } - fn on_disconnect(&mut self, _ctx: &mut Context, _peer_id: NodeIndex) { - + fn on_disconnect(&mut self, ctx: &mut Context, peer_id: NodeIndex) { + self.gossip.peer_disconnected(ctx, peer_id); } - fn on_message(&mut self, _ctx: &mut Context, _peer_id: NodeIndex, _message: ::message::Message) { - + fn on_message(&mut self, ctx: &mut Context, peer_id: NodeIndex, message: ::message::Message) { + if let ::message::generic::Message::ChainSpecific(data) = message { + let gossip_message = GossipMessage::decode(&mut &data[..]) + .expect("gossip messages all in known format; qed"); + self.gossip.on_chain_specific(ctx, peer_id, data, gossip_message.topic) + } } } @@ -173,19 +193,30 @@ impl Peer { fn flush(&self) { } - fn generate_blocks(&self, count: usize, mut edit_block: F) - where F: FnMut(&mut BlockBuilder) + /// Push a message into the gossip network and relay to peers. + /// `TestNet::sync_step` needs to be called to ensure it's propagated. + pub fn gossip_message(&self, topic: Hash, data: Vec) { + self.sync.with_spec(&mut TestIo::new(&self.queue, None), |spec, ctx| { + let message = GossipMessage { topic, data }.encode(); + spec.gossip.multicast_chain_specific(ctx, message, topic); + }) + } + + /// Add blocks to the peer -- edit the block before adding + pub fn generate_blocks(&self, count: usize, mut edit_block: F) + where F: FnMut(&mut BlockBuilder) { for _ in 0 .. count { let mut builder = self.client.new_block().unwrap(); edit_block(&mut builder); let block = builder.bake().unwrap(); - trace!("Generating {}, (#{})", block.hash(), block.header.number); + trace!("Generating {}, (#{}, parent={})", block.header.hash(), block.header.number, block.header.parent_hash); self.client.justify_and_import(client::BlockOrigin::File, block).unwrap(); } } - fn push_blocks(&self, count: usize, with_tx: bool) { + /// Push blocks to the peer (simplified: with or without a TX) + pub fn push_blocks(&self, count: usize, with_tx: bool) { let mut nonce = 0; if with_tx { self.generate_blocks(count, |builder| { @@ -203,6 +234,18 @@ impl Peer { self.generate_blocks(count, |_| ()); } } + + /// Execute a function with specialization for this peer. + pub fn with_spec(&self, f: F) -> U + where F: FnOnce(&mut DummySpecialization, &mut Context) -> U + { + self.sync.with_spec(&mut TestIo::new(&self.queue, None), f) + } + + /// Get a reference to the client. + pub fn client(&self) -> &Arc> { + &self.client + } } pub struct EmptyTransactionPool; @@ -226,11 +269,13 @@ pub struct TestNet { } impl TestNet { - fn new(n: usize) -> Self { + /// Create new test network with this many peers. + pub fn new(n: usize) -> Self { Self::new_with_config(n, ProtocolConfig::default()) } - fn new_with_config(n: usize, config: ProtocolConfig) -> Self { + /// Create new test network with peers and given config. + pub fn new_with_config(n: usize, config: ProtocolConfig) -> Self { let mut net = TestNet { peers: Vec::new(), started: false, @@ -243,11 +288,23 @@ impl TestNet { net } + /// Add a peer. pub fn add_peer(&mut self, config: &ProtocolConfig) { let client = Arc::new(test_client::new()); let tx_pool = Arc::new(EmptyTransactionPool); let import_queue = Arc::new(SyncImportQueue(false)); - let sync = Protocol::new(config.clone(), client.clone(), import_queue, None, tx_pool, DummySpecialization).unwrap(); + let specialization = DummySpecialization { + gossip: ConsensusGossip::new(), + }; + let sync = Protocol::new( + config.clone(), + client.clone(), + import_queue, + None, + tx_pool, + specialization + ).unwrap(); + self.peers.push(Arc::new(Peer { sync: sync, client: client, @@ -255,10 +312,12 @@ impl TestNet { })); } + /// Get reference to peer. pub fn peer(&self, i: usize) -> &Peer { &self.peers[i] } + /// Start network. fn start(&mut self) { if self.started { return; @@ -274,7 +333,8 @@ impl TestNet { self.started = true; } - fn sync_step(&mut self) { + /// Do one step of routing. + pub fn route(&mut self) { for peer in 0..self.peers.len() { let packet = self.peers[peer].pending_message(); if let Some(packet) = packet { @@ -294,20 +354,32 @@ impl TestNet { self.peers[*d].on_disconnect(peer as NodeIndex); } } - - self.sync_step_peer(peer); } } - fn sync_step_peer(&mut self, peer_num: usize) { - self.peers[peer_num].sync_step(); + /// Route messages between peers until all queues are empty. + pub fn route_until_complete(&mut self) { + while !self.done() { + self.route() + } } - fn restart_peer(&mut self, i: usize) { + /// Do a step of synchronization. + pub fn sync_step(&mut self) { + self.route(); + + for peer in &mut self.peers { + peer.sync_step(); + } + } + + /// Restart sync for a peer. + pub fn restart_peer(&mut self, i: usize) { self.peers[i].restart_sync(); } - fn sync(&mut self) -> u32 { + /// Perform synchronization until complete. + pub fn sync(&mut self) -> u32 { self.start(); let mut total_steps = 0; while !self.done() { @@ -317,14 +389,16 @@ impl TestNet { total_steps } - fn sync_steps(&mut self, count: usize) { + /// Do the given amount of sync steps. + pub fn sync_steps(&mut self, count: usize) { self.start(); for _ in 0..count { self.sync_step(); } } - fn done(&self) -> bool { + /// Whether all peers have synced. + pub fn done(&self) -> bool { self.peers.iter().all(|p| p.is_done()) } } diff --git a/substrate/node/network/src/lib.rs b/substrate/node/network/src/lib.rs index 7e07054fcb..b205da0652 100644 --- a/substrate/node/network/src/lib.rs +++ b/substrate/node/network/src/lib.rs @@ -68,7 +68,8 @@ impl Protocol { fn new_consensus(&mut self, parent_hash: Hash) { let old_consensus = self.live_consensus.take(); self.live_consensus = Some(parent_hash); - self.consensus_gossip.collect_garbage(old_consensus.as_ref()); + self.consensus_gossip + .collect_garbage(|topic| old_consensus.as_ref().map_or(true, |h| topic != h)); } } @@ -105,7 +106,7 @@ impl Specialization for Protocol { } fn maintain_peers(&mut self, _ctx: &mut Context) { - self.consensus_gossip.collect_garbage(None); + self.consensus_gossip.collect_garbage(|_| true); } fn on_block_imported(&mut self, _ctx: &mut Context, _hash: Hash, _header: &Header) {