Initial GRANDPA integration (#866)

* implement grandpa client

* consensus gossip with arbitrary topics

* defer GRANDPA messages until referenced blocks imported

* set up communication for voter in a transparent way

* instantiate GRANDPA voter

* keep last round state on disk

* switch back to crates.io finality-grandpa

* update cargo.lock

* use new `collect_garbage` API

* update sync test framework and make public

* test that observers can observe

* fix warning

* use more idiomatic predicate for collecting garbage in gossip

* kill spaces

* fix date
This commit is contained in:
Robert Habermeier
2018-10-05 10:30:39 -04:00
committed by Gav Wood
parent 1cc0e3b6ea
commit 68b4be14ff
10 changed files with 1082 additions and 166 deletions
+91 -61
View File
@@ -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"
+1
View File
@@ -24,6 +24,7 @@ members = [
"core/client",
"core/client/db",
"core/executor",
"core/finality-grandpa",
"core/keyring",
"core/misbehavior-check",
"core/network",
@@ -0,0 +1,22 @@
[package]
name = "substrate-finality-grandpa"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[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" }
+767
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Block> = grandpa::Message<<Block as BlockT>::Hash>;
/// A signed message.
pub type SignedMessage<Block> = grandpa::SignedMessage<<Block as BlockT>::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<AuthorityId>,
/// The local signing key.
pub local_key: Option<Arc<ed25519::Pair>>,
}
/// 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<GrandpaError> 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<Item=Vec<u8>,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<u8>);
/// Clean up messages for a round.
fn drop_messages(&self, round: u64);
}
/// Something which can determine if a block is known.
pub trait BlockStatus<Block: BlockT> {
/// 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<Option<u32>, Error>;
}
impl<B, E, Block: BlockT> BlockStatus<Block> for Arc<Client<B, E, Block>> where
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
NumberFor<Block>: As<u32>,
{
fn block_number(&self, hash: Block::Hash) -> Result<Option<u32>, 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<Block: BlockT, Status, I> {
import_notifications: Fuse<ImportNotifications<Block>>,
status_check: Status,
inner: Fuse<I>,
ready: VecDeque<SignedMessage<Block>>,
check_pending: Interval,
pending: HashMap<Block::Hash, Vec<SignedMessage<Block>>>,
}
impl<Block: BlockT, Status: BlockStatus<Block>, I: Stream> UntilImported<Block, Status, I> {
fn new(
import_notifications: ImportNotifications<Block>,
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<Block: BlockT, Status: BlockStatus<Block>, I> Stream for UntilImported<Block, Status, I>
where I: Stream<Item=SignedMessage<Block>,Error=Error>
{
type Item = SignedMessage<Block>;
type Error = Error;
fn poll(&mut self) -> Poll<Option<SignedMessage<Block>>, 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(&notification.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<I, N: Network> {
round: u64,
inner: I,
network: N,
}
impl<I: Sink, N: Network> Sink for ClearOnDrop<I, N> {
type SinkItem = I::SinkItem;
type SinkError = I::SinkError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
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<I, N: Network> Drop for ClearOnDrop<I, N> {
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<Block: BlockT, S>(inner: S, voters: Vec<AuthorityId>)
-> impl Stream<Item=SignedMessage<Block>,Error=Error> where
S: Stream<Item=Vec<u8>,Error=()>
{
inner
.filter_map(|raw| {
let decoded = SignedMessage::<Block>::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<Block: BlockT, N: Network>(
local_key: Option<Arc<ed25519::Pair>>,
voters: Vec<AuthorityId>,
round: u64,
network: N,
) -> (
impl Stream<Item=SignedMessage<Block>,Error=Error>,
impl Sink<SinkItem=Message<Block>,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<Block>| {
// 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::<Block> {
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<B, E, Block: BlockT, N: Network> {
inner: Arc<Client<B, E, Block>>,
voters: HashMap<AuthorityId, usize>,
config: Config,
network: N,
}
impl<Block: BlockT, B, E, N> grandpa::Chain<Block::Hash> for Environment<B, E, Block, N> where
Block: 'static,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
N: Network + 'static,
N::In: 'static,
NumberFor<Block>: As<u32>,
{
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, 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<B, E, Block: BlockT, N> voter::Environment<Block::Hash> for Environment<B, E, Block, N> where
Block: 'static,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
N: Network + 'static,
N::In: 'static,
NumberFor<Block>: As<u32>,
{
type Timer = Box<Future<Item = (), Error = Self::Error>>;
type Id = AuthorityId;
type Signature = ed25519::Signature;
type In = Box<Stream<Item = ::grandpa::SignedMessage<Block::Hash, Self::Signature, Self::Id>, Error = Self::Error>>;
type Out = Box<Sink<SinkItem = ::grandpa::Message<Block::Hash>, SinkError = Self::Error>>;
type Error = Error;
fn round_data(
&self,
round: u64
) -> voter::RoundData<Self::Timer, Self::Id, Self::In, Self::Out> {
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::<Block, _>(
self.network.messages_for(round),
self.config.voters.clone(),
);
let (out_rx, outgoing) = outgoing_messages::<Block, _>(
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<Block::Hash>) {
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::Id, Prevote<Block::Hash>, 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::Id, Precommit<Block::Hash>, 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<B, E, Block: BlockT, N>(
config: Config,
client: Arc<Client<B, E, Block>>,
voters: HashMap<AuthorityId, usize>,
network: N,
) -> Result<impl Future<Item=(),Error=()>,client::error::Error> where
Block::Hash: Ord,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static,
N: Network + 'static,
N::In: 'static,
NumberFor<Block>: As<u32>,
{
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<Block::Hash>)>::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<Mutex<TestNet>>,
peer_id: usize,
}
impl TestGrandpaNetwork {
fn new(inner: Arc<Mutex<TestNet>>, 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<Stream<Item=Vec<u8>,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<u8>) {
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();
}
}
+9 -1
View File
@@ -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"]
+41 -39
View File
@@ -54,7 +54,7 @@ struct MessageEntry<B: BlockT> {
/// Consensus network protocol handler. Manages statements and candidate requests.
pub struct ConsensusGossip<B: BlockT> {
peers: HashMap<NodeIndex, PeerConsensus<B::Hash>>,
message_sink: Option<(mpsc::UnboundedSender<ConsensusMessage<B>>, B::Hash)>,
live_message_sinks: HashMap<B::Hash, mpsc::UnboundedSender<ConsensusMessage<B>>>,
messages: Vec<MessageEntry<B>>,
message_hashes: HashSet<B::Hash>,
}
@@ -64,7 +64,7 @@ impl<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
/// 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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
}
/// Handles incoming chain-specific message and repropagates
pub fn on_chain_specific(&mut self, protocol: &mut Context<B>, who: NodeIndex, message: Vec<u8>, parent_hash: B::Hash) {
pub fn on_chain_specific(&mut self, protocol: &mut Context<B>, who: NodeIndex, message: Vec<u8>, 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<ConsensusMessage<B>> {
/// Get a stream of messages relevant to consensus for the given topic.
pub fn messages_for(&mut self, topic: B::Hash) -> mpsc::UnboundedReceiver<ConsensusMessage<B>> {
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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
}
}
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<B>, message: Vec<u8>, parent_hash: B::Hash) {
pub fn multicast_chain_specific(&mut self, protocol: &mut Context<B>, message: Vec<u8>, 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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
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<P: Fn(&B::Hash) -> 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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
}
fn handle_incoming(&mut self, protocol: &mut Context<B>, who: NodeIndex, message: ConsensusMessage<B>) -> Option<(B::Hash, ConsensusMessage<B>)> {
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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
}
)
}
ConsensusMessage::ChainSpecific(msg, parent) => {
ConsensusMessage::ChainSpecific(msg, topic) => {
let generic = GenericMessage::ChainSpecific(msg);
(
::protocol::hash_message::<B>(&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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
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<B: BlockT> ConsensusGossip<B> where B::Header: HeaderT<Number=u64> {
}
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());
}
+37 -32
View File
@@ -232,7 +232,7 @@ enum SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext<B>> {
/// Indirect link (through service).
Indirect(&'a RwLock<ChainSync<B>>, &'a Client<B>, &'a E),
/// Direct references are given.
#[cfg(test)]
#[cfg(any(test, feature = "test-helpers"))]
Direct(&'a mut ChainSync<B>, &'a mut Context<B>),
}
@@ -402,7 +402,7 @@ impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext<B>> SyncLink<'a, B, E> {
/// Execute closure with locked ChainSync.
fn with_sync<F: Fn(&mut ChainSync<B>, &mut Context<B>)>(&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<B>> SyncLink<'a, B, E> {
impl<'a, B: 'static + BlockT, E: ExecuteInContext<B>> SyncLinkApi<B> for SyncLink<'a, B, E> {
fn chain(&self) -> &Client<B> {
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<B>> SyncLinkApi<B> 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<B: 'static + BlockT> ImportQueue<B> for SyncImportQueue {
fn clear(&self) { }
fn stop(&self) { }
fn status(&self) -> ImportQueueStatus<B> {
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<B>, protocol: &mut Context<B>, blocks: (BlockOrigin, Vec<BlockData<B>>)) {
struct DummyExecuteInContext;
impl<B: 'static + BlockT> ExecuteInContext<B> for DummyExecuteInContext {
fn execute_in_context<F: Fn(&mut Context<B>)>(&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<B: 'static + BlockT> ExecuteInContext<B> for DummyExecuteInContext {
fn execute_in_context<F: Fn(&mut Context<B>)>(&self, _closure: F) { }
}
impl<B: 'static + BlockT> ImportQueue<B> for SyncImportQueue {
fn clear(&self) { }
fn stop(&self) { }
fn status(&self) -> ImportQueueStatus<B> {
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<B>, protocol: &mut Context<B>, blocks: (BlockOrigin, Vec<BlockData<B>>)) {
import_many_blocks(&mut SyncLink::Direct::<_, DummyExecuteInContext>(sync, protocol), None, blocks, self.0);
}
}
struct TestLink {
chain: Arc<Client<Block>>,
+10 -4
View File
@@ -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,
+101 -27
View File
@@ -14,6 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#![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<Block>,
}
#[derive(Encode, Decode)]
pub struct GossipMessage {
/// The topic to classify under.
pub topic: Hash,
/// The data to send.
pub data: Vec<u8>,
}
impl Specialization<Block> for DummySpecialization {
fn status(&self) -> Vec<u8> { vec![] }
fn on_connect(&mut self, _ctx: &mut Context<Block>, _peer_id: NodeIndex, _status: ::message::Status<Block>) {
fn on_connect(&mut self, ctx: &mut Context<Block>, peer_id: NodeIndex, status: ::message::Status<Block>) {
self.gossip.new_peer(ctx, peer_id, status.roles);
}
fn on_disconnect(&mut self, _ctx: &mut Context<Block>, _peer_id: NodeIndex) {
fn on_disconnect(&mut self, ctx: &mut Context<Block>, peer_id: NodeIndex) {
self.gossip.peer_disconnected(ctx, peer_id);
}
fn on_message(&mut self, _ctx: &mut Context<Block>, _peer_id: NodeIndex, _message: ::message::Message<Block>) {
fn on_message(&mut self, ctx: &mut Context<Block>, peer_id: NodeIndex, message: ::message::Message<Block>) {
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<F>(&self, count: usize, mut edit_block: F)
where F: FnMut(&mut BlockBuilder<test_client::Backend, test_client::Executor, Block, Blake2Hasher>)
/// 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<u8>) {
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<F>(&self, count: usize, mut edit_block: F)
where F: FnMut(&mut BlockBuilder<test_client::Backend, test_client::Executor, Block, Blake2Hasher>)
{
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<F, U>(&self, f: F) -> U
where F: FnOnce(&mut DummySpecialization, &mut Context<Block>) -> U
{
self.sync.with_spec(&mut TestIo::new(&self.queue, None), f)
}
/// Get a reference to the client.
pub fn client(&self) -> &Arc<client::Client<test_client::Backend, test_client::Executor, Block>> {
&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())
}
}
+3 -2
View File
@@ -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<Block> for Protocol {
}
fn maintain_peers(&mut self, _ctx: &mut Context<Block>) {
self.consensus_gossip.collect_garbage(None);
self.consensus_gossip.collect_garbage(|_| true);
}
fn on_block_imported(&mut self, _ctx: &mut Context<Block>, _hash: Hash, _header: &Header) {